Skip to content

Package: CollectionInstanceBuilder

CollectionInstanceBuilder

nameinstructionbranchcomplexitylinemethod
CollectionInstanceBuilder(CloneBuilderImpl, UnitOfWorkImpl)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
buildClone(Object, Field, Object, CloneConfiguration)
M: 4 C: 59
94%
M: 1 C: 11
92%
M: 1 C: 6
86%
M: 0 C: 15
100%
M: 0 C: 1
100%
buildDefaultCollectionInstance(Object, Field, Collection, CloneConfiguration)
M: 0 C: 41
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 9
100%
M: 0 C: 1
100%
buildInstanceOfSpecialCollection(Object, Field, Collection, CloneConfiguration)
M: 0 C: 55
100%
M: 0 C: 10
100%
M: 0 C: 6
100%
M: 0 C: 11
100%
M: 0 C: 1
100%
cloneCollectionContent(Object, Field, Collection, Collection, CloneConfiguration)
M: 0 C: 41
100%
M: 0 C: 8
100%
M: 0 C: 5
100%
M: 0 C: 13
100%
M: 0 C: 1
100%
cloneCollectionElement(Object, Field, Object, CloneConfiguration)
M: 0 C: 27
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
cloneUsingDefaultConstructor(Object, Field, Collection, CloneConfiguration)
M: 0 C: 21
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
createDefaultCollection(Class)
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
createNewInstance(Class, int)
M: 22 C: 43
66%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 8 C: 13
62%
M: 0 C: 1
100%
lambda$cloneUsingDefaultConstructor$0(Object, Field, Collection, CloneConfiguration, Collection)
M: 0 C: 8
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
mergeChanges(Field, Object, Object, Object)
M: 8 C: 90
92%
M: 3 C: 17
85%
M: 3 C: 8
73%
M: 0 C: 19
100%
M: 0 C: 1
100%
populatesAttributes()
M: 0 C: 2
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
static {...}
M: 0 C: 25
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 4
100%
M: 0 C: 1
100%

Coverage

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