Skip to content

Package: CloneBuilderImpl$Builders

CloneBuilderImpl$Builders

nameinstructionbranchcomplexitylinemethod
CloneBuilderImpl.Builders(CloneBuilderImpl)
M: 0 C: 22
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
getBuilder(Object)
M: 0 C: 47
100%
M: 0 C: 10
100%
M: 0 C: 6
100%
M: 0 C: 12
100%
M: 0 C: 1
100%

Coverage

1: /**
2: * Copyright (C) 2011 Czech Technical University in Prague
3: * <p>
4: * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
5: * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
6: * version.
7: * <p>
8: * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
9: * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10: * details. You should have received a copy of the GNU General Public License along with this program. If not, see
11: * <http://www.gnu.org/licenses/>.
12: */
13: package cz.cvut.kbss.jopa.sessions;
14:
15: import cz.cvut.kbss.jopa.adapters.IndirectCollection;
16: import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
17: import cz.cvut.kbss.jopa.model.annotations.Inferred;
18: import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
19: import cz.cvut.kbss.jopa.model.metamodel.EntityType;
20: import cz.cvut.kbss.jopa.model.metamodel.FieldSpecification;
21: import cz.cvut.kbss.jopa.model.metamodel.Identifier;
22: import cz.cvut.kbss.jopa.model.metamodel.Metamodel;
23: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
24:
25: import java.lang.reflect.Field;
26: import java.net.URI;
27: import java.util.*;
28: import java.util.logging.Level;
29: import java.util.logging.Logger;
30:
31: public class CloneBuilderImpl implements CloneBuilder {
32:
33: private static final Logger LOG = Logger.getLogger(CloneBuilderImpl.class.getName());
34:
35: private static final Set<Class<?>> WRAPPER_TYPES = getWrapperTypes();
36:
37: // Contains entities that are already cloned, so that we don't clone them again
38: private final RepositoryMap visitedEntities;
39:
40: private final Builders builders;
41:
42: private final UnitOfWorkImpl uow;
43:
44: public CloneBuilderImpl(UnitOfWorkImpl uow) {
45: this.uow = uow;
46: this.visitedEntities = new RepositoryMap();
47: this.builders = new Builders();
48: }
49:
50: @Override
51: public Object buildClone(Object original, Descriptor descriptor) {
52: if (original == null || descriptor == null) {
53: throw new NullPointerException();
54: }
55: if (LOG.isLoggable(Level.FINER)) {
56: LOG.finer("Cloning object " + original);
57: }
58: return buildCloneImpl(null, null, original, descriptor);
59: }
60:
61: @Override
62: public Object buildClone(Object cloneOwner, Field clonedField, Object original,
63: Descriptor descriptor) {
64: if (cloneOwner == null || original == null || descriptor == null) {
65: throw new NullPointerException();
66: }
67: if (LOG.isLoggable(Level.FINER)) {
68: LOG.finer("Cloning object " + original + " with owner " + cloneOwner);
69: }
70: return buildCloneImpl(cloneOwner, clonedField, original, descriptor);
71: }
72:
73: private Object buildCloneImpl(Object cloneOwner, Field clonedField, Object original,
74: Descriptor descriptor) {
75: if (isOriginalInUoW(original)) {
76: return uow.getCloneForOriginal(original);
77: }
78: final Class<?> cls = original.getClass();
79: final boolean managed = isTypeManaged(cls);
80: if (managed) {
81: final Object visitedClone = getVisitedEntity(descriptor, original);
82: if (visitedClone != null) {
83: return visitedClone;
84: }
85: }
86: final AbstractInstanceBuilder builder = getInstanceBuilder(original);
87: Object clone = builder.buildClone(cloneOwner, clonedField, original, descriptor);
88: if (managed) {
89: // Register visited object before populating attributes to prevent endless cloning cycles
90: putVisitedEntity(descriptor, original, clone);
91: }
92: if (!builder.populatesAttributes() && !isPrimitiveOrString(original.getClass())) {
93: populateAttributes(original, clone, descriptor);
94: }
95: return clone;
96: }
97:
98: /**
99: * Clone all the attributes of the original and set the clone values. This also means cloning any relationships and
100: * their targets.
101: *
102: * @param original Original
103: * @param clone Object
104: */
105: private void populateAttributes(final Object original, Object clone, final Descriptor descriptor) {
106: final Class<?> originalClass = original.getClass();
107: final EntityType<?> et = getMetamodel().entity(originalClass);
108: for (FieldSpecification<?, ?> fs : et.getFieldSpecifications()) {
109: final Field f = fs.getJavaField();
110: final Object origVal = EntityPropertiesUtils.getFieldValue(f, original);
111: if (origVal == null) {
112: continue;
113: }
114: final Class<?> origValueClass = origVal.getClass();
115: Object clonedValue;
116: if (isPrimitiveOrString(origValueClass)) {
117: // The field is an immutable type
118: clonedValue = origVal;
119: } else if (origVal instanceof Collection || origVal instanceof Map) {
120: final Descriptor fieldDescriptor = getFieldDescriptor(f, originalClass, descriptor);
121: // Collection or Map
122: clonedValue = getInstanceBuilder(origVal).buildClone(clone, f, origVal, fieldDescriptor);
123: } else {
124: // Otherwise we have a relationship and we need to clone its target as well
125: if (isOriginalInUoW(origVal)) {
126: // If the reference is already managed
127: clonedValue = uow.getCloneForOriginal(origVal);
128: } else {
129: if (isTypeManaged(origValueClass)) {
130: final Descriptor fieldDescriptor = getFieldDescriptor(f, originalClass, descriptor);
131: clonedValue = getVisitedEntity(descriptor, origVal);
132: if (clonedValue == null) {
133: clonedValue = uow.registerExistingObject(origVal, fieldDescriptor);
134: }
135: } else {
136: clonedValue = buildClone(origVal, descriptor);
137: }
138: }
139: }
140: EntityPropertiesUtils.setFieldValue(f, clone, clonedValue);
141: }
142: cloneIdentifier(original, clone, et);
143: }
144:
145: private Descriptor getFieldDescriptor(Field field, Class<?> entityClass, Descriptor entityDescriptor) {
146: final EntityType<?> et = getMetamodel().entity(entityClass);
147: final FieldSpecification<?, ?> fieldSpec = et.getFieldSpecification(field.getName());
148: return entityDescriptor.getAttributeDescriptor(fieldSpec);
149: }
150:
151: private void cloneIdentifier(Object original, Object clone, EntityType<?> et) {
152: final Identifier identifier = et.getIdentifier();
153: final Object idValue = EntityPropertiesUtils.getFieldValue(identifier.getJavaField(), original);
154: EntityPropertiesUtils.setFieldValue(identifier.getJavaField(), clone, idValue);
155: }
156:
157: /**
158: * Check if the given class is of primitive, String or Enum type. This is used by the {@link
159: * #populateAttributes(Object, Object, Descriptor)} method. If this returns true, the populateAttributes can simply
160: * assign the value.
161: *
162: * @param cls Class<?>
163: * @return boolean
164: */
165: public static boolean isPrimitiveOrString(final Class<?> cls) {
166: return cls.isPrimitive() || String.class.equals(cls) || cls.isEnum()
167: || WRAPPER_TYPES.contains(cls) || URI.class.equals(cls);
168: // TODO Check that URI is effectively final. Also consider adding URL here
169: }
170:
171: @Override
172: public void mergeChanges(Object original, ObjectChangeSet changeSet) {
173: Map<String, ChangeRecord> changes = changeSet.getChanges();
174: try {
175: for (String att : changes.keySet()) {
176: ChangeRecord change = changes.get(att);
177: Field f = original.getClass().getDeclaredField(att);
178: if (isPrimitiveOrString(f.getType())) {
179: EntityPropertiesUtils.setFieldValue(f, original, change.getNewValue());
180: continue;
181: }
182: Object origVal = EntityPropertiesUtils.getFieldValue(f, original);
183: Object newVal = change.getNewValue();
184: if (newVal == null) {
185: EntityPropertiesUtils.setFieldValue(f, original, null);
186: continue;
187: }
188: getInstanceBuilder(newVal).mergeChanges(f, original, origVal, newVal);
189: }
190: } catch (NoSuchFieldException | SecurityException e) {
191: throw new OWLPersistenceException(e);
192: }
193: }
194:
195: Object getVisitedEntity(Descriptor descriptor, Object original) {
196: assert descriptor != null;
197: assert original != null;
198: return visitedEntities.get(descriptor, original);
199: }
200:
201: private void putVisitedEntity(Descriptor descriptor, Object original, Object clone) {
202: assert descriptor != null;
203: visitedEntities.add(descriptor, original, clone);
204: }
205:
206: AbstractInstanceBuilder getInstanceBuilder(Object toClone) {
207: return builders.getBuilder(toClone);
208: }
209:
210: boolean isTypeManaged(Class<?> cls) {
211: return uow.isTypeManaged(cls);
212: }
213:
214: boolean isOriginalInUoW(Object original) {
215: return uow.containsOriginal(original);
216: }
217:
218: Object getOriginal(Object clone) {
219: return uow.getOriginal(clone);
220: }
221:
222: Metamodel getMetamodel() {
223: return uow.getMetamodel();
224: }
225:
226: @Override
227: public void reset() {
228: visitedEntities.clear();
229: }
230:
231: IndirectCollection<?> createIndirectCollection(Object c, Object owner, Field f) {
232: return uow.createIndirectCollection(c, owner, f);
233: }
234:
235: public static synchronized boolean isFieldInferred(final Field f) {
236: return f.getAnnotation(Inferred.class) != null;
237: }
238:
239: private static Set<Class<?>> getWrapperTypes() {
240: HashSet<Class<?>> ret = new HashSet<>();
241: ret.add(Boolean.class);
242: ret.add(Character.class);
243: ret.add(Byte.class);
244: ret.add(Short.class);
245: ret.add(Integer.class);
246: ret.add(Long.class);
247: ret.add(Float.class);
248: ret.add(Double.class);
249: ret.add(Void.class);
250: return ret;
251: }
252:
253: private final class Builders {
254: private AbstractInstanceBuilder defaultBuilder;
255: private AbstractInstanceBuilder dateBuilder;
256: // Lists and Sets
257: private AbstractInstanceBuilder collectionBuilder;
258: private AbstractInstanceBuilder mapBuilder;
259:
260: private Builders() {
261: this.defaultBuilder = new DefaultInstanceBuilder(CloneBuilderImpl.this, uow);
262: this.dateBuilder = new DateInstanceBuilder(CloneBuilderImpl.this, uow);
263: }
264:
265: private AbstractInstanceBuilder getBuilder(Object toClone) {
266:• if (toClone instanceof Date) {
267: return dateBuilder;
268: }
269:• if (toClone instanceof Map) {
270:• if (mapBuilder == null) {
271: this.mapBuilder = new MapInstanceBuilder(CloneBuilderImpl.this, uow);
272: }
273: return mapBuilder;
274:• } else if (toClone instanceof Collection) {
275:• if (collectionBuilder == null) {
276: this.collectionBuilder = new CollectionInstanceBuilder(CloneBuilderImpl.this,
277: uow);
278: }
279: return collectionBuilder;
280: } else {
281: return defaultBuilder;
282: }
283: }
284: }
285: }