1
2
3
4
5
6 package dev.metaschema.databind.model.impl;
7
8 import java.lang.reflect.Field;
9 import java.util.Collection;
10 import java.util.Collections;
11 import java.util.LinkedHashMap;
12 import java.util.LinkedList;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.function.Consumer;
16 import java.util.function.Function;
17 import java.util.stream.Collectors;
18 import java.util.stream.Stream;
19
20 import dev.metaschema.core.model.IContainerFlagSupport;
21 import dev.metaschema.core.util.CollectionUtil;
22 import dev.metaschema.core.util.ObjectUtils;
23 import dev.metaschema.databind.model.IBoundDefinitionModelComplex;
24 import dev.metaschema.databind.model.IBoundInstanceFlag;
25 import dev.metaschema.databind.model.annotations.BoundFlag;
26 import dev.metaschema.databind.model.annotations.Ignore;
27 import edu.umd.cs.findbugs.annotations.NonNull;
28 import edu.umd.cs.findbugs.annotations.Nullable;
29 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
30
31 public class FlagContainerSupport implements IContainerFlagSupport<IBoundInstanceFlag> {
32 @NonNull
33 private final Map<Integer, IBoundInstanceFlag> flagInstances;
34 @Nullable
35 private IBoundInstanceFlag jsonKeyFlag;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
51 public FlagContainerSupport(
52 @NonNull IBoundDefinitionModelComplex definition,
53 @Nullable Consumer<IBoundInstanceFlag> peeker) {
54 Class<?> clazz = definition.getBoundClass();
55
56 Stream<IBoundInstanceFlag> instances = getFlagInstanceFields(clazz).stream()
57 .flatMap(field -> {
58 Stream<IBoundInstanceFlag> stream;
59 if (field.isAnnotationPresent(BoundFlag.class)) {
60 stream = Stream.of(IBoundInstanceFlag.newInstance(field, definition));
61 } else {
62 stream = Stream.empty();
63 }
64 return stream;
65 });
66
67 Consumer<IBoundInstanceFlag> intermediate = this::handleFlagInstance;
68
69 if (peeker != null) {
70 intermediate = intermediate.andThen(peeker);
71 }
72
73 this.flagInstances = CollectionUtil.unmodifiableMap(ObjectUtils.notNull(instances
74 .peek(intermediate)
75 .collect(Collectors.toMap(
76 flag -> flag.getQName().getIndexPosition(),
77 Function.identity(),
78 (v1, v2) -> v2,
79 LinkedHashMap::new))));
80 }
81
82
83
84
85
86
87
88
89 @NonNull
90 protected static Collection<Field> getFlagInstanceFields(Class<?> clazz) {
91 Field[] fields = clazz.getDeclaredFields();
92
93 List<Field> retval = new LinkedList<>();
94
95 Class<?> superClass = clazz.getSuperclass();
96 if (superClass != null) {
97
98 retval.addAll(getFlagInstanceFields(superClass));
99 }
100
101 for (Field field : fields) {
102 if (!field.isAnnotationPresent(BoundFlag.class) || field.isAnnotationPresent(Ignore.class)) {
103
104 continue;
105 }
106
107 retval.add(field);
108 }
109 return ObjectUtils.notNull(Collections.unmodifiableCollection(retval));
110 }
111
112
113
114
115
116
117
118 protected void handleFlagInstance(IBoundInstanceFlag instance) {
119 if (instance.isJsonKey()) {
120 this.jsonKeyFlag = instance;
121 }
122 }
123
124 @Override
125 @NonNull
126 public Map<Integer, IBoundInstanceFlag> getFlagInstanceMap() {
127 return flagInstances;
128 }
129
130 @Override
131 public IBoundInstanceFlag getJsonKeyFlagInstance() {
132 return jsonKeyFlag;
133 }
134 }