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 javax.xml.namespace.QName;
29
30 import edu.umd.cs.findbugs.annotations.NonNull;
31 import edu.umd.cs.findbugs.annotations.Nullable;
32 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
33
34 public class FlagContainerSupport implements IContainerFlagSupport<IBoundInstanceFlag> {
35 @NonNull
36 private final Map<QName, IBoundInstanceFlag> flagInstances;
37 @Nullable
38 private IBoundInstanceFlag jsonKeyFlag;
39
40 @SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
41 @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
42 public FlagContainerSupport(
43 @NonNull IBoundDefinitionModelComplex definition,
44 @Nullable Consumer<IBoundInstanceFlag> peeker) {
45 Class<?> clazz = definition.getBoundClass();
46
47 Stream<IBoundInstanceFlag> instances = getFlagInstanceFields(clazz).stream()
48 .flatMap(field -> {
49 Stream<IBoundInstanceFlag> stream;
50 if (field.isAnnotationPresent(BoundFlag.class)) {
51 stream = Stream.of(IBoundInstanceFlag.newInstance(field, definition));
52 } else {
53 stream = Stream.empty();
54 }
55 return stream;
56 });
57
58 Consumer<IBoundInstanceFlag> intermediate = this::handleFlagInstance;
59
60 if (peeker != null) {
61 intermediate = intermediate.andThen(peeker);
62 }
63
64 this.flagInstances = CollectionUtil.unmodifiableMap(ObjectUtils.notNull(instances
65 .peek(intermediate)
66 .collect(Collectors.toMap(
67 IBoundInstanceFlag::getXmlQName,
68 Function.identity(),
69 (v1, v2) -> v2,
70 LinkedHashMap::new))));
71 }
72
73
74
75
76
77
78
79
80 @SuppressWarnings("PMD.UseArraysAsList")
81 @NonNull
82 protected static Collection<Field> getFlagInstanceFields(Class<?> clazz) {
83 Field[] fields = clazz.getDeclaredFields();
84
85 List<Field> retval = new LinkedList<>();
86
87 Class<?> superClass = clazz.getSuperclass();
88 if (superClass != null) {
89
90 retval.addAll(getFlagInstanceFields(superClass));
91 }
92
93 for (Field field : fields) {
94 if (!field.isAnnotationPresent(BoundFlag.class) || field.isAnnotationPresent(Ignore.class)) {
95
96 continue;
97 }
98
99 retval.add(field);
100 }
101 return ObjectUtils.notNull(Collections.unmodifiableCollection(retval));
102 }
103
104
105
106
107
108
109
110 protected void handleFlagInstance(IBoundInstanceFlag instance) {
111 if (instance.isJsonKey()) {
112 this.jsonKeyFlag = instance;
113 }
114 }
115
116 @Override
117 @NonNull
118 public Map<QName, IBoundInstanceFlag> getFlagInstanceMap() {
119 return flagInstances;
120 }
121
122 @Override
123 public IBoundInstanceFlag getJsonKeyFlagInstance() {
124 return jsonKeyFlag;
125 }
126 }