Skip to content

Package: ConverterResolver

ConverterResolver

nameinstructionbranchcomplexitylinemethod
ConverterResolver(Converters)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
createCustomConverter(Class)
M: 0 C: 29
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 7
100%
M: 0 C: 1
100%
createEnumConverter(Class, PropertyAttributes)
M: 0 C: 21
100%
M: 0 C: 3
100%
M: 0 C: 3
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
getConverterGenerics(Class)
M: 0 C: 11
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
isMultilingualReferencedList(Class, PropertyInfo)
M: 0 C: 18
100%
M: 1 C: 5
83%
M: 1 C: 3
75%
M: 0 C: 3
100%
M: 0 C: 1
100%
lambda$getConverterGenerics$0(Type)
M: 1 C: 12
92%
M: 2 C: 2
50%
M: 2 C: 1
33%
M: 0 C: 2
100%
M: 0 C: 1
100%
lambda$getConverterGenerics$1(Class)
M: 8 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
registerConverter(Class, ConverterWrapper)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
resolveConverter(PropertyInfo, PropertyAttributes)
M: 0 C: 65
100%
M: 0 C: 12
100%
M: 0 C: 7
100%
M: 0 C: 17
100%
M: 0 C: 1
100%
resolveConverter(Type)
M: 6 C: 11
65%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 1 C: 3
75%
M: 0 C: 1
100%
resolveConverterAttributeType(Class)
M: 4 C: 15
79%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 0 C: 3
100%
M: 0 C: 1
100%
resolveConverterAxiomType(Class)
M: 4 C: 15
79%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 0 C: 3
100%
M: 0 C: 1
100%
resolveCustomConverter(PropertyInfo, PropertyAttributes)
M: 0 C: 26
100%
M: 0 C: 6
100%
M: 0 C: 4
100%
M: 0 C: 6
100%
M: 0 C: 1
100%
static {...}
M: 0 C: 1
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
verifyTypeIsString(PropertyInfo, Class)
M: 0 C: 12
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 3
100%
M: 0 C: 1
100%

Coverage

