Skip to contentMethod: mergeChanges(Field, Object, Object, Object)
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: if (CloneBuilder.isImmutable(original)) {
55: return original;
56: }
57: final Class<?> javaClass = original.getClass();
58: Object newInstance = buildNewInstanceUsingDefaultConstructor(javaClass);
59: if (newInstance == null) {
60: final Field[] fields = javaClass.getDeclaredFields();
61: List<Class<?>> fieldClasses = new ArrayList<>();
62: Constructor<?> c;
63: try {
64: for (Field f : fields) {
65: if (EntityPropertiesUtils.isFieldTransient(f)) {
66: continue;
67: }
68: Class<?>[] args = {f.getType()};
69: c = getDeclaredConstructorFor(javaClass, args);
70: if (c == null) {
71: fieldClasses.add(f.getType());
72: } else {
73: newInstance = tryCreatingUsingConstructorWithArguments(c).orElse(null);
74: }
75: }
76: Class<?>[] args = new Class<?>[fieldClasses.size()];
77: args = fieldClasses.toArray(args);
78: c = getDeclaredConstructorFor(javaClass, args);
79: if (c != null) {
80: newInstance = tryCreatingUsingConstructorWithArguments(c).orElse(null);
81: }
82: } catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
83: InvocationTargetException e) {
84: throw new OWLPersistenceException(e);
85: }
86: }
87: if (newInstance == null) {
88: throw new OWLPersistenceException("Unable to create a new object or to find a suitable constructor for " + javaClass.getName());
89: }
90: return newInstance;
91: }
92:
93: private static Optional<Object> tryCreatingUsingConstructorWithArguments(
94: Constructor<?> ctor) throws InvocationTargetException, InstantiationException, IllegalAccessException {
95: try {
96: Object[] params = new Object[ctor.getParameterCount()];
97: Arrays.fill(params, null);
98: return Optional.of(ctor.newInstance(params));
99: } catch (SecurityException e) {
100: logConstructorAccessException(ctor, e);
101: return Optional.empty();
102: }
103: }
104:
105: @Override
106: void mergeChanges(Field field, Object target, Object originalValue, Object cloneValue) {
107:• if (originalValue == null) {
108: Object clOrig = builder.getOriginal(cloneValue);
109:• if (clOrig == null) {
110: clOrig = cloneValue;
111: }
112: EntityPropertiesUtils.setFieldValue(field, target, clOrig);
113: return;
114: }
115: Class<?> cls = originalValue.getClass();
116:• if (builder.isTypeManaged(cls) && builder.getOriginal(cloneValue) != null) {
117: EntityPropertiesUtils.setFieldValue(field, target, builder.getOriginal(cloneValue));
118: } else {
119: mergeFieldChanges(originalValue, cloneValue, cls);
120: }
121: }
122:
123: private void mergeFieldChanges(Object originalValue, Object cloneValue, Class<?> cls) {
124: List<Field> fields = EntityPropertiesUtils.getAllFields(cls);
125: for (Field f : fields) {
126: Object clVal = EntityPropertiesUtils.getFieldValue(f, cloneValue);
127: Object origVal = EntityPropertiesUtils.getFieldValue(f, originalValue);
128: if (!(clVal instanceof Collection) && !builder.isOriginalInUoW(origVal)) {
129: EntityPropertiesUtils.setFieldValue(f, originalValue, clVal);
130: } else {
131: builder.getInstanceBuilder(origVal).mergeChanges(f, originalValue, origVal, clVal);
132: }
133: }
134: }
135:
136: /**
137: * Builds a new instance of the specified class, using its no-argument constructor.
138: *
139: * @return New object of the given class, or null if the class has no no-argument constructor.
140: */
141: private static Object buildNewInstanceUsingDefaultConstructor(final Class<?> javaClass) {
142: final Constructor<?> c = getDeclaredConstructorFor(javaClass, null);
143: if (c != null) {
144: try {
145: return c.newInstance((Object[]) null);
146: } catch (SecurityException e) {
147: logConstructorAccessException(c, e);
148: // Do nothing
149: } catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
150: InvocationTargetException e) {
151: LOG.trace("Class {} does not have a suitable no-arg constructor.", javaClass);
152: // Do nothing
153: }
154: }
155: return null;
156: }
157: }