Package: CollectionInstanceBuilder
CollectionInstanceBuilder
name | instruction | branch | complexity | line | method | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
CollectionInstanceBuilder(CloneBuilderImpl, UnitOfWorkImpl) |
|
|
|
|
|
||||||||||||||||||||
buildClone(Object, Field, Object, CloneConfiguration) |
|
|
|
|
|
||||||||||||||||||||
buildDefaultCollectionInstance(Object, Field, Collection, CloneConfiguration) |
|
|
|
|
|
||||||||||||||||||||
buildInstanceOfSpecialCollection(Object, Field, Collection, CloneConfiguration) |
|
|
|
|
|
||||||||||||||||||||
cloneCollectionContent(Object, Field, Collection, Collection, CloneConfiguration) |
|
|
|
|
|
||||||||||||||||||||
cloneCollectionElement(Object, Field, Object, CloneConfiguration) |
|
|
|
|
|
||||||||||||||||||||
cloneUsingDefaultConstructor(Object, Field, Collection, CloneConfiguration) |
|
|
|
|
|
||||||||||||||||||||
createDefaultCollection(Class) |
|
|
|
|
|
||||||||||||||||||||
createNewInstance(Class, int) |
|
|
|
|
|
||||||||||||||||||||
lambda$cloneUsingDefaultConstructor$0(Object, Field, Collection, CloneConfiguration, Collection) |
|
|
|
|
|
||||||||||||||||||||
mergeChanges(Field, Object, Object, Object) |
|
|
|
|
|
||||||||||||||||||||
populatesAttributes() |
|
|
|
|
|
||||||||||||||||||||
static {...} |
|
|
|
|
|
Coverage
1: /*
2: * JOPA
3: * Copyright (C) 2023 Czech Technical University in Prague
4: *
5: * This library is free software; you can redistribute it and/or
6: * modify it under the terms of the GNU Lesser General Public
7: * License as published by the Free Software Foundation; either
8: * version 3.0 of the License, or (at your option) any later version.
9: *
10: * This library is distributed in the hope that it will be useful,
11: * but WITHOUT ANY WARRANTY; without even the implied warranty of
12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13: * Lesser General Public License for more details.
14: *
15: * You should have received a copy of the GNU Lesser General Public
16: * License along with this library.
17: */
18: package cz.cvut.kbss.jopa.sessions;
19:
20: import cz.cvut.kbss.jopa.adapters.IndirectCollection;
21: import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
22: import cz.cvut.kbss.jopa.model.annotations.Types;
23: import cz.cvut.kbss.jopa.model.metamodel.CollectionType;
24: import cz.cvut.kbss.jopa.utils.CollectionFactory;
25: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
26: import cz.cvut.kbss.jopa.utils.MetamodelUtils;
27: import org.slf4j.Logger;
28: import org.slf4j.LoggerFactory;
29:
30: import java.lang.reflect.Constructor;
31: import java.lang.reflect.Field;
32: import java.lang.reflect.InvocationTargetException;
33: import java.security.AccessController;
34: import java.security.PrivilegedActionException;
35: import java.util.*;
36:
37: /**
38: * Special class for cloning collections. Introduced because some Java collection have no no-argument constructor and
39: * thus they must be cloned specially. NOTE: This class may be removed in case a better cloning mechanisms (namely
40: * database mappings and copy policies) is introduced.
41: */
42: class CollectionInstanceBuilder extends AbstractInstanceBuilder {
43:
44: private static final Logger LOG = LoggerFactory.getLogger(CollectionInstanceBuilder.class);
45:
46: private static final Class<?> singletonListClass = Collections.singletonList(null).getClass();
47: private static final Class<?> singletonSetClass = Collections.singleton(null).getClass();
48: private static final Class<?> arrayAsListClass = Arrays.asList(null, null).getClass();
49:
50: CollectionInstanceBuilder(CloneBuilderImpl builder, UnitOfWorkImpl uow) {
51: super(builder, uow);
52: }
53:
54: /**
55: * This method is the entry point for cloning the Java collections. It clones standard collections as well as
56: * immutable collections and singleton collections.
57: * <p>
58: * Currently supported are List and Set.
59: *
60: * @param collection The collection to clone
61: * @return A deep clone of the specified collection
62: */
63: @Override
64: Object buildClone(Object cloneOwner, Field field, Object collection, CloneConfiguration configuration) {
65:• assert collection instanceof Collection;
66: Collection<?> container = (Collection<?>) collection;
67:• if (container instanceof IndirectCollection<?>) {
68: container = (Collection<?>) ((IndirectCollection<?>) container).unwrap();
69: }
70:• if (Collections.emptyList() == container || Collections.emptySet() == container) {
71: return container;
72: }
73: Object clone = cloneUsingDefaultConstructor(cloneOwner, field, container, configuration);
74:• if (clone == null) {
75: clone = buildInstanceOfSpecialCollection(cloneOwner, field, container, configuration);
76: }
77:• if (clone == null) {
78: clone = buildDefaultCollectionInstance(cloneOwner, field, container, configuration);
79: }
80: clone = uow.createIndirectCollection(clone, cloneOwner, field);
81: return clone;
82: }
83:
84: /**
85: * Clones the specified collection using its default zero argument constructor. If the specified collection has none
86: * (e. g. like SingletonList), this method returns null.
87: *
88: * @param container The collection to clone.
89: * @return cloned collection
90: */
91: private Collection<?> cloneUsingDefaultConstructor(Object cloneOwner, Field field,
92: Collection<?> container, CloneConfiguration configuration) {
93: Class<?> javaClass = container.getClass();
94: final Optional<Collection<?>> result = createNewInstance(javaClass, container.size());
95: // Makes shallow copy
96: result.ifPresent(r -> cloneCollectionContent(cloneOwner, field, container, r, configuration));
97: return result.orElse(null);
98: }
99:
100: private static Optional<Collection<?>> createNewInstance(Class<?> type, int size) {
101: Object[] params = null;
102: Class<?>[] types = {int.class};
103: // Look for constructor taking initial size as parameter
104: Constructor<?> ctor = getDeclaredConstructorFor(type, types);
105:• if (ctor != null) {
106: params = new Object[1];
107: params[0] = size;
108: } else {
109: ctor = DefaultInstanceBuilder.getDeclaredConstructorFor(type, null);
110: }
111:• if (ctor == null) {
112: return Optional.empty();
113: }
114: Collection<?> result = null;
115: try {
116: result = (Collection<?>) ctor.newInstance(params);
117: } catch (InstantiationException | InvocationTargetException | IllegalArgumentException e) {
118: throw new OWLPersistenceException(e);
119: } catch (IllegalAccessException e) {
120: logConstructorAccessException(ctor, e);
121: try {
122: result = (Collection<?>) AccessController.doPrivileged(new PrivilegedInstanceCreator(ctor));
123: } catch (PrivilegedActionException ex) {
124: logPrivilegedConstructorAccessException(ctor, ex);
125: // Do nothing
126: }
127: }
128: return Optional.ofNullable(result);
129: }
130:
131: /**
132: * Clone all the elements in the collection. This will make sure that the cloning process creates a deep copy.
133: *
134: * @param source The collection to clone.
135: */
136: private void cloneCollectionContent(Object cloneOwner, Field field, Collection<?> source,
137: Collection<?> target, CloneConfiguration configuration) {
138:• if (source.isEmpty()) {
139: return;
140: }
141: Collection<Object> tg = (Collection<Object>) target;
142:• for (Object elem : source) {
143:• if (elem == null) {
144: tg.add(null);
145: continue;
146: }
147:• if (CloneBuilderImpl.isImmutable(elem)) {
148: tg.addAll(source);
149: break;
150: }
151: tg.add(cloneCollectionElement(cloneOwner, field, elem, configuration));
152: }
153: }
154:
155: private Object cloneCollectionElement(Object cloneOwner, Field field, Object element,
156: CloneConfiguration configuration) {
157: Object clone;
158:• if (builder.isTypeManaged(element.getClass())) {
159: clone = uow.registerExistingObject(element, configuration.getDescriptor(), configuration.getPostRegister());
160: } else {
161: clone = builder.buildClone(cloneOwner, field, element, configuration.getDescriptor());
162: }
163: return clone;
164: }
165:
166:
167: private Collection<?> buildInstanceOfSpecialCollection(Object cloneOwner, Field field, Collection<?> container,
168: CloneConfiguration configuration) {
169:• if (arrayAsListClass.isInstance(container)) {
170: final List<?> arrayList = new ArrayList<>(container.size());
171: cloneCollectionContent(cloneOwner, field, container, arrayList, configuration);
172: return arrayList;
173:• } else if (singletonListClass.isInstance(container) || singletonSetClass.isInstance(container)) {
174: final Object element = container.iterator().next();
175:• final Object elementClone = CloneBuilderImpl.isImmutable(element) ? element :
176: cloneCollectionElement(cloneOwner, field, element, configuration);
177:• return singletonListClass.isInstance(container) ? Collections.singletonList(elementClone) :
178: Collections.singleton(elementClone);
179: } else {
180: return null;
181: }
182: }
183:
184: private Collection<?> buildDefaultCollectionInstance(Object cloneOwner, Field field, Collection<?> container,
185: CloneConfiguration configuration) {
186: LOG.trace("Unable to find matching collection constructor. Creating default collection.");
187: final Collection<?> clone;
188:• if (container instanceof List) {
189: clone = CollectionFactory.createDefaultCollection(CollectionType.LIST);
190:• } else if (container instanceof Set) {
191: clone = CollectionFactory.createDefaultCollection(CollectionType.SET);
192: } else {
193: throw new OWLPersistenceException(
194: "Cannot clone unsupported collection instance of type " + container.getClass() + ".");
195: }
196: cloneCollectionContent(cloneOwner, field, container, clone, configuration);
197: return clone;
198: }
199:
200: @Override
201: void mergeChanges(Field field, Object target, Object originalValue, Object cloneValue) {
202:• assert originalValue == null || originalValue instanceof Collection;
203:• assert cloneValue instanceof Collection;
204:
205: Collection<Object> clone = (Collection<Object>) cloneValue;
206:• if (clone instanceof IndirectCollection) {
207: clone = ((IndirectCollection<Collection<Object>>) clone).unwrap();
208: }
209: Collection<Object> orig;
210:• if (clone == Collections.emptyList() || clone == Collections.emptySet()) {
211: orig = createDefaultCollection(clone.getClass());
212: } else {
213: final Optional<Collection<?>> origOpt = createNewInstance(clone.getClass(), clone.size());
214: orig = (Collection<Object>) origOpt.orElse(createDefaultCollection(clone.getClass()));
215: }
216: EntityPropertiesUtils.setFieldValue(field, target, orig);
217:
218:• if (clone.isEmpty()) {
219: return;
220: }
221:• for (Object cl : clone) {
222:• orig.add(uow.contains(cl) ? builder.getOriginal(cl) : cl);
223: }
224: final Types types = field.getAnnotation(Types.class);
225:• if (types != null) {
226: MetamodelUtils.checkForModuleSignatureExtension(orig, builder.getMetamodel());
227: }
228: }
229:
230: private static Collection<Object> createDefaultCollection(Class<?> cls) {
231: return CollectionFactory.createDefaultCollection(CollectionType.fromClass(cls));
232: }
233:
234: @Override
235: boolean populatesAttributes() {
236: return true;
237: }
238: }