Skip to content

Package: TargetClassResolver

TargetClassResolver

nameinstructionbranchcomplexitylinemethod
TargetClassResolver(TypeMap)
M: 0 C: 11
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
TargetClassResolver(TypeMap, TargetClassResolverConfig)
M: 0 C: 9
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
ambiguousTargetType(Collection, List)
M: 0 C: 16
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
doesExpectedClassMatchesTypes(Class, Collection)
M: 0 C: 16
100%
M: 1 C: 3
75%
M: 1 C: 2
67%
M: 0 C: 2
100%
M: 0 C: 1
100%
getTargetClass(Class, Collection)
M: 4 C: 77
95%
M: 2 C: 8
80%
M: 2 C: 4
67%
M: 0 C: 14
100%
M: 0 C: 1
100%
getTargetClassCandidates(Collection)
M: 0 C: 9
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
lambda$getTargetClassCandidates$0(String)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
lambda$null$2(Class, Class)
M: 0 C: 12
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
lambda$null$4(Class, Class)
M: 6 C: 6
50%
M: 3 C: 1
25%
M: 2 C: 1
33%
M: 0 C: 1
100%
M: 0 C: 1
100%
lambda$reduceTargetClassCandidates$1(Class, Class)
M: 0 C: 12
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
lambda$reduceToMostGeneralSuperclasses$5(List, Class)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
lambda$reduceToMostSpecificSubclasses$3(List, Class)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
pickOne(List)
M: 0 C: 20
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
reduceTargetClassCandidates(Class, List)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
reduceToMostGeneralSuperclasses(List)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
reduceToMostSpecificSubclasses(List)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
selectFinalTargetClass(List, List, Collection)
M: 4 C: 28
88%
M: 1 C: 7
88%
M: 1 C: 4
80%
M: 0 C: 7
100%
M: 0 C: 1
100%
selectTargetClassWithSuperclassPreference(List, List)
M: 0 C: 10
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
static {...}
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%

Coverage

