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