Skip to content

Method: canDeclareInheritanceStrategy(IdentifiableType)

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.model.metamodel;
19:
20: import cz.cvut.kbss.jopa.exception.MetamodelInitializationException;
21: import cz.cvut.kbss.jopa.loaders.PersistenceUnitClassFinder;
22: import cz.cvut.kbss.jopa.model.JOPAPersistenceProperties;
23: import cz.cvut.kbss.jopa.model.TypeReferenceMap;
24: import cz.cvut.kbss.jopa.model.annotations.Inheritance;
25: import cz.cvut.kbss.jopa.model.annotations.InheritanceType;
26: import cz.cvut.kbss.jopa.model.annotations.OWLAnnotationProperty;
27: import cz.cvut.kbss.jopa.model.annotations.OWLDataProperty;
28: import cz.cvut.kbss.jopa.model.annotations.OWLObjectProperty;
29: import cz.cvut.kbss.jopa.query.NamedQueryManager;
30: import cz.cvut.kbss.jopa.query.ResultSetMappingManager;
31: import cz.cvut.kbss.jopa.query.mapper.ResultSetMappingProcessor;
32: import cz.cvut.kbss.jopa.utils.Configuration;
33: import cz.cvut.kbss.jopa.utils.Constants;
34: import org.slf4j.Logger;
35: import org.slf4j.LoggerFactory;
36:
37: import java.lang.reflect.Field;
38: import java.lang.reflect.Method;
39: import java.util.Arrays;
40: import java.util.Collections;
41: import java.util.HashMap;
42: import java.util.HashSet;
43: import java.util.List;
44: import java.util.Map;
45: import java.util.Set;
46:
47: public class MetamodelBuilder {
48:
49: private static final Logger LOG = LoggerFactory.getLogger(MetamodelBuilder.class);
50:
51: private final NamedNativeQueryProcessor queryProcessor = new NamedNativeQueryProcessor();
52: private final ResultSetMappingProcessor mappingProcessor;
53:
54: private final Map<Class<?>, AbstractIdentifiableType<?>> typeMap = new HashMap<>();
55: private final Set<Class<?>> inferredClasses = new HashSet<>();
56:
57: private final Map<IdentifiableType<?>, Set<AnnotatedAccessor>> annotatedAccessors = new HashMap<>();
58: private final TypeReferenceMap typeReferenceMap = new TypeReferenceMap();
59:
60: private final ConverterResolver converterResolver;
61:
62: private final Configuration configuration;
63:
64: public MetamodelBuilder(Configuration configuration) {
65: this.configuration = configuration;
66: this.mappingProcessor = new ResultSetMappingProcessor(this);
67: this.converterResolver = new ConverterResolver(new Converters(configuration));
68: }
69:
70: /**
71: * Builds persistence unit metamodel based on classes discovered by the specified class finder.
72: *
73: * @param classFinder Holder of information about classes relevant for persistence unit building
74: */
75: public void buildMetamodel(PersistenceUnitClassFinder classFinder) {
76: assert classFinder != null;
77: classFinder.getAttributeConverters().forEach(converterResolver::registerConverter);
78: classFinder.getEntities().forEach(this::processOWLClass);
79: classFinder.getResultSetMappings().forEach(mappingProcessor::buildMapper);
80:
81: }
82:
83: /**
84: * Builds persistence unit metamodel based on the specified entity classes.
85: * <p>
86: * No additional metamodel features (e.g., custom attribute converters, query result mappers) are built.
87: *
88: * @param entityClasses Entity classes to build metamodel for
89: */
90: public void buildMetamodel(Set<Class<?>> entityClasses) {
91: assert entityClasses != null;
92: entityClasses.forEach(this::processOWLClass);
93: }
94:
95: private <X> void processOWLClass(final Class<X> cls) {
96: if (typeMap.containsKey(cls)) {
97: return;
98: }
99:
100: LOG.debug("Processing OWL class: {}", cls);
101:
102: final TypeBuilderContext<X> et = ManagedClassProcessor.processManagedType(cls, configuration);
103: et.setConverterResolver(converterResolver);
104: et.setPuLanguage(configuration.get(JOPAPersistenceProperties.LANG));
105:
106: processManagedType(et);
107: }
108:
109: private <X> void processMethods(Class<X> cls, AbstractIdentifiableType<X> type) {
110: Arrays.stream(cls.getDeclaredMethods())
111: .filter(MetamodelBuilder::isOWLPropertyMethod)
112: .forEach(m -> addAnnotatedAccessor(type, AnnotatedAccessor.from(m)));
113: }
114:
115: private static boolean isOWLPropertyMethod(Method m) {
116: return m.getAnnotation(OWLDataProperty.class) != null ||
117: m.getAnnotation(OWLAnnotationProperty.class) != null ||
118: m.getAnnotation(OWLObjectProperty.class) != null;
119: }
120:
121: private void addAnnotatedAccessor(IdentifiableType<?> type, AnnotatedAccessor accessor) {
122: annotatedAccessors.computeIfAbsent(type, t -> new HashSet<>());
123: annotatedAccessors.get(type).add(accessor);
124: }
125:
126: private <X> void processManagedType(TypeBuilderContext<X> context) {
127: final AbstractIdentifiableType<X> type = context.getType();
128: final Class<X> cls = type.getJavaType();
129: typeMap.put(cls, type);
130:
131: final Set<AbstractIdentifiableType<? super X>> supertypes = processSupertypes(cls);
132:
133: type.setSupertypes(supertypes);
134:
135: type.setLifecycleListenerManager(new EntityLifecycleCallbackResolver(type).resolve());
136:
137: final ClassFieldMetamodelProcessor<X> fieldProcessor = new ClassFieldMetamodelProcessor<>(context, this);
138:
139: processMethods(cls, type);
140:
141: for (Field f : cls.getDeclaredFields()) {
142: fieldProcessor.processField(f);
143: }
144:
145: if (!type.isAbstract()) {
146: try {
147: type.getIdentifier();
148: } catch (IllegalArgumentException e) {
149: throw new MetamodelInitializationException("Missing identifier field in entity " + cls);
150: }
151: }
152:
153: if (type.getPersistenceType() == Type.PersistenceType.ENTITY) {
154: resolveInheritanceType((IdentifiableEntityType<X>) type);
155: }
156:
157: queryProcessor.processClass(cls);
158: }
159:
160: private <X> Set<AbstractIdentifiableType<? super X>> processSupertypes(Class<X> cls) {
161: Set<AbstractIdentifiableType<? super X>> superTypes = new HashSet<>();
162:
163: final Set<Class<? super X>> managedSuperTypes = ManagedClassProcessor.getManagedSuperInterfaces(cls);
164:
165: final Class<? super X> managedSuperClass = ManagedClassProcessor.getManagedSuperClass(cls);
166:
167: if (managedSuperClass != null) {
168: managedSuperTypes.add(managedSuperClass);
169: }
170:
171: for (Class<? super X> managedSupertype : managedSuperTypes) {
172: if (typeMap.containsKey(managedSupertype)) {
173: superTypes.add((AbstractIdentifiableType<? super X>) typeMap.get(managedSupertype));
174: } else {
175: final TypeBuilderContext<? super X> context = ManagedClassProcessor.processManagedType(managedSupertype, configuration);
176: context.setConverterResolver(converterResolver);
177: context.setPuLanguage(configuration.get(JOPAPersistenceProperties.LANG));
178: processManagedType(context);
179: superTypes.add(context.getType());
180: }
181: }
182: return superTypes;
183: }
184:
185: private static <X> boolean canDeclareInheritanceStrategy(IdentifiableType<X> et) {
186:• return et.getSupertypes() == null || et.getSupertypes()
187: .stream()
188:• .noneMatch(supertype -> supertype.getPersistenceType() == Type.PersistenceType.ENTITY);
189: }
190:
191: private static <X> InheritanceType getInheritanceTypeFromParents(IdentifiableEntityType<X> et) {
192: List<InheritanceType> superTypesInheritanceTypes = et.getSupertypes().stream()
193: .filter(superType -> superType.getPersistenceType() == Type.PersistenceType.ENTITY)
194: .map(abstractIdentifiableType -> ((IdentifiableEntityType<?>) abstractIdentifiableType).getInheritanceType())
195: .distinct()
196: .toList();
197: if (superTypesInheritanceTypes.size() == 1) { /// there is an agreement from all parents on inheritance type
198: return superTypesInheritanceTypes.get(0);
199: } else {
200: throw new MetamodelInitializationException("Entity " + et.getName() + " inherits two distinct inheritance strategies");
201: }
202: }
203:
204: private static <X> void resolveInheritanceType(IdentifiableEntityType<X> et) {
205: final Class<X> cls = et.getJavaType();
206: final Inheritance inheritance = cls.getDeclaredAnnotation(Inheritance.class);
207:
208: if (canDeclareInheritanceStrategy(et)) {
209: if (inheritance != null) {
210: et.setInheritanceType(inheritance.strategy());
211: } else {
212: et.setInheritanceType(Constants.DEFAULT_INHERITANCE_TYPE);
213: }
214: } else if (inheritance != null) {
215: throw new MetamodelInitializationException("Class " + cls +
216: " cannot declare inheritance strategy, because it already inherits it from its supertype.");
217:
218: } else {
219: et.setInheritanceType(getInheritanceTypeFromParents(et));
220: }
221:
222: }
223:
224: public Map<Class<?>, ManagedType<?>> getTypeMap() {
225: return Collections.unmodifiableMap(typeMap);
226: }
227:
228: public <X> AbstractIdentifiableType<X> entity(Class<X> cls) {
229: return (AbstractIdentifiableType<X>) typeMap.get(cls);
230: }
231:
232: public Map<Class<?>, EntityType<?>> getEntities() {
233: final Map<Class<?>, EntityType<?>> map = new HashMap<>();
234: typeMap.entrySet().stream().filter(e -> e.getValue().getPersistenceType() == Type.PersistenceType.ENTITY)
235: .forEach(e -> map.put(e.getKey(), (EntityType<?>) e.getValue()));
236: return map;
237: }
238:
239: public Set<Class<?>> getInferredClasses() {
240: return Collections.unmodifiableSet(inferredClasses);
241: }
242:
243: public NamedQueryManager getNamedQueryManager() {
244: return queryProcessor.getQueryManager();
245: }
246:
247: public ResultSetMappingManager getResultSetMappingManager() {
248: return mappingProcessor.getManager();
249: }
250:
251: void addInferredClass(Class<?> cls) {
252: inferredClasses.add(cls);
253: }
254:
255: @SuppressWarnings("unchecked")
256: <X> ManagedType<X> getEntityClass(Class<X> cls) {
257: if (!typeMap.containsKey(cls)) {
258: processOWLClass(cls);
259: }
260: return (ManagedType<X>) typeMap.get(cls);
261: }
262:
263: /**
264: * Checks whether the specified class represents a managed type already processed by the metamodel builder.
265: *
266: * @param cls Class to check
267: * @return Managed type existence status
268: */
269: boolean hasManagedType(Class<?> cls) {
270: return typeMap.containsKey(cls);
271: }
272:
273: void registerTypeReference(Class<?> referencedType, Class<?> referringType) {
274: assert hasManagedType(referencedType);
275: assert hasManagedType(referringType);
276: typeReferenceMap.addReference(referencedType, referringType);
277: }
278:
279: public TypeReferenceMap getTypeReferenceMap() {
280: return typeReferenceMap;
281: }
282:
283: public Set<AnnotatedAccessor> getAnnotatedAccessorsForClass(IdentifiableType<?> k) {
284: return annotatedAccessors.getOrDefault(k, Collections.emptySet());
285: }
286: }