Skip to content

Package: MapInstanceBuilder

MapInstanceBuilder

nameinstructionbranchcomplexitylinemethod
MapInstanceBuilder(CloneBuilder, UnitOfWork)
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: 13 C: 52
80%
M: 3 C: 7
70%
M: 2 C: 4
67%
M: 3 C: 12
80%
M: 0 C: 1
100%
buildSingletonClone(Object, Field, Map, CloneConfiguration)
M: 13 C: 43
77%
M: 5 C: 5
50%
M: 4 C: 2
33%
M: 0 C: 8
100%
M: 0 C: 1
100%
cloneMapContent(Object, Field, Map, Map, CloneConfiguration)
M: 25 C: 54
68%
M: 4 C: 6
60%
M: 3 C: 3
50%
M: 4 C: 14
78%
M: 0 C: 1
100%
cloneObject(Object, Field, Object, CloneConfiguration)
M: 17 C: 19
53%
M: 2 C: 2
50%
M: 2 C: 1
33%
M: 2 C: 4
67%
M: 0 C: 1
100%
cloneUsingDefaultConstructor(Object, Field, Class, Map, CloneConfiguration)
M: 0 C: 18
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
createDefaultMap(int)
M: 10 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
createNewInstance(Class, int)
M: 10 C: 43
81%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 4 C: 13
76%
M: 0 C: 1
100%
lambda$cloneUsingDefaultConstructor$0(Object, Field, Map, CloneConfiguration, Map)
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%
lambda$mergeChanges$1(Map)
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
mergeChanges(Field, Object, Object, Object)
M: 19 C: 83
81%
M: 5 C: 13
72%
M: 5 C: 5
50%
M: 1 C: 18
95%
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: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%

Coverage

