Package: DefaultInstanceBuilder
DefaultInstanceBuilder
name | instruction | branch | complexity | line | method | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
DefaultInstanceBuilder(CloneBuilder, UnitOfWork) |
|
|
|
|
|
||||||||||||||||||||
buildClone(Object, Field, Object, CloneConfiguration) |
|
|
|
|
|
||||||||||||||||||||
buildNewInstanceUsingDefaultConstructor(Class) |
|
|
|
|
|
||||||||||||||||||||
mergeChanges(Field, Object, Object, Object) |
|
|
|
|
|
||||||||||||||||||||
mergeFieldChanges(Object, Object, Class) |
|
|
|
|
|
||||||||||||||||||||
static {...} |
|
|
|
|
|
||||||||||||||||||||
tryCreatingUsingConstructorWithArguments(Constructor) |
|
|
|
|
|
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.sessions.util.CloneConfiguration;
22: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
23: import org.slf4j.Logger;
24: import org.slf4j.LoggerFactory;
25:
26: import java.lang.reflect.Constructor;
27: import java.lang.reflect.Field;
28: import java.lang.reflect.InvocationTargetException;
29: import java.util.ArrayList;
30: import java.util.Arrays;
31: import java.util.Collection;
32: import java.util.List;
33: import java.util.Optional;
34:
35: /**
36: * This class has responsibility for creating new instances of various kinds of objects. It handles security
37: * restrictions as well.
38: */
39: class DefaultInstanceBuilder extends AbstractInstanceBuilder {
40:
41: private static final Logger LOG = LoggerFactory.getLogger(DefaultInstanceBuilder.class);
42:
43: DefaultInstanceBuilder(CloneBuilder builder, UnitOfWork uow) {
44: super(builder, uow);
45: }
46:
47: /**
48: * Builds a new instance of the specified class.
49: *
50: * @return New object of the given class.
51: */
52: @Override
53: Object buildClone(Object cloneOwner, Field field, Object original, CloneConfiguration config) {
54: final Class<?> javaClass = original.getClass();
55:
56: // Assume the original is immutable and there is no other InstanceBuilder
57:• if (!builder.isTypeManaged(javaClass)) {
58: return original;
59: }
60:
61: Object newInstance = buildNewInstanceUsingDefaultConstructor(javaClass);
62:• if (newInstance == null) {
63: final Field[] fields = javaClass.getDeclaredFields();
64: List<Class<?>> fieldClasses = new ArrayList<>();
65: Constructor<?> c;
66: try {
67:• for (Field f : fields) {
68:• if (EntityPropertiesUtils.isFieldTransient(f)) {
69: continue;
70: }
71: Class<?>[] args = {f.getType()};
72: c = getDeclaredConstructorFor(javaClass, args);
73:• if (c == null) {
74: fieldClasses.add(f.getType());
75: } else {
76: newInstance = tryCreatingUsingConstructorWithArguments(c).orElse(null);
77: }
78: }
79: Class<?>[] args = new Class<?>[fieldClasses.size()];
80: args = fieldClasses.toArray(args);
81: c = getDeclaredConstructorFor(javaClass, args);
82:• if (c != null) {
83: newInstance = tryCreatingUsingConstructorWithArguments(c).orElse(null);
84: }
85: } catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
86: InvocationTargetException e) {
87: throw new OWLPersistenceException(e);
88: }
89: }
90:• if (newInstance == null) {
91: throw new OWLPersistenceException("Unable to create a new object or to find a suitable constructor for " + javaClass.getName());
92: }
93: return newInstance;
94: }
95:
96: private static Optional<Object> tryCreatingUsingConstructorWithArguments(
97: Constructor<?> ctor) throws InvocationTargetException, InstantiationException, IllegalAccessException {
98: try {
99: Object[] params = new Object[ctor.getParameterCount()];
100: Arrays.fill(params, null);
101: return Optional.of(ctor.newInstance(params));
102: } catch (SecurityException e) {
103: logConstructorAccessException(ctor, e);
104: return Optional.empty();
105: }
106: }
107:
108: @Override
109: void mergeChanges(Field field, Object target, Object originalValue, Object cloneValue) {
110:• if (originalValue == null) {
111: Object clOrig = builder.getOriginal(cloneValue);
112:• if (clOrig == null) {
113: clOrig = cloneValue;
114: }
115: EntityPropertiesUtils.setFieldValue(field, target, clOrig);
116: return;
117: }
118: Class<?> cls = originalValue.getClass();
119:• if (builder.isTypeManaged(cls) && builder.getOriginal(cloneValue) != null) {
120: EntityPropertiesUtils.setFieldValue(field, target, builder.getOriginal(cloneValue));
121: } else {
122: mergeFieldChanges(originalValue, cloneValue, cls);
123: }
124: }
125:
126: private void mergeFieldChanges(Object originalValue, Object cloneValue, Class<?> cls) {
127: List<Field> fields = EntityPropertiesUtils.getAllFields(cls);
128:• for (Field f : fields) {
129: Object clVal = EntityPropertiesUtils.getFieldValue(f, cloneValue);
130: Object origVal = EntityPropertiesUtils.getFieldValue(f, originalValue);
131:• if (!(clVal instanceof Collection) && !builder.isOriginalInUoW(origVal)) {
132: EntityPropertiesUtils.setFieldValue(f, originalValue, clVal);
133: } else {
134: builder.getInstanceBuilder(origVal).mergeChanges(f, originalValue, origVal, clVal);
135: }
136: }
137: }
138:
139: /**
140: * Builds a new instance of the specified class, using its no-argument constructor.
141: *
142: * @return New object of the given class, or null if the class has no no-argument constructor.
143: */
144: private static Object buildNewInstanceUsingDefaultConstructor(final Class<?> javaClass) {
145: final Constructor<?> c = getDeclaredConstructorFor(javaClass, null);
146:• if (c != null) {
147: try {
148: return c.newInstance((Object[]) null);
149: } catch (SecurityException e) {
150: logConstructorAccessException(c, e);
151: // Do nothing
152: } catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
153: InvocationTargetException e) {
154: LOG.trace("Class {} does not have a suitable no-arg constructor.", javaClass);
155: // Do nothing
156: }
157: }
158: return null;
159: }
160: }