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