1: /*
2: * JOPA
3: * Copyright (C) 2023 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.InstantiationException;
21: import cz.cvut.kbss.jopa.exception.InvalidConverterException;
22: import cz.cvut.kbss.jopa.exception.InvalidFieldMappingException;
23: import cz.cvut.kbss.jopa.model.AttributeConverter;
24: import cz.cvut.kbss.jopa.model.MultilingualString;
25: import cz.cvut.kbss.jopa.model.annotations.Convert;
26: import cz.cvut.kbss.jopa.model.annotations.Sequence;
27: import cz.cvut.kbss.jopa.oom.converter.ConverterWrapper;
28: import cz.cvut.kbss.jopa.oom.converter.CustomConverterWrapper;
29: import cz.cvut.kbss.jopa.oom.converter.ObjectOneOfEnumConverter;
30: import cz.cvut.kbss.jopa.oom.converter.OrdinalEnumConverter;
31: import cz.cvut.kbss.jopa.oom.converter.StringEnumConverter;
32: import cz.cvut.kbss.jopa.oom.converter.ToLexicalFormConverter;
33: import cz.cvut.kbss.jopa.oom.converter.ToMultilingualStringConverter;
34: import cz.cvut.kbss.jopa.oom.converter.ToRdfLiteralConverter;
35: import cz.cvut.kbss.jopa.utils.ReflectionUtils;
36:
37: import java.lang.reflect.ParameterizedType;
38: import java.util.Arrays;
39: import java.util.List;
40: import java.util.Optional;
41:
42: /**
43: * Determines potential converters which may be used on a field.
44: * <p>
45: * Currently, only built-in converters for data and annotation property attributes are supported, but in the future,
46: * custom converters should be also supported.
47: */
48: public class ConverterResolver {
49:
50: private final Converters converters;
51:
52: ConverterResolver(Converters converters) {
53: this.converters = converters;
54: }
55:
56: /**
57: * Determines converter which should be used for transformation of values to and from the specified field.
58: * <p>
59: * Beside custom converters, the system supports a number of built-in converters, which ensure that e.g. widening
60: * conversion or mapping to Java 8 Date/Time API is supported.
61: * <p>
62: * The order of priorities of converter resolution is:
63: * <ol>
64: * <li>Custom converter declared on attribute with {@link Convert}</li>
65: * <li>Custom automatically applied converter declared with {@link cz.cvut.kbss.jopa.model.annotations.Converter}</li>
66: * <li>Built-in converter</li>
67: * </ol>
68: *
69: * @param field The field for which converter should be determined
70: * @param config Mapping configuration extracted during metamodel building
71: * @return Possible converter instance to be used for transformation of values of the specified field. Returns empty
72: * {@code Optional} if no suitable converter is found (or needed)
73: */
74: public Optional<ConverterWrapper<?, ?>> resolveConverter(PropertyInfo field, PropertyAttributes config) {
75: final Class<?> attValueType = config.getType().getJavaType();
76: final Optional<ConverterWrapper<?, ?>> localCustomConverter = resolveCustomConverter(field, config);
77:• if (localCustomConverter.isPresent()) {
78: return localCustomConverter;
79: }
80: final Optional<ConverterWrapper<?, ?>> globalCustomConverter = converters.getCustomConverter(attValueType);
81:• if (globalCustomConverter.isPresent()) {
82: return globalCustomConverter;
83: }
84:• if (attValueType.isEnum()) {
85: return Optional.of(createEnumConverter(attValueType, config));
86: }
87:• if (config.hasDatatype()) {
88: verifyTypeIsString(field, attValueType);
89: return Optional.of(new ToRdfLiteralConverter(config.getDatatype()));
90: }
91:• if (config.isLexicalForm()) {
92: return Optional.of(new ToLexicalFormConverter());
93: }
94:• if (isMultilingualReferencedList(attValueType, field)) {
95: return Optional.of(new ToMultilingualStringConverter());
96: }
97: return Converters.getDefaultConverter(attValueType);
98: }
99:
100: private static ConverterWrapper<?, ?> createEnumConverter(Class<?> valueType, PropertyAttributes pa) {
101:• switch (pa.getEnumType()) {
102: case OBJECT_ONE_OF:
103: return new ObjectOneOfEnumConverter(valueType);
104: case ORDINAL:
105: return new OrdinalEnumConverter(valueType);
106: default:
107: return new StringEnumConverter(valueType);
108: }
109: }
110:
111: private static void verifyTypeIsString(PropertyInfo field, Class<?> attValueType) {
112:• if (!attValueType.equals(String.class)) {
113: throw new InvalidFieldMappingException(
114: "Attributes with explicit datatype identifier must have values of type String. " +
115: "The provided attribute " + field + " has type " + attValueType);
116: }
117: }
118:
119: private static Optional<ConverterWrapper<?, ?>> resolveCustomConverter(PropertyInfo field,
120: PropertyAttributes config) {
121: final Convert convertAnn = field.getAnnotation(Convert.class);
122:• if (convertAnn == null || convertAnn.disableConversion()) {
123: return Optional.empty();
124: }
125:• if (config.getPersistentAttributeType() == Attribute.PersistentAttributeType.OBJECT) {
126: throw new InvalidConverterException("Attribute converters cannot be declared attributes mapped to object properties.");
127: }
128: return Optional.of(createCustomConverter(convertAnn.converter()));
129: }
130:
131: public static ConverterWrapper<?, ?> createCustomConverter(Class<?> converterType) {
132:• if (!AttributeConverter.class.isAssignableFrom(converterType)) {
133: throw new InvalidConverterException(
134: "Specified converter type " + converterType + " does not implement " + AttributeConverter.class);
135: }
136: try {
137: final AttributeConverter<?, ?> converter =
138: (AttributeConverter<?, ?>) ReflectionUtils.instantiateUsingDefaultConstructor(converterType);
139: return new CustomConverterWrapper(converter, resolveConverterAxiomType(converterType));
140: } catch (InstantiationException e) {
141: throw new InvalidConverterException("Unable to instantiate attribute converter.", e);
142: }
143: }
144:
145: private boolean isMultilingualReferencedList(Class<?> elemType, PropertyInfo field) {
146:• return MultilingualString.class.isAssignableFrom(elemType)
147:• && field.getAnnotation(Sequence.class) != null
148:• && List.class.isAssignableFrom(field.getField().getType());
149: }
150:
151: public static Class<?> resolveConverterAttributeType(Class<?> converterType) {
152: final ParameterizedType typeSpec = getConverterGenerics(converterType);
153:• assert typeSpec.getActualTypeArguments().length == 2;
154: return (Class<?>) typeSpec.getActualTypeArguments()[0];
155: }
156:
157: private static ParameterizedType getConverterGenerics(Class<?> converterType) {
158: return (ParameterizedType) Arrays.stream(converterType.getGenericInterfaces())
159:• .filter(t -> t instanceof ParameterizedType && ((ParameterizedType) t).getRawType()
160:• .equals(AttributeConverter.class))
161: .findAny().orElseThrow(
162: () -> new InvalidConverterException(
163: "Specified converter type " + converterType + " does not implement " + AttributeConverter.class.getSimpleName()));
164: }
165:
166: private static Class<?> resolveConverterAxiomType(Class<?> converterType) {
167: final ParameterizedType typeSpec = getConverterGenerics(converterType);
168:• assert typeSpec.getActualTypeArguments().length == 2;
169: return (Class<?>) typeSpec.getActualTypeArguments()[1];
170:
171: }
172:
173: /**
174: * Alternative method for resolving converter. Can be used when the persistent attribute type is not relevant and/or
175: * the class {@link PropertyAttributes} is not used for attribute configuration, e.g. for attributes defined by a
176: * query.
177: * <p>
178: * Determines converter which should be used for transformation of values to and from the specified type of field.
179: * <p>
180: * Beside custom converters, the system supports a number of built-in converters, which ensure that e.g. widening
181: * conversion or mapping to Java 8 Date/Time API is supported.
182: *
183: * @param type attribute type as defined in {@link cz.cvut.kbss.jopa.model.metamodel.Type} (not to be confused with
184: * {@link java.lang.reflect.Type})
185: * @return Possible converter instance to be used for transformation of values of the specified field. Returns empty
186: * {@code Optional} if no suitable converter is found (or needed)
187: * @see cz.cvut.kbss.jopa.model.metamodel.QueryAttribute
188: */
189: public Optional<ConverterWrapper<?, ?>> resolveConverter(Type<?> type) {
190: final Class<?> attValueType = type.getJavaType();
191:• if (attValueType.isEnum()) {
192: return Optional.of(new StringEnumConverter(attValueType));
193: }
194:
195: return converters.getCustomConverter(attValueType);
196: }
197:
198: /**
199: * Registers the specified converter for automatically converting values to the specified attribute type.
200: *
201: * @param attributeType Entity attribute type to convert to/from
202: * @param converter Converter instance
203: */
204: public void registerConverter(Class<?> attributeType, ConverterWrapper<?, ?> converter) {
205: converters.registerConverter(attributeType, converter);
206: }
207: }