1
2
3
4
5
6 package gov.nist.secauto.metaschema.databind.model;
7
8 import gov.nist.secauto.metaschema.core.metapath.StaticContext;
9 import gov.nist.secauto.metaschema.core.model.AbstractModule;
10 import gov.nist.secauto.metaschema.core.model.IBoundObject;
11 import gov.nist.secauto.metaschema.core.util.CollectionUtil;
12 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
13 import gov.nist.secauto.metaschema.databind.IBindingContext;
14 import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaModule;
15 import gov.nist.secauto.metaschema.databind.model.annotations.NsBinding;
16
17 import java.lang.reflect.Array;
18 import java.lang.reflect.Constructor;
19 import java.lang.reflect.InvocationTargetException;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.LinkedHashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.function.Function;
28 import java.util.stream.Collectors;
29
30 import javax.xml.namespace.QName;
31
32 import edu.umd.cs.findbugs.annotations.NonNull;
33 import nl.talsmasoftware.lazy4j.Lazy;
34
35 public abstract class AbstractBoundModule
36 extends AbstractModule<
37 IBoundModule,
38 IBoundDefinitionModelComplex,
39 IBoundDefinitionFlag,
40 IBoundDefinitionModelField<?>,
41 IBoundDefinitionModelAssembly>
42 implements IBoundModule {
43 @NonNull
44 private final IBindingContext bindingContext;
45 @NonNull
46 private final Lazy<Map<QName, IBoundDefinitionModelAssembly>> assemblyDefinitions;
47 @NonNull
48 private final Lazy<Map<QName, IBoundDefinitionModelField<?>>> fieldDefinitions;
49 @NonNull
50 private final Lazy<StaticContext> staticContext;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 @NonNull
66 public static IBoundModule createInstance(
67 @NonNull Class<? extends IBoundModule> clazz,
68 @NonNull IBindingContext bindingContext) {
69
70 if (!clazz.isAnnotationPresent(MetaschemaModule.class)) {
71 throw new IllegalStateException(String.format("The class '%s' is missing the '%s' annotation",
72 clazz.getCanonicalName(), MetaschemaModule.class.getCanonicalName()));
73 }
74
75 MetaschemaModule moduleAnnotation = clazz.getAnnotation(MetaschemaModule.class);
76
77 List<IBoundModule> importedModules;
78 if (moduleAnnotation.imports().length > 0) {
79 importedModules = new ArrayList<>(moduleAnnotation.imports().length);
80 for (Class<? extends IBoundModule> importClass : moduleAnnotation.imports()) {
81 assert importClass != null;
82 IBoundModule moduleImport = bindingContext.registerModule(importClass);
83 importedModules.add(moduleImport);
84 }
85 } else {
86 importedModules = CollectionUtil.emptyList();
87 }
88 return createInstance(clazz, bindingContext, importedModules);
89 }
90
91 @NonNull
92 private static IBoundModule createInstance(
93 @NonNull Class<? extends IBoundModule> clazz,
94 @NonNull IBindingContext bindingContext,
95 @NonNull List<? extends IBoundModule> importedModules) {
96
97 Constructor<? extends IBoundModule> constructor;
98 try {
99 constructor = clazz.getDeclaredConstructor(List.class, IBindingContext.class);
100 } catch (NoSuchMethodException ex) {
101 throw new IllegalArgumentException(ex);
102 }
103
104 try {
105 return ObjectUtils.notNull(constructor.newInstance(importedModules, bindingContext));
106 } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
107 throw new IllegalArgumentException(ex);
108 }
109 }
110
111
112
113
114
115
116
117
118
119 protected AbstractBoundModule(
120 @NonNull List<? extends IBoundModule> importedModules,
121 @NonNull IBindingContext bindingContext) {
122 super(importedModules);
123 this.bindingContext = bindingContext;
124 this.assemblyDefinitions = ObjectUtils.notNull(Lazy.lazy(() -> Arrays.stream(getAssemblyClasses())
125 .map(clazz -> {
126 assert clazz != null;
127 return (IBoundDefinitionModelAssembly) ObjectUtils
128 .requireNonNull(bindingContext.getBoundDefinitionForClass(clazz));
129 })
130 .collect(Collectors.toUnmodifiableMap(
131 IBoundDefinitionModelAssembly::getDefinitionQName,
132 Function.identity()))));
133 this.fieldDefinitions = ObjectUtils.notNull(Lazy.lazy(() -> Arrays.stream(getFieldClasses())
134 .map(clazz -> {
135 assert clazz != null;
136 return (IBoundDefinitionModelField<?>) ObjectUtils
137 .requireNonNull(bindingContext.getBoundDefinitionForClass(clazz));
138 })
139 .collect(Collectors.toUnmodifiableMap(
140 IBoundDefinitionModelField::getDefinitionQName,
141 Function.identity()))));
142 this.staticContext = ObjectUtils.notNull(Lazy.lazy(() -> {
143 StaticContext.Builder builder = StaticContext.builder()
144 .defaultModelNamespace(getXmlNamespace());
145
146 getNamespaceBindings()
147 .forEach(
148 (prefix, ns) -> builder.namespace(
149 ObjectUtils.requireNonNull(prefix),
150 ObjectUtils.requireNonNull(ns)));
151 return builder.build();
152 }));
153 }
154
155 @Override
156 public StaticContext getModuleStaticContext() {
157 return ObjectUtils.notNull(staticContext.get());
158 }
159
160 @Override
161 @NonNull
162 public IBindingContext getBindingContext() {
163 return bindingContext;
164 }
165
166 @Override
167 public Map<String, String> getNamespaceBindings() {
168 return ObjectUtils.notNull(Arrays.stream(getNsBindings())
169 .collect(Collectors.toMap(
170 NsBinding::prefix,
171 NsBinding::uri,
172 (v1, v2) -> v2,
173 LinkedHashMap::new)));
174 }
175
176 @SuppressWarnings({ "null" })
177 @NonNull
178 protected NsBinding[] getNsBindings() {
179 return getClass().isAnnotationPresent(MetaschemaModule.class)
180 ? getClass().getAnnotation(MetaschemaModule.class).nsBindings()
181 : (NsBinding[]) Array.newInstance(NsBinding.class, 0);
182 }
183
184
185
186
187
188
189
190 @SuppressWarnings({ "null", "unchecked" })
191 @NonNull
192 protected Class<? extends IBoundObject>[] getAssemblyClasses() {
193 return getClass().isAnnotationPresent(MetaschemaModule.class)
194 ? getClass().getAnnotation(MetaschemaModule.class).assemblies()
195 : (Class<? extends IBoundObject>[]) Array.newInstance(Class.class, 0);
196 }
197
198
199
200
201
202
203 @SuppressWarnings({ "null", "unchecked" })
204 @NonNull
205 protected Class<? extends IBoundObject>[] getFieldClasses() {
206 return getClass().isAnnotationPresent(MetaschemaModule.class)
207 ? getClass().getAnnotation(MetaschemaModule.class).fields()
208 : (Class<? extends IBoundObject>[]) Array.newInstance(Class.class, 0);
209 }
210
211
212
213
214
215
216 protected Map<QName, IBoundDefinitionModelAssembly> getAssemblyDefinitionMap() {
217 return assemblyDefinitions.get();
218 }
219
220 @SuppressWarnings("null")
221 @Override
222 public Collection<IBoundDefinitionModelAssembly> getAssemblyDefinitions() {
223 return getAssemblyDefinitionMap().values();
224 }
225
226 @Override
227 public IBoundDefinitionModelAssembly getAssemblyDefinitionByName(@NonNull QName name) {
228 return getAssemblyDefinitionMap().get(name);
229 }
230
231
232
233
234
235
236 protected Map<QName, IBoundDefinitionModelField<?>> getFieldDefinitionMap() {
237 return fieldDefinitions.get();
238 }
239
240 @SuppressWarnings("null")
241 @Override
242 public Collection<IBoundDefinitionModelField<?>> getFieldDefinitions() {
243 return getFieldDefinitionMap().values();
244 }
245
246 @Override
247 public IBoundDefinitionModelField<?> getFieldDefinitionByName(@NonNull QName name) {
248 return getFieldDefinitionMap().get(name);
249 }
250
251 @SuppressWarnings("null")
252 @Override
253 public Collection<IBoundDefinitionFlag> getFlagDefinitions() {
254
255 return Collections.emptyList();
256 }
257
258 @Override
259 public IBoundDefinitionFlag getFlagDefinitionByName(@NonNull QName name) {
260
261 return null;
262 }
263 }