1
2
3
4
5
6 package gov.nist.secauto.metaschema.core.model;
7
8 import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
9 import gov.nist.secauto.metaschema.core.util.CollectionUtil;
10 import gov.nist.secauto.metaschema.core.util.CustomCollectors;
11 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
12
13 import org.apache.logging.log4j.LogManager;
14 import org.apache.logging.log4j.Logger;
15
16 import java.util.Collection;
17 import java.util.List;
18 import java.util.Locale;
19 import java.util.Map;
20 import java.util.function.Function;
21 import java.util.function.Predicate;
22 import java.util.stream.Collectors;
23 import java.util.stream.Stream;
24
25 import edu.umd.cs.findbugs.annotations.NonNull;
26 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
27 import nl.talsmasoftware.lazy4j.Lazy;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 @SuppressWarnings("PMD.CouplingBetweenObjects")
44 public abstract class AbstractModule<
45 M extends IModuleExtended<M, D, FL, FI, A>,
46 D extends IModelDefinition,
47 FL extends IFlagDefinition,
48 FI extends IFieldDefinition,
49 A extends IAssemblyDefinition>
50 implements IModuleExtended<M, D, FL, FI, A> {
51 private static final Logger LOGGER = LogManager.getLogger(AbstractModule.class);
52
53 @NonNull
54 private final List<? extends M> importedModules;
55 @NonNull
56 private final Lazy<Exports> exports;
57 @NonNull
58 private final Lazy<IEnhancedQName> qname;
59
60
61
62
63
64
65
66
67 public AbstractModule(@NonNull List<? extends M> importedModules) {
68 this.importedModules
69 = CollectionUtil.unmodifiableList(ObjectUtils.requireNonNull(importedModules, "importedModules"));
70 this.exports = ObjectUtils.notNull(Lazy.of(() -> new Exports(importedModules)));
71 this.qname = ObjectUtils.notNull(Lazy.of(() -> IEnhancedQName.of(getXmlNamespace(), getShortName())));
72 }
73
74 @Override
75 public IEnhancedQName getQName() {
76 return ObjectUtils.notNull(qname.get());
77 }
78
79 @Override
80 @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "interface doesn't allow modification")
81 public List<? extends M> getImportedModules() {
82 return importedModules;
83 }
84
85 @SuppressWarnings("null")
86 @NonNull
87 private Exports getExports() {
88 return exports.get();
89 }
90
91 private Map<String, ? extends M> getImportedModulesByShortName() {
92 return importedModules.stream().collect(Collectors.toMap(IModule::getShortName, Function.identity()));
93 }
94
95 @Override
96 public M getImportedModuleByShortName(String name) {
97 return getImportedModulesByShortName().get(name);
98 }
99
100 @SuppressWarnings("null")
101 @Override
102 public Collection<FL> getExportedFlagDefinitions() {
103 return getExports().getExportedFlagDefinitionMap().values();
104 }
105
106 @Override
107 public FL getExportedFlagDefinitionByName(IEnhancedQName name) {
108 return getExports().getExportedFlagDefinitionMap().get(name);
109 }
110
111 @SuppressWarnings("null")
112 @Override
113 public Collection<FI> getExportedFieldDefinitions() {
114 return getExports().getExportedFieldDefinitionMap().values();
115 }
116
117 @Override
118 public FI getExportedFieldDefinitionByName(Integer name) {
119 return getExports().getExportedFieldDefinitionMap().get(name);
120 }
121
122 @SuppressWarnings("null")
123 @Override
124 public Collection<A> getExportedAssemblyDefinitions() {
125 return getExports().getExportedAssemblyDefinitionMap().values();
126 }
127
128 @Override
129 public A getExportedAssemblyDefinitionByName(Integer name) {
130 return getExports().getExportedAssemblyDefinitionMap().get(name);
131 }
132
133 @Override
134 public A getExportedRootAssemblyDefinitionByName(Integer name) {
135 return getExports().getExportedRootAssemblyDefinitionMap().get(name);
136 }
137
138 @SuppressWarnings({ "unused", "PMD.UnusedPrivateMethod" })
139 private static <DEF extends IDefinition> DEF handleShadowedDefinitions(
140 @NonNull IEnhancedQName key,
141 @NonNull DEF oldDef,
142 @NonNull DEF newDef) {
143 if (!oldDef.equals(newDef) && LOGGER.isWarnEnabled()) {
144 LOGGER.warn("The {} '{}' from metaschema '{}' is shadowing '{}' from metaschema '{}'",
145 newDef.getModelType().name().toLowerCase(Locale.ROOT),
146 newDef.getName(),
147 newDef.getContainingModule().getShortName(),
148 oldDef.getName(),
149 oldDef.getContainingModule().getShortName());
150 }
151 return newDef;
152 }
153
154 @SuppressWarnings({ "unused", "PMD.UnusedPrivateMethod" })
155 private static <DEF extends IDefinition> DEF handleShadowedDefinitions(
156 @NonNull Integer key,
157 @NonNull DEF oldDef,
158 @NonNull DEF newDef) {
159 if (!oldDef.equals(newDef) && LOGGER.isWarnEnabled()) {
160 LOGGER.warn("The {} '{}' from metaschema '{}' is shadowing '{}' from metaschema '{}'",
161 newDef.getModelType().name().toLowerCase(Locale.ROOT),
162 newDef.getName(),
163 newDef.getContainingModule().getShortName(),
164 oldDef.getName(),
165 oldDef.getContainingModule().getShortName());
166 }
167 return newDef;
168 }
169
170 private class Exports {
171 @NonNull
172 private final Map<IEnhancedQName, FL> exportedFlagDefinitions;
173 @NonNull
174 private final Map<Integer, FI> exportedFieldDefinitions;
175 @NonNull
176 private final Map<Integer, A> exportedAssemblyDefinitions;
177 @NonNull
178 private final Map<Integer, A> exportedRootAssemblyDefinitions;
179
180 @SuppressWarnings({ "PMD.ConstructorCallsOverridableMethod", "synthetic-access" })
181 public Exports(@NonNull List<? extends M> importedModules) {
182
183 Predicate<IDefinition> filter = IModuleExtended.allNonLocalDefinitions();
184 Stream<FL> flags = getFlagDefinitions().stream()
185 .filter(filter);
186 Stream<FI> fields = getFieldDefinitions().stream()
187 .filter(filter);
188 Stream<A> assemblies = getAssemblyDefinitions().stream()
189 .filter(filter);
190
191
192 if (!importedModules.isEmpty()) {
193 Stream<FL> importedFlags = Stream.empty();
194 Stream<FI> importedFields = Stream.empty();
195 Stream<A> importedAssemblies = Stream.empty();
196
197 for (M module : importedModules) {
198 importedFlags = Stream.concat(importedFlags, module.getExportedFlagDefinitions().stream());
199 importedFields = Stream.concat(importedFields, module.getExportedFieldDefinitions().stream());
200 importedAssemblies
201 = Stream.concat(importedAssemblies, module.getExportedAssemblyDefinitions().stream());
202 }
203
204 flags = Stream.concat(importedFlags, flags);
205 fields = Stream.concat(importedFields, fields);
206 assemblies = Stream.concat(importedAssemblies, assemblies);
207 }
208
209
210
211
212 Map<IEnhancedQName, FL> exportedFlagDefinitions = flags.collect(
213 CustomCollectors.toMap(
214 IFlagDefinition::getDefinitionQName,
215 CustomCollectors.identity(),
216 AbstractModule::handleShadowedDefinitions));
217 Map<Integer, FI> exportedFieldDefinitions = fields.collect(
218 CustomCollectors.toMap(
219 def -> def.getDefinitionQName().getIndexPosition(),
220 CustomCollectors.identity(),
221 AbstractModule::handleShadowedDefinitions));
222 Map<Integer, A> exportedAssemblyDefinitions = assemblies.collect(
223 CustomCollectors.toMap(
224 def -> def.getDefinitionQName().getIndexPosition(),
225 CustomCollectors.identity(),
226 AbstractModule::handleShadowedDefinitions));
227
228 this.exportedFlagDefinitions = exportedFlagDefinitions.isEmpty()
229 ? CollectionUtil.emptyMap()
230 : CollectionUtil.unmodifiableMap(exportedFlagDefinitions);
231 this.exportedFieldDefinitions = exportedFieldDefinitions.isEmpty()
232 ? CollectionUtil.emptyMap()
233 : CollectionUtil.unmodifiableMap(exportedFieldDefinitions);
234 this.exportedAssemblyDefinitions = exportedAssemblyDefinitions.isEmpty()
235 ? CollectionUtil.emptyMap()
236 : CollectionUtil.unmodifiableMap(exportedAssemblyDefinitions);
237 this.exportedRootAssemblyDefinitions = exportedAssemblyDefinitions.isEmpty()
238 ? CollectionUtil.emptyMap()
239 : CollectionUtil.unmodifiableMap(ObjectUtils.notNull(exportedAssemblyDefinitions.values().stream()
240 .filter(IAssemblyDefinition::isRoot)
241 .collect(CustomCollectors.toMap(
242 def -> def.getRootQName().getIndexPosition(),
243 CustomCollectors.identity(),
244 AbstractModule::handleShadowedDefinitions))));
245 }
246
247 @NonNull
248 public Map<IEnhancedQName, FL> getExportedFlagDefinitionMap() {
249 return this.exportedFlagDefinitions;
250 }
251
252 @NonNull
253 public Map<Integer, FI> getExportedFieldDefinitionMap() {
254 return this.exportedFieldDefinitions;
255 }
256
257 @NonNull
258 public Map<Integer, A> getExportedAssemblyDefinitionMap() {
259 return this.exportedAssemblyDefinitions;
260 }
261
262 @NonNull
263 public Map<Integer, A> getExportedRootAssemblyDefinitionMap() {
264 return this.exportedRootAssemblyDefinitions;
265 }
266 }
267 }