1
2
3
4
5
6 package gov.nist.secauto.metaschema.databind;
7
8 import gov.nist.secauto.metaschema.core.datatype.DataTypeService;
9 import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
10 import gov.nist.secauto.metaschema.core.model.IBoundObject;
11 import gov.nist.secauto.metaschema.core.model.IModule;
12 import gov.nist.secauto.metaschema.core.model.IModuleLoader;
13 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
14 import gov.nist.secauto.metaschema.databind.codegen.IProduction;
15 import gov.nist.secauto.metaschema.databind.codegen.ModuleCompilerHelper;
16 import gov.nist.secauto.metaschema.databind.io.BindingException;
17 import gov.nist.secauto.metaschema.databind.io.DefaultBoundLoader;
18 import gov.nist.secauto.metaschema.databind.io.Format;
19 import gov.nist.secauto.metaschema.databind.io.IDeserializer;
20 import gov.nist.secauto.metaschema.databind.io.ISerializer;
21 import gov.nist.secauto.metaschema.databind.io.json.DefaultJsonDeserializer;
22 import gov.nist.secauto.metaschema.databind.io.json.DefaultJsonSerializer;
23 import gov.nist.secauto.metaschema.databind.io.xml.DefaultXmlDeserializer;
24 import gov.nist.secauto.metaschema.databind.io.xml.DefaultXmlSerializer;
25 import gov.nist.secauto.metaschema.databind.io.yaml.DefaultYamlDeserializer;
26 import gov.nist.secauto.metaschema.databind.io.yaml.DefaultYamlSerializer;
27 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelAssembly;
28 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelComplex;
29 import gov.nist.secauto.metaschema.databind.model.IBoundModule;
30 import gov.nist.secauto.metaschema.databind.model.binding.metaschema.METASCHEMA;
31
32 import java.io.IOException;
33 import java.nio.file.Files;
34 import java.nio.file.Path;
35 import java.util.Collection;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Objects;
39 import java.util.concurrent.ConcurrentHashMap;
40
41 import javax.xml.namespace.QName;
42
43 import edu.umd.cs.findbugs.annotations.NonNull;
44 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class DefaultBindingContext implements IBindingContext {
61 private static DefaultBindingContext singleton;
62 @NonNull
63 private final IModuleLoaderStrategy moduleLoaderStrategy;
64 @NonNull
65 private final Map<Class<?>, IBoundDefinitionModelComplex> boundClassToStrategyMap = new ConcurrentHashMap<>();
66 @NonNull
67 private final Map<IBoundDefinitionModelAssembly, IBindingMatcher> bindingMatchers = new ConcurrentHashMap<>();
68
69
70
71
72
73
74 @NonNull
75 public static DefaultBindingContext instance() {
76 synchronized (DefaultBindingContext.class) {
77 if (singleton == null) {
78 singleton = new DefaultBindingContext();
79 }
80 }
81 return ObjectUtils.notNull(singleton);
82 }
83
84
85
86
87
88
89
90 @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
91 public DefaultBindingContext(@NonNull List<IModuleLoader.IModulePostProcessor> modulePostProcessors) {
92
93 moduleLoaderStrategy = new PostProcessingModuleLoaderStrategy(this, modulePostProcessors);
94 registerBindingMatcher(METASCHEMA.class);
95 }
96
97
98
99
100 @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
101 public DefaultBindingContext() {
102
103 moduleLoaderStrategy = new SimpleModuleLoaderStrategy(this);
104 registerBindingMatcher(METASCHEMA.class);
105 }
106
107 @NonNull
108 protected IModuleLoaderStrategy getModuleLoaderStrategy() {
109 return moduleLoaderStrategy;
110 }
111
112
113
114
115
116
117
118
119 @NonNull
120 protected Collection<IBindingMatcher> getBindingMatchers() {
121 return ObjectUtils.notNull(bindingMatchers.values());
122 }
123
124 @Override
125 @NonNull
126 public final IBindingMatcher registerBindingMatcher(@NonNull IBoundDefinitionModelAssembly definition) {
127 return ObjectUtils.notNull(bindingMatchers.computeIfAbsent(definition, key -> IBindingMatcher.of(definition)));
128 }
129
130 @Override
131 public final IBindingMatcher registerBindingMatcher(@NonNull Class<? extends IBoundObject> clazz) {
132 IBoundDefinitionModelComplex definition = getBoundDefinitionForClass(clazz);
133 if (definition == null) {
134 throw new IllegalArgumentException(String.format("Unable to find bound definition for class '%s'.",
135 clazz.getName()));
136 }
137
138 try {
139 IBoundDefinitionModelAssembly assemblyDefinition = IBoundDefinitionModelAssembly.class.cast(definition);
140 return registerBindingMatcher(ObjectUtils.notNull(assemblyDefinition));
141 } catch (ClassCastException ex) {
142 throw new IllegalArgumentException(
143 String.format("The provided class '%s' is not a root assembly.", clazz.getName()), ex);
144 }
145 }
146
147 @Override
148 public final IBoundDefinitionModelComplex registerClassBinding(IBoundDefinitionModelComplex definition) {
149 Class<?> clazz = definition.getBoundClass();
150 return boundClassToStrategyMap.computeIfAbsent(clazz, k -> definition);
151 }
152
153 @Override
154 public final IBoundDefinitionModelComplex getBoundDefinitionForClass(@NonNull Class<? extends IBoundObject> clazz) {
155 return moduleLoaderStrategy.getBoundDefinitionForClass(clazz);
156 }
157
158 @Override
159 public <TYPE extends IDataTypeAdapter<?>> TYPE getJavaTypeAdapterInstance(@NonNull Class<TYPE> clazz) {
160 return DataTypeService.getInstance().getJavaTypeAdapterByClass(clazz);
161 }
162
163
164
165
166
167
168 @Override
169 public <CLASS extends IBoundObject> ISerializer<CLASS> newSerializer(
170 @NonNull Format format,
171 @NonNull Class<CLASS> clazz) {
172 Objects.requireNonNull(format, "format");
173 IBoundDefinitionModelAssembly definition;
174 try {
175 definition = IBoundDefinitionModelAssembly.class.cast(getBoundDefinitionForClass(clazz));
176 } catch (ClassCastException ex) {
177 throw new IllegalStateException(
178 String.format("Class '%s' is not a bound assembly.", clazz.getClass().getName()), ex);
179 }
180 if (definition == null) {
181 throw new IllegalStateException(String.format("Class '%s' is not bound", clazz.getClass().getName()));
182 }
183 ISerializer<CLASS> retval;
184 switch (format) {
185 case JSON:
186 retval = new DefaultJsonSerializer<>(definition);
187 break;
188 case XML:
189 retval = new DefaultXmlSerializer<>(definition);
190 break;
191 case YAML:
192 retval = new DefaultYamlSerializer<>(definition);
193 break;
194 default:
195 throw new UnsupportedOperationException(String.format("Unsupported format '%s'", format));
196 }
197 return retval;
198 }
199
200
201
202
203
204
205 @Override
206 public <CLASS extends IBoundObject> IDeserializer<CLASS> newDeserializer(
207 @NonNull Format format,
208 @NonNull Class<CLASS> clazz) {
209 IBoundDefinitionModelAssembly definition;
210 try {
211 definition = IBoundDefinitionModelAssembly.class.cast(getBoundDefinitionForClass(clazz));
212 } catch (ClassCastException ex) {
213 throw new IllegalStateException(
214 String.format("Class '%s' is not a bound assembly.", clazz.getClass().getName()),
215 ex);
216 }
217 if (definition == null) {
218 throw new IllegalStateException(String.format("Class '%s' is not bound", clazz.getName()));
219 }
220 IDeserializer<CLASS> retval;
221 switch (format) {
222 case JSON:
223 retval = new DefaultJsonDeserializer<>(definition);
224 break;
225 case XML:
226 retval = new DefaultXmlDeserializer<>(definition);
227 break;
228 case YAML:
229 retval = new DefaultYamlDeserializer<>(definition);
230 break;
231 default:
232 throw new UnsupportedOperationException(String.format("Unsupported format '%s'", format));
233 }
234
235 return retval;
236 }
237
238 @Override
239 @SuppressWarnings({ "PMD.UseProperClassLoader", "unchecked" })
240 @NonNull
241 public IBindingContext registerModule(
242 @NonNull IModule module,
243 @NonNull Path compilePath) throws IOException {
244 if (!(module instanceof IBoundModule)) {
245 Files.createDirectories(compilePath);
246
247 ClassLoader classLoader = ModuleCompilerHelper.newClassLoader(
248 compilePath,
249 ObjectUtils.notNull(Thread.currentThread().getContextClassLoader()));
250
251 IProduction production = ModuleCompilerHelper.compileMetaschema(module, compilePath);
252 production.getModuleProductions().stream()
253 .map(item -> {
254 try {
255 return (Class<? extends IBoundModule>) classLoader.loadClass(item.getClassName().reflectionName());
256 } catch (ClassNotFoundException ex) {
257 throw new IllegalStateException(ex);
258 }
259 })
260 .forEachOrdered(clazz -> {
261 IBoundModule boundModule = registerModule(ObjectUtils.notNull(clazz));
262
263 boundModule.getRootAssemblyDefinitions();
264 });
265 }
266 return this;
267 }
268
269 @Override
270 public IBoundModule registerModule(Class<? extends IBoundModule> clazz) {
271 return getModuleLoaderStrategy().loadModule(clazz);
272
273
274
275
276
277 }
278
279 @Override
280 public Class<? extends IBoundObject> getBoundClassForRootXmlQName(@NonNull QName rootQName) {
281 Class<? extends IBoundObject> retval = null;
282 for (IBindingMatcher matcher : getBindingMatchers()) {
283 retval = matcher.getBoundClassForXmlQName(rootQName);
284 if (retval != null) {
285 break;
286 }
287 }
288 return retval;
289 }
290
291 @Override
292 public Class<? extends IBoundObject> getBoundClassForRootJsonName(@NonNull String rootName) {
293 Class<? extends IBoundObject> retval = null;
294 for (IBindingMatcher matcher : getBindingMatchers()) {
295 retval = matcher.getBoundClassForJsonName(rootName);
296 if (retval != null) {
297 break;
298 }
299 }
300 return retval;
301 }
302
303 @Override
304 public DefaultBoundLoader newBoundLoader() {
305 return new DefaultBoundLoader(this);
306 }
307
308 @Override
309 public <CLASS extends IBoundObject> CLASS deepCopy(@NonNull CLASS other, IBoundObject parentInstance)
310 throws BindingException {
311 IBoundDefinitionModelComplex definition = getBoundDefinitionForClass(other.getClass());
312 if (definition == null) {
313 throw new IllegalStateException(String.format("Class '%s' is not bound", other.getClass().getName()));
314 }
315 return ObjectUtils.asType(definition.deepCopyItem(other, parentInstance));
316 }
317 }