1: /**
2: * Copyright (C) 2020 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.jsonld.deserialization.util;
14:
15: import cz.cvut.kbss.jopa.model.annotations.OWLClass;
16: import cz.cvut.kbss.jsonld.common.BeanAnnotationProcessor;
17: import cz.cvut.kbss.jsonld.exception.AmbiguousTargetTypeException;
18: import cz.cvut.kbss.jsonld.exception.TargetTypeException;
19: import org.slf4j.Logger;
20: import org.slf4j.LoggerFactory;
21:
22: import java.lang.reflect.Modifier;
23: import java.util.ArrayList;
24: import java.util.Collection;
25: import java.util.List;
26: import java.util.stream.Collectors;
27:
28: /**
29: * Resolves the type of instance into which a JSON-LD object will be deserialized.
30: */
31: public class TargetClassResolver {
32:
33: private static final Logger LOG = LoggerFactory.getLogger(TargetClassResolver.class);
34:
35: private final TypeMap typeMap;
36:
37: private final TargetClassResolverConfig config;
38:
39: public TargetClassResolver(TypeMap typeMap) {
40: this.typeMap = typeMap;
41: this.config = new TargetClassResolverConfig();
42: }
43:
44: public TargetClassResolver(TypeMap typeMap, TargetClassResolverConfig config) {
45: this.typeMap = typeMap;
46: this.config = config;
47: }
48:
49: /**
50: * Resolves object deserialization target class based on the specified type info.
51: *
52: * @param <T> The type of the expected target class
53: * @param expectedClass Expected class as specified by deserialization return type of field type
54: * @param types Types of the JSON-LD object to deserialize
55: * @return Resolved target class. It has to be a subtype of the {@code expectedClass}
56: * @throws TargetTypeException If the resulting candidate is not assignable to the expected class or it cannot be
57: * determined
58: */
59: public <T> Class<? extends T> getTargetClass(Class<T> expectedClass, Collection<String> types) {
60:• if (types.isEmpty() && config.shouldAllowAssumingTargetType()) {
61: LOG.trace("Assuming target type to be " + expectedClass);
62: return expectedClass;
63: }
64: final List<Class<?>> candidates = getTargetClassCandidates(types);
65: final Class<?> targetCandidate;
66: reduceTargetClassCandidates(expectedClass, candidates);
67: final List<Class<?>> reducedCandidates = new ArrayList<>(candidates);
68: reduceToMostSpecificSubclasses(candidates);
69:• if (candidates.isEmpty()) {
70:• if (doesExpectedClassMatchesTypes(expectedClass, types)) {
71: targetCandidate = expectedClass;
72: } else {
73: throw new TargetTypeException(
74: "Neither " + expectedClass + " nor any of its subclasses matches the types " + types + ".");
75: }
76: } else {
77: targetCandidate = selectFinalTargetClass(candidates, reducedCandidates, types);
78: }
79:• assert expectedClass.isAssignableFrom(targetCandidate);
80: return (Class<? extends T>) targetCandidate;
81: }
82:
83: private List<Class<?>> getTargetClassCandidates(Collection<String> types) {
84: return types.stream().flatMap(t -> typeMap.get(t).stream()).collect(Collectors.toList());
85: }
86:
87: private void reduceTargetClassCandidates(Class<?> expectedClass, List<Class<?>> candidates) {
88:• candidates.removeIf(c -> !expectedClass.isAssignableFrom(c) || Modifier.isAbstract(c.getModifiers()));
89: }
90:
91: private void reduceToMostSpecificSubclasses(List<Class<?>> candidates) {
92:• candidates.removeIf(cls -> candidates.stream().anyMatch(c -> !cls.equals(c) && cls.isAssignableFrom(c)));
93: }
94:
95: private Class<?> selectFinalTargetClass(List<Class<?>> mostSpecificCandidates, List<Class<?>> candidates,
96: Collection<String> types) {
97:• assert mostSpecificCandidates.size() > 0;
98:• if (mostSpecificCandidates.size() > 1) {
99:• if (!config.isOptimisticTypeResolutionEnabled()) {
100: throw ambiguousTargetType(types, mostSpecificCandidates);
101: }
102:• if (config.shouldPreferSuperclass()) {
103: return selectTargetClassWithSuperclassPreference(mostSpecificCandidates, candidates);
104: }
105: }
106: return pickOne(mostSpecificCandidates);
107: }
108:
109: private static Class<?> pickOne(List<Class<?>> candidates) {
110:• return candidates.size() == 1 ? candidates.get(0) :
111: candidates.stream().filter(BeanAnnotationProcessor::hasPropertiesField).findFirst()
112: .orElse(candidates.get(0));
113: }
114:
115: private Class<?> selectTargetClassWithSuperclassPreference(List<Class<?>> mostSpecificCandidates,
116: List<Class<?>> candidates) {
117: candidates.removeAll(mostSpecificCandidates);
118: reduceToMostGeneralSuperclasses(candidates);
119: return pickOne(candidates);
120: }
121:
122: private void reduceToMostGeneralSuperclasses(List<Class<?>> candidates) {
123:• candidates.removeIf(cls -> candidates.stream().anyMatch(c -> !cls.equals(c) && c.isAssignableFrom(cls)));
124: }
125:
126: private static AmbiguousTargetTypeException ambiguousTargetType(Collection<String> types,
127: List<Class<?>> candidates) {
128: return new AmbiguousTargetTypeException(
129: "Object with types " + types + " matches multiple equivalent target classes: " + candidates);
130: }
131:
132: private boolean doesExpectedClassMatchesTypes(Class<?> expectedClass, Collection<String> types) {
133: final OWLClass owlClass = expectedClass.getDeclaredAnnotation(OWLClass.class);
134:• return owlClass != null && types.contains(owlClass.iri());
135: }
136: }