1: /*
2: * JOPA
3: * Copyright (C) 2024 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.exceptions.OWLPersistenceException;
21: import cz.cvut.kbss.jopa.proxy.change.ChangeTrackingIndirectCollection;
22: import cz.cvut.kbss.jopa.proxy.change.ChangeTrackingIndirectMap;
23: import cz.cvut.kbss.jopa.sessions.util.CloneConfiguration;
24: import cz.cvut.kbss.jopa.sessions.util.CloneRegistrationDescriptor;
25: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
26:
27: import java.lang.reflect.Constructor;
28: import java.lang.reflect.Field;
29: import java.lang.reflect.InvocationTargetException;
30: import java.util.Collection;
31: import java.util.Collections;
32: import java.util.HashMap;
33: import java.util.Map;
34: import java.util.Map.Entry;
35: import java.util.Optional;
36:
37: class MapInstanceBuilder extends AbstractInstanceBuilder {
38:
39: private static final Class<?> singletonMapClass = Collections.singletonMap(null, null).getClass();
40:
41: MapInstanceBuilder(CloneBuilder builder, UnitOfWork uow) {
42: super(builder, uow);
43: }
44:
45: @Override
46: Object buildClone(Object cloneOwner, Field field, Object original, CloneConfiguration configuration) {
47: Map<?, ?> orig = (Map<?, ?>) original;
48:• if (original instanceof ChangeTrackingIndirectCollection) {
49: orig = ((ChangeTrackingIndirectCollection<Map<?, ?>>) original).unwrap();
50: }
51:• if (orig == Collections.emptyMap()) {
52: return orig;
53: }
54: final Class<?> origCls = orig.getClass();
55: Map<?, ?> clone;
56: clone = cloneUsingDefaultConstructor(cloneOwner, field, origCls, orig, configuration);
57:• if (clone == null) {
58:• if (singletonMapClass.isInstance(orig)) {
59: clone = buildSingletonClone(cloneOwner, field, orig, configuration);
60:• } else if (Collections.emptyMap().equals(orig)) {
61: clone = orig;
62: } else {
63: throw new IllegalArgumentException("Unsupported map type " + origCls);
64: }
65: }
66: clone = new ChangeTrackingIndirectMap<>(cloneOwner, field, uow, clone);
67: return clone;
68:
69: }
70:
71: private Map<?, ?> cloneUsingDefaultConstructor(Object cloneOwner, Field field, Class<?> origCls, Map<?, ?> original,
72: CloneConfiguration configuration) {
73: Optional<Map<?, ?>> result = createNewInstance(origCls, original.size());
74: result.ifPresent(r -> cloneMapContent(cloneOwner, field, original, r, configuration));
75: return result.orElse(null);
76: }
77:
78: private static Optional<Map<?, ?>> createNewInstance(Class<?> type, int size) {
79: Map<?, ?> result = null;
80: final Class<?>[] types = {int.class};
81: Object[] params;
82: Constructor<?> c = getDeclaredConstructorFor(type, types);
83:• if (c != null) {
84: params = new Object[1];
85: params[0] = size;
86: } else {
87: c = getDeclaredConstructorFor(type, null);
88: params = null;
89: }
90:• if (c == null) {
91: return Optional.empty();
92: }
93: try {
94: result = (Map<?, ?>) c.newInstance(params);
95: } catch (InstantiationException | IllegalArgumentException | InvocationTargetException e) {
96: throw new OWLPersistenceException(e);
97: } catch (IllegalAccessException e) {
98: logConstructorAccessException(c, e);
99: // Do nothing
100: }
101: return Optional.ofNullable(result);
102: }
103:
104: private Map<?, ?> buildSingletonClone(Object cloneOwner, Field field, Map<?, ?> orig,
105: CloneConfiguration configuration) {
106: Entry<?, ?> e = orig.entrySet().iterator().next();
107:• Object key = CloneBuilder.isImmutable(e.getKey()) ? e.getKey() :
108: cloneObject(cloneOwner, field, e.getKey(), configuration);
109:• Object value = CloneBuilder.isImmutable(e.getValue()) ? e.getValue() :
110: cloneObject(cloneOwner, field, e.getValue(), configuration);
111:• if ((value instanceof Collection || value instanceof Map) && !(value instanceof ChangeTrackingIndirectCollection)) {
112: value = uow.createIndirectCollection(value, cloneOwner, field);
113: }
114: return Collections.singletonMap(key, value);
115: }
116:
117: private void cloneMapContent(Object cloneOwner, Field field, Map<?, ?> source,
118: Map<?, ?> target, CloneConfiguration configuration) {
119:• if (source.isEmpty()) {
120: return;
121: }
122: final Map<Object, Object> m = (Map<Object, Object>) target;
123: final Entry<?, ?> tmp = source.entrySet().iterator().next();
124: // Note: If we encounter null -> null mapping first, the whole map will be treated as immutable type map, which can be incorrect
125: final boolean keyPrimitive = CloneBuilder.isImmutable(tmp.getKey());
126: final boolean valuePrimitive = CloneBuilder.isImmutable(tmp.getValue());
127:• for (Entry<?, ?> e : source.entrySet()) {
128: Object key;
129: Object value;
130:• if (keyPrimitive) {
131:• if (valuePrimitive) {
132: m.putAll(source);
133: break;
134: }
135: key = e.getKey();
136: value = cloneObject(cloneOwner, field, e.getValue(), configuration);
137: } else {
138: key = cloneObject(cloneOwner, field, e.getKey(), configuration);
139:• value = valuePrimitive ? e.getValue() : cloneObject(cloneOwner, field, e.getValue(), configuration);
140: }
141: m.put(key, value);
142: }
143: }
144:
145: private Object cloneObject(Object owner, Field field, Object obj, CloneConfiguration configuration) {
146: Object clone;
147:• if (obj == null) {
148: clone = null;
149:• } else if (builder.isTypeManaged(obj.getClass())) {
150: clone = uow.registerExistingObject(obj, new CloneRegistrationDescriptor(configuration.getDescriptor()).postCloneHandlers(configuration.getPostRegister()));
151: } else {
152: clone = builder.buildClone(owner, field, obj, configuration.getDescriptor());
153: }
154: return clone;
155: }
156:
157: @Override
158: void mergeChanges(Field field, Object target, Object originalValue, Object cloneValue) {
159:• assert (originalValue == null) || (originalValue instanceof Map);
160:• assert cloneValue instanceof Map;
161:
162: Map<Object, Object> orig = (Map<Object, Object>) originalValue;
163:• final Map<Object, Object> clone = cloneValue instanceof ChangeTrackingIndirectCollection ?
164: ((ChangeTrackingIndirectCollection<Map<Object, Object>>) cloneValue).unwrap() : (Map<Object, Object>) cloneValue;
165:• if (orig == null) {
166: orig = (Map<Object, Object>) createNewInstance(clone.getClass(), clone.size()).orElseGet(() -> createDefaultMap(clone.size()));
167: EntityPropertiesUtils.setFieldValue(field, target, orig);
168: }
169: orig.clear();
170:• if (clone.isEmpty()) {
171: return;
172: }
173:• for (Entry<?, ?> e : clone.entrySet()) {
174: final Object key = e.getKey();
175: final Object value = e.getValue();
176:• final Object keyToPut = uow.contains(key) ? builder.getOriginal(key) : key;
177:• final Object valueToPut = uow.contains(value) ? builder.getOriginal(value) : value;
178: orig.put(keyToPut, valueToPut);
179: }
180: }
181:
182: private static Map<Object, Object> createDefaultMap(int size) {
183:• return new HashMap<>(size > 1 ? size : 16);
184: }
185:
186: @Override
187: boolean populatesAttributes() {
188: return true;
189: }
190: }