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: 38
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 7
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%
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: 17 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(Field, PropertyAttributes)
M: 0 C: 61
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(Field)
M: 0 C: 17
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 4
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(Field, Class)
M: 0 C: 21
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 3
100%
M: 0 C: 1
100%

Coverage

1: /**
2: * Copyright (C) 2022 Czech Technical University in Prague
3: * <p>
4: * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
5: * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
6: * version.
7: * <p>
8: * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
9: * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10: * details. You should have received a copy of the GNU General Public License along with this program. If not, see
11: * <http://www.gnu.org/licenses/>.
12: */
13: package cz.cvut.kbss.jopa.model.metamodel;
14:
15: import cz.cvut.kbss.jopa.exception.InstantiationException;
16: import cz.cvut.kbss.jopa.exception.InvalidConverterException;
17: import cz.cvut.kbss.jopa.exception.InvalidFieldMappingException;
18: import cz.cvut.kbss.jopa.model.AttributeConverter;
19: import cz.cvut.kbss.jopa.model.annotations.Convert;
20: import cz.cvut.kbss.jopa.oom.converter.*;
21: import cz.cvut.kbss.jopa.utils.ReflectionUtils;
22:
23: import java.lang.reflect.Field;
24: import java.lang.reflect.ParameterizedType;
25: import java.util.Arrays;
26: import java.util.Optional;
27:
28: /**
29: * Determines potential converters which may be used on a field.
30: * <p>
31: * Currently, only built-in converters for data and annotation property attributes are supported, but in the future,
32: * custom converters should be also supported.
33: */
34: public class ConverterResolver {
35:
36: private final Converters converters;
37:
38: ConverterResolver(Converters converters) {
39: this.converters = converters;
40: }
41:
42: /**
43: * Determines converter which should be used for transformation of values to and from the specified field.
44: * <p>
45: * Beside custom converters, the system supports a number of built-in converters, which ensure that e.g. widening
46: * conversion or mapping to Java 8 Date/Time API is supported.
47: * <p>
48: * The order of priorities of converter resolution is:
49: * <ol>
50: * <li>Custom converter declared on attribute with {@link Convert}</li>
51: * <li>Custom automatically applied converter declared with {@link cz.cvut.kbss.jopa.model.annotations.Converter}</li>
52: * <li>Built-in converter</li>
53: * </ol>
54: *
55: * @param field The field for which converter should be determined
56: * @param config Mapping configuration extracted during metamodel building
57: * @return Possible converter instance to be used for transformation of values of the specified field. Returns empty
58: * {@code Optional} if no suitable converter is found (or needed)
59: */
60: public Optional<ConverterWrapper<?, ?>> resolveConverter(Field field, PropertyAttributes config) {
61:• if (config.getPersistentAttributeType() == Attribute.PersistentAttributeType.OBJECT) {
62: return Optional.empty();
63: }
64: final Class<?> attValueType = config.getType().getJavaType();
65: final Optional<ConverterWrapper<?, ?>> localCustomConverter = resolveCustomConverter(field);
66:• if (localCustomConverter.isPresent()) {
67: return localCustomConverter;
68: }
69: final Optional<ConverterWrapper<?, ?>> globalCustomConverter = converters.getCustomConverter(attValueType);
70:• if (globalCustomConverter.isPresent()) {
71: return globalCustomConverter;
72: }
73:• if (attValueType.isEnum()) {
74: return Optional.of(new EnumConverter(attValueType));
75: }
76:• if (config.hasDatatype()) {
77: verifyTypeIsString(field, attValueType);
78: return Optional.of(new ToRdfLiteralConverter(config.getDatatype()));
79: }
80:• if (config.isLexicalForm()) {
81: return Optional.of(new ToLexicalFormConverter());
82: }
83: return Converters.getDefaultConverter(attValueType);
84: }
85:
86: private static void verifyTypeIsString(Field field, Class<?> attValueType) {
87:• if (!attValueType.equals(String.class)) {
88: throw new InvalidFieldMappingException(
89: "Attributes with explicit datatype identifier must have values of type String. " +
90: "The provided attribute " + field + " has type " + attValueType);
91: }
92: }
93:
94: private static Optional<ConverterWrapper<?, ?>> resolveCustomConverter(Field field) {
95: final Convert convertAnn = field.getAnnotation(Convert.class);
96:• if (convertAnn == null || convertAnn.disableConversion()) {
97: return Optional.empty();
98: }
99: return Optional.of(createCustomConverter(convertAnn.converter()));
100: }
101:
102: public static ConverterWrapper<?, ?> createCustomConverter(Class<?> converterType) {
103:• if (!AttributeConverter.class.isAssignableFrom(converterType)) {
104: throw new InvalidConverterException(
105: "Specified converter type " + converterType + " does not implement " + AttributeConverter.class);
106: }
107: try {
108: final AttributeConverter<?, ?> converter =
109: (AttributeConverter<?, ?>) ReflectionUtils.instantiateUsingDefaultConstructor(converterType);
110: return new CustomConverterWrapper(converter, resolveConverterAxiomType(converterType));
111: } catch (InstantiationException e) {
112: throw new InvalidConverterException("Unable to instantiate attribute converter.", e);
113: }
114: }
115:
116: public static Class<?> resolveConverterAttributeType(Class<?> converterType) {
117: final ParameterizedType typeSpec = getConverterGenerics(converterType);
118:• assert typeSpec.getActualTypeArguments().length == 2;
119: return (Class<?>) typeSpec.getActualTypeArguments()[0];
120: }
121:
122: private static ParameterizedType getConverterGenerics(Class<?> converterType) {
123: return (ParameterizedType) Arrays.stream(converterType.getGenericInterfaces())
124:• .filter(t -> t instanceof ParameterizedType && ((ParameterizedType) t).getRawType()
125:• .equals(AttributeConverter.class))
126: .findAny().orElseThrow(
127: () -> new InvalidConverterException(
128: "Specified converter type " + converterType + " does not implement " + AttributeConverter.class.getSimpleName()));
129: }
130:
131: private static Class<?> resolveConverterAxiomType(Class<?> converterType) {
132: final ParameterizedType typeSpec = getConverterGenerics(converterType);
133:• assert typeSpec.getActualTypeArguments().length == 2;
134: return (Class<?>) typeSpec.getActualTypeArguments()[1];
135:
136: }
137:
138: /**
139: * Alternative method for resolving converter. Can be used when the persistent attribute type is not relevant and/or
140: * the class {@link PropertyAttributes} is not used for attribute configuration, e.g. for attributes defined by a
141: * query.
142: * <p>
143: * Determines converter which should be used for transformation of values to and from the specified type of field.
144: * <p>
145: * Beside custom converters, the system supports a number of built-in converters, which ensure that e.g. widening
146: * conversion or mapping to Java 8 Date/Time API is supported.
147: *
148: * @param type attribute type as defined in {@link cz.cvut.kbss.jopa.model.metamodel.Type} (not to be confused with
149: * {@link java.lang.reflect.Type})
150: * @return Possible converter instance to be used for transformation of values of the specified field. Returns empty
151: * {@code Optional} if no suitable converter is found (or needed)
152: * @see cz.cvut.kbss.jopa.model.metamodel.QueryAttribute
153: */
154: public Optional<ConverterWrapper<?, ?>> resolveConverter(Type<?> type) {
155: final Class<?> attValueType = type.getJavaType();
156:• if (attValueType.isEnum()) {
157: return Optional.of(new EnumConverter(attValueType));
158: }
159:
160: return converters.getCustomConverter(attValueType);
161: }
162:
163: /**
164: * Registers the specified converter for automatically converting values to the specified attribute type.
165: *
166: * @param attributeType Entity attribute type to convert to/from
167: * @param converter Converter instance
168: */
169: public void registerConverter(Class<?> attributeType, ConverterWrapper<?, ?> converter) {
170: converters.registerConverter(attributeType, converter);
171: }
172: }