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: import org.slf4j.Logger;
27: import org.slf4j.LoggerFactory;
28:
29: import java.lang.reflect.Field;
30: import java.net.URI;
31: import java.net.URL;
32: import java.util.*;
33:
34: public class CloneBuilderImpl implements CloneBuilder {
35:
36: private static final Logger LOG = LoggerFactory.getLogger(CloneBuilderImpl.class);
37:
38: private static final Set<Class<?>> IMMUTABLE_TYPES = getImmutableTypes();
39:
40: // Contains entities that are already cloned, so that we don't clone them again
41: private final RepositoryMap visitedEntities;
42:
43: private final Builders builders;
44:
45: private final UnitOfWorkImpl uow;
46:
47: public CloneBuilderImpl(UnitOfWorkImpl uow) {
48: this.uow = uow;
49: this.visitedEntities = new RepositoryMap();
50: this.builders = new Builders();
51: }
52:
53: @Override
54: public Object buildClone(Object original, Descriptor descriptor) {
55: if (original == null || descriptor == null) {
56: throw new NullPointerException();
57: }
58: LOG.trace("Cloning object {}.", original);
59: return buildCloneImpl(null, null, original, descriptor);
60: }
61:
62: @Override
63: public Object buildClone(Object cloneOwner, Field clonedField, Object original,
64: Descriptor descriptor) {
65: if (cloneOwner == null || original == null || descriptor == null) {
66: throw new NullPointerException();
67: }
68: LOG.trace("Cloning object {} with owner {}", original, cloneOwner);
69: return buildCloneImpl(cloneOwner, clonedField, original, descriptor);
70: }
71:
72: private Object buildCloneImpl(Object cloneOwner, Field clonedField, Object original,
73: Descriptor descriptor) {
74: if (isOriginalInUoW(original)) {
75: return uow.getCloneForOriginal(original);
76: }
77: final Class<?> cls = original.getClass();
78: final boolean managed = isTypeManaged(cls);
79: if (managed) {
80: final Object visitedClone = getVisitedEntity(descriptor, original);
81: if (visitedClone != null) {
82: return visitedClone;
83: }
84: }
85: final AbstractInstanceBuilder builder = getInstanceBuilder(original);
86: Object clone = builder.buildClone(cloneOwner, clonedField, original, descriptor);
87: if (managed) {
88: // Register visited object before populating attributes to prevent endless cloning cycles
89: putVisitedEntity(descriptor, original, clone);
90: }
91: if (!builder.populatesAttributes() && !isImmutable(original.getClass())) {
92: populateAttributes(original, clone, descriptor);
93: }
94: return clone;
95: }
96:
97: /**
98: * Clone all the attributes of the original and set the clone values. This also means cloning any relationships and
99: * their targets.
100: *
101: * @param original Original
102: * @param clone Object
103: */
104: private void populateAttributes(final Object original, Object clone, final Descriptor descriptor) {
105: final Class<?> originalClass = original.getClass();
106: final EntityType<?> et = getMetamodel().entity(originalClass);
107: for (FieldSpecification<?, ?> fs : et.getFieldSpecifications()) {
108: final Field f = fs.getJavaField();
109: final Object origVal = EntityPropertiesUtils.getFieldValue(f, original);
110: if (origVal == null) {
111: continue;
112: }
113: final Class<?> origValueClass = origVal.getClass();
114: Object clonedValue;
115: if (isImmutable(origValueClass)) {
116: // The field is an immutable type
117: clonedValue = origVal;
118: } else if (origVal instanceof Collection || origVal instanceof Map) {
119: final Descriptor fieldDescriptor = getFieldDescriptor(f, originalClass, descriptor);
120: // Collection or Map
121: clonedValue = getInstanceBuilder(origVal).buildClone(clone, f, origVal, fieldDescriptor);
122: } else {
123: // Otherwise we have a relationship and we need to clone its target as well
124: if (isOriginalInUoW(origVal)) {
125: // If the reference is already managed
126: clonedValue = uow.getCloneForOriginal(origVal);
127: } else {
128: if (isTypeManaged(origValueClass)) {
129: final Descriptor fieldDescriptor = getFieldDescriptor(f, originalClass, descriptor);
130: clonedValue = getVisitedEntity(descriptor, origVal);
131: if (clonedValue == null) {
132: clonedValue = uow.registerExistingObject(origVal, fieldDescriptor);
133: }
134: } else {
135: clonedValue = buildClone(origVal, descriptor);
136: }
137: }
138: }
139: EntityPropertiesUtils.setFieldValue(f, clone, clonedValue);
140: }
141: cloneIdentifier(original, clone, et);
142: }
143:
144: private Descriptor getFieldDescriptor(Field field, Class<?> entityClass, Descriptor entityDescriptor) {
145: final EntityType<?> et = getMetamodel().entity(entityClass);
146: final FieldSpecification<?, ?> fieldSpec = et.getFieldSpecification(field.getName());
147: return entityDescriptor.getAttributeDescriptor(fieldSpec);
148: }
149:
150: private void cloneIdentifier(Object original, Object clone, EntityType<?> et) {
151: final Identifier identifier = et.getIdentifier();
152: final Object idValue = EntityPropertiesUtils.getFieldValue(identifier.getJavaField(), original);
153: EntityPropertiesUtils.setFieldValue(identifier.getJavaField(), clone, idValue);
154: }
155:
156: /**
157: * Check if the given class is an immutable type. This is used by the {@link
158: * #populateAttributes(Object, Object, Descriptor)} method. If this returns true, the populateAttributes can simply
159: * assign the value.
160: *
161: * @param cls the class to check
162: * @return Whether the class represents immutable objects
163: */
164: public static boolean isImmutable(final Class<?> cls) {
165: return cls.isPrimitive() || cls.isEnum()|| IMMUTABLE_TYPES.contains(cls);
166: }
167:
168: @Override
169: public void mergeChanges(Object original, ObjectChangeSet changeSet) {
170: Map<String, ChangeRecord> changes = changeSet.getChanges();
171: try {
172: for (String att : changes.keySet()) {
173: ChangeRecord change = changes.get(att);
174: Field f = original.getClass().getDeclaredField(att);
175: if (isImmutable(f.getType())) {
176: EntityPropertiesUtils.setFieldValue(f, original, change.getNewValue());
177: continue;
178: }
179: Object origVal = EntityPropertiesUtils.getFieldValue(f, original);
180: Object newVal = change.getNewValue();
181: if (newVal == null) {
182: EntityPropertiesUtils.setFieldValue(f, original, null);
183: continue;
184: }
185: getInstanceBuilder(newVal).mergeChanges(f, original, origVal, newVal);
186: }
187: } catch (NoSuchFieldException | SecurityException e) {
188: throw new OWLPersistenceException(e);
189: }
190: }
191:
192: Object getVisitedEntity(Descriptor descriptor, Object original) {
193: assert descriptor != null;
194: assert original != null;
195: return visitedEntities.get(descriptor, original);
196: }
197:
198: private void putVisitedEntity(Descriptor descriptor, Object original, Object clone) {
199: assert descriptor != null;
200: visitedEntities.add(descriptor, original, clone);
201: }
202:
203: AbstractInstanceBuilder getInstanceBuilder(Object toClone) {
204: return builders.getBuilder(toClone);
205: }
206:
207: boolean isTypeManaged(Class<?> cls) {
208: return uow.isTypeManaged(cls);
209: }
210:
211: boolean isOriginalInUoW(Object original) {
212: return uow.containsOriginal(original);
213: }
214:
215: Object getOriginal(Object clone) {
216: return uow.getOriginal(clone);
217: }
218:
219: Metamodel getMetamodel() {
220: return uow.getMetamodel();
221: }
222:
223: @Override
224: public void reset() {
225: visitedEntities.clear();
226: }
227:
228: IndirectCollection<?> createIndirectCollection(Object c, Object owner, Field f) {
229: return uow.createIndirectCollection(c, owner, f);
230: }
231:
232: public static synchronized boolean isFieldInferred(final Field f) {
233: return f.getAnnotation(Inferred.class) != null;
234: }
235:
236: private static Set<Class<?>> getImmutableTypes() {
237: HashSet<Class<?>> ret = new HashSet<>();
238: ret.add(Boolean.class);
239: ret.add(Character.class);
240: ret.add(Byte.class);
241: ret.add(Short.class);
242: ret.add(Integer.class);
243: ret.add(Long.class);
244: ret.add(Float.class);
245: ret.add(Double.class);
246: ret.add(Void.class);
247: ret.add(String.class);
248: ret.add(URI.class);
249: ret.add(URL.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: }