Skip to content

Package: CollectionInstanceBuilder

CollectionInstanceBuilder

nameinstructionbranchcomplexitylinemethod
CollectionInstanceBuilder(CloneBuilderImpl, UnitOfWork)
M: 0 C: 8
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
buildClone(Object, Field, Object, CloneConfiguration)
M: 4 C: 60
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: 6 C: 44
88%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 2 C: 10
83%
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: 79
91%
M: 2 C: 14
88%
M: 2 C: 7
78%
M: 0 C: 17
100%
M: 0 C: 1
100%
static {...}
M: 0 C: 29
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 6
100%
M: 0 C: 1
100%

Coverage

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