Skip to contentMethod: processInferenceInfo(Field)
1: /**
2: * Copyright (C) 2020 Czech Technical University in Prague
3: *
4: * This program is free software: you can redistribute it and/or modify it under
5: * the terms of the GNU General Public License as published by the Free Software
6: * Foundation, either version 3 of the License, or (at your option) any
7: * later version.
8: *
9: * This program is distributed in the hope that it will be useful, but WITHOUT
10: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12: * details. You should have received a copy of the GNU General Public License
13: * along with this program. If not, see <http://www.gnu.org/licenses/>.
14: */
15: package cz.cvut.kbss.jopa.model.metamodel;
16:
17: import cz.cvut.kbss.jopa.exception.MetamodelInitializationException;
18: import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
19: import cz.cvut.kbss.jopa.model.BeanListenerAspect;
20: import cz.cvut.kbss.jopa.model.IRI;
21: import cz.cvut.kbss.jopa.model.annotations.*;
22: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
23: import org.slf4j.Logger;
24: import org.slf4j.LoggerFactory;
25:
26: import java.lang.reflect.Field;
27: import java.lang.reflect.ParameterizedType;
28: import java.lang.reflect.Type;
29: import java.util.Collection;
30: import java.util.List;
31: import java.util.Map;
32: import java.util.Set;
33:
34: class ClassFieldMetamodelProcessor<X> {
35:
36: private static final Logger LOG = LoggerFactory.getLogger(ClassFieldMetamodelProcessor.class);
37:
38: private final FieldMappingValidator mappingValidator = new FieldMappingValidator();
39:
40: private final Class<X> cls;
41: private final AbstractIdentifiableType<X> et;
42: private final TypeBuilderContext<X> context;
43: private final MetamodelBuilder metamodelBuilder;
44:
45: ClassFieldMetamodelProcessor(TypeBuilderContext<X> context, MetamodelBuilder metamodelBuilder) {
46: this.cls = context.getType().getJavaType();
47: this.context = context;
48: this.et = context.getType();
49: this.metamodelBuilder = metamodelBuilder;
50: }
51:
52: void processField(Field field) {
53: LOG.trace("processing field: {}", field);
54: if (EntityPropertiesUtils.isFieldTransient(field)) {
55: // Do not log static fields
56: if (!EntityPropertiesUtils.isFieldStatic(field)) {
57: LOG.trace("Skipping transient field {}", field);
58: }
59: return;
60: }
61: if (field.getType().isPrimitive()) {
62: throw new MetamodelInitializationException(
63: "Primitive types cannot be used for entity fields. Field " + field + " in class " + cls);
64: }
65:
66: final Class<?> fieldValueCls = getFieldValueType(field);
67: field.setAccessible(true);
68: final InferenceInfo inference = processInferenceInfo(field);
69:
70: if (isTypesField(field)) {
71: processTypesField(field, fieldValueCls, inference);
72: return;
73: }
74: if (isPropertiesField(field)) {
75: processPropertiesField(field, fieldValueCls, inference);
76: return;
77: }
78:
79: final PropertyAttributes propertyAtt = PropertyAttributes.create(field, mappingValidator, context);
80: propertyAtt.resolve(field, metamodelBuilder, fieldValueCls);
81:
82: if (propertyAtt.isKnownOwlProperty()) {
83: createAttribute(field, inference, propertyAtt);
84: } else if (!isAspectIntegrationField(field)) {
85: final boolean success = processIdentifierField(field);
86: if (!success) {
87: throw new MetamodelInitializationException(
88: "Unable to process field " + field + ". It is not transient but has no mapping information.");
89: }
90: }
91: }
92:
93: private static Class<?> getFieldValueType(Field field) {
94: if (Collection.class.isAssignableFrom(field.getType())) {
95: return getSetOrListErasureType((ParameterizedType) field.getGenericType());
96: } else if (field.getType().isArray()) {
97: throw new MetamodelInitializationException("Array persistent attributes are not supported.");
98: } else {
99: return field.getType();
100: }
101: }
102:
103: private static Class<?> getSetOrListErasureType(final ParameterizedType cls) {
104: final Type[] t = cls.getActualTypeArguments();
105:
106: if (t.length != 1) {
107: throw new OWLPersistenceException("Only collections with a single generic parameter are supported.");
108: }
109: Type type = t[0];
110: if (!(type instanceof Class<?>)) {
111: throw new OWLPersistenceException("Only Classes are valid parameters for generic lists and sets.");
112: }
113: return (Class<?>) type;
114: }
115:
116: private InferenceInfo processInferenceInfo(Field field) {
117: final Inferred inferred = field.getAnnotation(Inferred.class);
118:
119: final InferenceInfo inference = new InferenceInfo(inferred);
120:• if (inference.inferred) {
121: metamodelBuilder.addInferredClass(cls);
122: }
123: return inference;
124: }
125:
126: private static boolean isTypesField(Field field) {
127: return field.getAnnotation(Types.class) != null;
128: }
129:
130: private void processTypesField(Field field, Class<?> fieldValueCls, InferenceInfo inference) {
131: Types tt = field.getAnnotation(Types.class);
132: mappingValidator.validateTypesField(field);
133: et.addDirectTypes(new TypesSpecificationImpl<>(et, tt.fetchType(), field, fieldValueCls, inference.inferred));
134: }
135:
136: private static boolean isPropertiesField(Field field) {
137: return field.getAnnotation(Properties.class) != null;
138: }
139:
140: private void processPropertiesField(Field field, Class<?> fieldValueCls, InferenceInfo inference) {
141: Properties properties = field.getAnnotation(Properties.class);
142: mappingValidator.validatePropertiesField(field);
143: final PropertiesParametersResolver paramsResolver = new PropertiesParametersResolver(field);
144: et.addOtherProperties(
145: PropertiesSpecificationImpl.declaringType(et).fetchType(properties.fetchType()).javaField(field)
146: .javaType(fieldValueCls).inferred(inference.inferred)
147: .propertyIdType(paramsResolver.getPropertyIdentifierType())
148: .propertyValueType(paramsResolver.getPropertyValueType()).build());
149: }
150:
151: private void createAttribute(Field field, InferenceInfo inference, PropertyAttributes propertyAttributes) {
152: final AbstractAttribute<X, ?> a;
153: if (field.getType().isAssignableFrom(List.class)) {
154: final Sequence os = field.getAnnotation(Sequence.class);
155:
156: if (os == null) {
157: throw new MetamodelInitializationException("Expected Sequence annotation.");
158: }
159: final ListAttributeImpl.ListAttributeBuilder builder =
160: ListAttributeImpl.builder(propertyAttributes).declaringType(et)
161: .field(field)
162: .inferred(inference.inferred).includeExplicit(inference.includeExplicit)
163: .owlListClass(IRI.create(os.ClassOWLListIRI()))
164: .hasNextProperty(IRI.create(os.ObjectPropertyHasNextIRI()))
165: .hasContentsProperty(IRI.create(os.ObjectPropertyHasContentsIRI()))
166: .sequenceType(os.type());
167: context.getConverterResolver().resolveConverter(field, propertyAttributes).ifPresent(builder::converter);
168: a = builder.build();
169: } else if (field.getType().isAssignableFrom(Set.class)) {
170: final AbstractPluralAttribute.PluralAttributeBuilder builder =
171: SetAttributeImpl.builder(propertyAttributes).declaringType(et)
172: .field(field)
173: .inferred(inference.inferred).includeExplicit(inference.includeExplicit);
174: context.getConverterResolver().resolveConverter(field, propertyAttributes).ifPresent(builder::converter);
175: a = (AbstractAttribute<X, ?>) builder.build();
176: } else if (field.getType().isAssignableFrom(Map.class)) {
177: throw new IllegalArgumentException("NOT YET SUPPORTED");
178: } else {
179: final SingularAttributeImpl.SingularAttributeBuilder builder =
180: SingularAttributeImpl.builder(propertyAttributes).declaringType(et)
181: .field(field)
182: .inferred(inference.inferred).includeExplicit(inference.includeExplicit);
183: context.getConverterResolver().resolveConverter(field, propertyAttributes).ifPresent(builder::converter);
184: a = (AbstractAttribute<X, ?>) builder.build();
185: }
186: et.addDeclaredAttribute(field.getName(), a);
187: }
188:
189: private boolean processIdentifierField(Field field) {
190: final Id id = field.getAnnotation(Id.class);
191: if (id == null) {
192: return false;
193: }
194: mappingValidator.validateIdentifierType(field.getType());
195: et.setIdentifier(new IRIIdentifierImpl<>(et, field, id.generated()));
196: return true;
197: }
198:
199: private static boolean isAspectIntegrationField(Field field) {
200: // AspectJ integration fields cannot be declared transitive (they're generated by the AJC), so we have to
201: // skip them manually
202: return field.getType().equals(BeanListenerAspect.Manageable.class);
203: }
204:
205: private static class InferenceInfo {
206: private final boolean inferred;
207: private final boolean includeExplicit;
208:
209: InferenceInfo(Inferred inferredAnnotation) {
210: this.inferred = inferredAnnotation != null;
211: this.includeExplicit = inferredAnnotation == null || inferredAnnotation.includeExplicit();
212: }
213: }
214: }