Skip to content

Method: static {...}

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.model.IRI;
22: import cz.cvut.kbss.jopa.model.annotations.MappedSuperclass;
23: import cz.cvut.kbss.jopa.model.annotations.Namespace;
24: import cz.cvut.kbss.jopa.model.annotations.Namespaces;
25: import cz.cvut.kbss.jopa.model.annotations.OWLClass;
26: import cz.cvut.kbss.jopa.model.metamodel.gen.ManageableClassGenerator;
27: import cz.cvut.kbss.jopa.utils.ChangeTrackingMode;
28: import cz.cvut.kbss.jopa.utils.Configuration;
29: import cz.cvut.kbss.jopa.utils.NamespaceResolver;
30:
31: import java.lang.reflect.AnnotatedElement;
32: import java.lang.reflect.Modifier;
33: import java.util.Arrays;
34: import java.util.Objects;
35: import java.util.Set;
36: import java.util.stream.Collectors;
37:
38: /**
39: * Utility methods for processing managed types for metamodel construction.
40: */
41: public class ManagedClassProcessor {
42:
43: private ManagedClassProcessor() {
44: throw new AssertionError();
45: }
46:
47: static <T> TypeBuilderContext<T> processManagedType(Class<T> cls, Configuration config) {
48: final NamespaceResolver resolver = detectNamespaces(cls);
49: final AbstractIdentifiableType<T> type;
50: if (isEntityType(cls)) {
51: type = processEntityType(cls, resolver, config);
52: } else if (isMappedSuperclassType(cls)) {
53: type = processMappedSuperclassType(cls);
54: } else {
55: throw new MetamodelInitializationException("Type " + cls + " is not a managed type.");
56: }
57: return new TypeBuilderContext<>(type, resolver);
58: }
59:
60: /**
61: * Detects namespace declarations relevant to the specified class.
62: * <p>
63: * This means namespaces declared on the class itself, namespaces it inherited from super types, as well as
64: * namespaces declared for the package that contains the specified class.
65: * <p>
66: * Namespaces declared directly by {@link Namespace} as well as in {@link Namespaces} are considered.
67: *
68: * @param cls Class to detect namespaces for
69: * @return Namespace resolver containing detected namespaces
70: */
71: public static <T> NamespaceResolver detectNamespaces(Class<T> cls) {
72: final NamespaceResolver resolver = new NamespaceResolver();
73: detectNamespaces(cls, resolver);
74: return resolver;
75: }
76:
77: /**
78: * Detects namespace declarations relevant to the specified class and registers them with the specified {@link
79: * NamespaceResolver}.
80: * <p>
81: * This means namespaces declared on the class itself, namespaces it inherited from super types, as well as
82: * namespaces declared for the package that contains the specified class.
83: * <p>
84: * Namespaces declared directly by {@link Namespace} as well as in {@link Namespaces} are considered.
85: *
86: * @param cls Class to detect namespaces for
87: * @param namespaceResolver Namespace resolver containing detected namespaces
88: */
89: public static <T> void detectNamespaces(Class<T> cls, NamespaceResolver namespaceResolver) {
90: Objects.requireNonNull(cls);
91: Objects.requireNonNull(namespaceResolver);
92: if (cls.getPackage() != null) {
93: resolveNamespaces(cls.getPackage(), namespaceResolver);
94: }
95: resolveNamespaces(cls, namespaceResolver);
96: }
97:
98: private static void resolveNamespaces(AnnotatedElement target, NamespaceResolver namespaceResolver) {
99: final Namespaces namespaces = target.getDeclaredAnnotation(Namespaces.class);
100: if (namespaces != null) {
101: for (Namespace ns : namespaces.value()) {
102: namespaceResolver.registerNamespace(ns.prefix(), ns.namespace());
103: }
104: }
105: final Namespace namespace = target.getDeclaredAnnotation(Namespace.class);
106: if (namespace != null) {
107: namespaceResolver.registerNamespace(namespace.prefix(), namespace.namespace());
108: }
109: }
110:
111: private static <T> IdentifiableEntityType<T> processEntityType(Class<T> cls, NamespaceResolver namespaceResolver,
112: Configuration config) {
113: final OWLClass c = cls.getDeclaredAnnotation(OWLClass.class);
114: assert c != null;
115:
116: if (cls.isInterface() || Modifier.isAbstract(cls.getModifiers())) {
117: return new AbstractEntityType<>(cls, IRI.create(namespaceResolver.resolveFullIri(c.iri())));
118: } else {
119: checkForNoArgConstructor(cls);
120: final Class<? extends T> instantiableType = resolveInstantiableType(cls, config);
121: return new ConcreteEntityType<>(cls, instantiableType, IRI.create(namespaceResolver.resolveFullIri(c.iri())));
122: }
123: }
124:
125: private static <T> Class<? extends T> resolveInstantiableType(Class<T> cls, Configuration config) {
126: if (ChangeTrackingMode.IMMEDIATE == ChangeTrackingMode.resolve(config)) {
127: return new ManageableClassGenerator(config).generate(cls);
128: } else {
129: return cls;
130: }
131: }
132:
133: private static <T> void checkForNoArgConstructor(Class<T> cls) {
134: try {
135: cls.getDeclaredConstructor();
136: } catch (NoSuchMethodException e) {
137: throw new MetamodelInitializationException("Entity " + cls + " is missing required no-arg constructor.", e);
138: }
139: }
140:
141: private static <T> MappedSuperclassTypeImpl<T> processMappedSuperclassType(Class<T> cls) {
142: assert cls.getDeclaredAnnotation(MappedSuperclass.class) != null;
143:
144: return new MappedSuperclassTypeImpl<>(cls);
145: }
146:
147: static <T> Class<? super T> getManagedSuperClass(Class<T> cls) {
148: if (cls.getSuperclass() != null && isManagedType(cls.getSuperclass())) {
149: return cls.getSuperclass();
150: }
151: return null;
152: }
153:
154: static <T> Set<Class<? super T>> getManagedSuperInterfaces(Class<T> cls) {
155: return Arrays.stream(cls.getInterfaces()).filter(ManagedClassProcessor::isManagedType)
156: .map(clazz -> (Class<? super T>) clazz)
157: .collect(Collectors.toSet());
158: }
159:
160: /**
161: * Checks whether the specified class is a managed type.
162: * <p>
163: * That is, if it is an entity type (annotated with {@link OWLClass}) or a mapped superclass (annotated with {@link
164: * MappedSuperclass}).
165: *
166: * @param cls Class to check
167: * @return {@code true} if the class is a managed type, {@code false} otherwise
168: */
169: public static boolean isManagedType(Class<?> cls) {
170: return isEntityType(cls) || isMappedSuperclassType(cls);
171: }
172:
173: /**
174: * Checks whether the specified class is an entity type.
175: * <p>
176: * An entity is annotated with {@link OWLClass}.
177: *
178: * @param cls Class to check
179: * @return {@code true} if the class is an entity type, {@code false} otherwise
180: */
181: public static boolean isEntityType(Class<?> cls) {
182: return cls != null && cls.getDeclaredAnnotation(OWLClass.class) != null;
183: }
184:
185: /**
186: * Checks whether the specified class is a managed superclass.
187: * <p>
188: * An entity is annotated with {@link MappedSuperclass}.
189: *
190: * @param cls Class to check
191: * @return {@code true} if the class is a mapped superclass, {@code false} otherwise
192: */
193: public static boolean isMappedSuperclassType(Class<?> cls) {
194: return cls != null && cls.getDeclaredAnnotation(MappedSuperclass.class) != null;
195: }
196: }