1
2
3
4
5
6 package gov.nist.secauto.metaschema.databind;
7
8 import gov.nist.secauto.metaschema.core.model.IBoundObject;
9 import gov.nist.secauto.metaschema.core.model.MetaschemaException;
10 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
11 import gov.nist.secauto.metaschema.databind.io.BindingException;
12 import gov.nist.secauto.metaschema.databind.io.Format;
13 import gov.nist.secauto.metaschema.databind.io.IDeserializer;
14 import gov.nist.secauto.metaschema.databind.io.ISerializer;
15 import gov.nist.secauto.metaschema.databind.io.json.DefaultJsonDeserializer;
16 import gov.nist.secauto.metaschema.databind.io.json.DefaultJsonSerializer;
17 import gov.nist.secauto.metaschema.databind.io.xml.DefaultXmlDeserializer;
18 import gov.nist.secauto.metaschema.databind.io.xml.DefaultXmlSerializer;
19 import gov.nist.secauto.metaschema.databind.io.yaml.DefaultYamlDeserializer;
20 import gov.nist.secauto.metaschema.databind.io.yaml.DefaultYamlSerializer;
21 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelAssembly;
22 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelComplex;
23 import gov.nist.secauto.metaschema.databind.model.IBoundModule;
24 import gov.nist.secauto.metaschema.databind.model.metaschema.BindingModuleLoader;
25 import gov.nist.secauto.metaschema.databind.model.metaschema.IBindingMetaschemaModule;
26 import gov.nist.secauto.metaschema.databind.model.metaschema.IBindingModuleLoader;
27 import gov.nist.secauto.metaschema.databind.model.metaschema.ModuleLoadingPostProcessor;
28 import gov.nist.secauto.metaschema.databind.model.metaschema.binding.METASCHEMA;
29 import gov.nist.secauto.metaschema.databind.model.metaschema.binding.MetaschemaModelModule;
30
31 import java.io.IOException;
32 import java.net.URI;
33 import java.net.URL;
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 import nl.talsmasoftware.lazy4j.Lazy;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 @SuppressWarnings("PMD.CouplingBetweenObjects")
62 public class DefaultBindingContext implements IBindingContext {
63 private static Lazy<DefaultBindingContext> singleton = Lazy.of(DefaultBindingContext::new);
64 @NonNull
65 private final IModuleLoaderStrategy moduleLoaderStrategy;
66 @NonNull
67 private final Map<Class<?>, IBoundDefinitionModelComplex> boundClassToStrategyMap = new ConcurrentHashMap<>();
68
69
70
71
72
73
74
75
76
77
78 @NonNull
79 static DefaultBindingContext instance() {
80 return ObjectUtils.notNull(singleton.get());
81 }
82
83
84
85
86 @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
87 public DefaultBindingContext() {
88 this(new SimpleModuleLoaderStrategy());
89 }
90
91
92
93
94
95
96
97
98 public DefaultBindingContext(@NonNull IBindingContext.IModuleLoaderStrategy strategy) {
99
100 moduleLoaderStrategy = strategy;
101 try {
102 registerModule(MetaschemaModelModule.class);
103 } catch (MetaschemaException ex) {
104 throw new IllegalStateException("Unable to register the builtin Metaschema module.", ex);
105 }
106 }
107
108 @Override
109 @NonNull
110 public final IModuleLoaderStrategy getModuleLoaderStrategy() {
111 return moduleLoaderStrategy;
112 }
113
114 @Override
115 public IBindingModuleLoader newModuleLoader() {
116 return new ModuleLoader(this, getModuleLoaderStrategy());
117 }
118
119 @Override
120 @NonNull
121 public final IBoundModule registerModule(@NonNull Class<? extends IBoundModule> clazz) throws MetaschemaException {
122 IModuleLoaderStrategy strategy = getModuleLoaderStrategy();
123 IBoundModule module = strategy.loadModule(clazz, this);
124 registerImportedModules(module);
125 return strategy.registerModule(module, this);
126 }
127
128 private void registerImportedModules(@NonNull IBoundModule module) throws MetaschemaException {
129 IModuleLoaderStrategy strategy = getModuleLoaderStrategy();
130 for (IBoundModule parentModule : module.getImportedModules()) {
131 assert parentModule != null;
132 registerImportedModules(parentModule);
133 strategy.registerModule(parentModule, this);
134 }
135 }
136
137
138
139
140
141
142 @NonNull
143 protected Collection<IBindingMatcher> getBindingMatchers() {
144 return getModuleLoaderStrategy().getBindingMatchers();
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, this);
156 }
157
158
159
160
161
162
163 @Override
164 public <CLASS extends IBoundObject> ISerializer<CLASS> newSerializer(
165 @NonNull Format format,
166 @NonNull Class<CLASS> clazz) {
167 Objects.requireNonNull(format, "format");
168 IBoundDefinitionModelAssembly definition;
169 try {
170 definition = IBoundDefinitionModelAssembly.class.cast(getBoundDefinitionForClass(clazz));
171 } catch (ClassCastException ex) {
172 throw new IllegalStateException(
173 String.format("Class '%s' is not a bound assembly.", clazz.getClass().getName()), ex);
174 }
175 if (definition == null) {
176 throw new IllegalStateException(String.format("Class '%s' is not bound", clazz.getClass().getName()));
177 }
178 ISerializer<CLASS> retval;
179 switch (format) {
180 case JSON:
181 retval = new DefaultJsonSerializer<>(definition);
182 break;
183 case XML:
184 retval = new DefaultXmlSerializer<>(definition);
185 break;
186 case YAML:
187 retval = new DefaultYamlSerializer<>(definition);
188 break;
189 default:
190 throw new UnsupportedOperationException(String.format("Unsupported format '%s'", format));
191 }
192 return retval;
193 }
194
195
196
197
198
199
200 @Override
201 public <CLASS extends IBoundObject> IDeserializer<CLASS> newDeserializer(
202 @NonNull Format format,
203 @NonNull Class<CLASS> clazz) {
204 IBoundDefinitionModelAssembly definition;
205 try {
206 definition = IBoundDefinitionModelAssembly.class.cast(getBoundDefinitionForClass(clazz));
207 } catch (ClassCastException ex) {
208 throw new IllegalStateException(
209 String.format("Class '%s' is not a bound assembly.", clazz.getClass().getName()),
210 ex);
211 }
212 if (definition == null) {
213 throw new IllegalStateException(String.format("Class '%s' is not bound", clazz.getName()));
214 }
215 IDeserializer<CLASS> retval;
216 switch (format) {
217 case JSON:
218 retval = new DefaultJsonDeserializer<>(definition);
219 break;
220 case XML:
221 retval = new DefaultXmlDeserializer<>(definition);
222 break;
223 case YAML:
224 retval = new DefaultYamlDeserializer<>(definition);
225 break;
226 default:
227 throw new UnsupportedOperationException(String.format("Unsupported format '%s'", format));
228 }
229
230 return retval;
231 }
232
233 @Override
234 public Class<? extends IBoundObject> getBoundClassForRootXmlQName(@NonNull QName rootQName) {
235 Class<? extends IBoundObject> retval = null;
236 for (IBindingMatcher matcher : getBindingMatchers()) {
237 retval = matcher.getBoundClassForXmlQName(rootQName);
238 if (retval != null) {
239 break;
240 }
241 }
242 return retval;
243 }
244
245 @Override
246 public Class<? extends IBoundObject> getBoundClassForRootJsonName(@NonNull String rootName) {
247 Class<? extends IBoundObject> retval = null;
248 for (IBindingMatcher matcher : getBindingMatchers()) {
249 retval = matcher.getBoundClassForJsonName(rootName);
250 if (retval != null) {
251 break;
252 }
253 }
254 return retval;
255 }
256
257 @Override
258 public <CLASS extends IBoundObject> CLASS deepCopy(@NonNull CLASS other, IBoundObject parentInstance)
259 throws BindingException {
260 IBoundDefinitionModelComplex definition = getBoundDefinitionForClass(other.getClass());
261 if (definition == null) {
262 throw new IllegalStateException(String.format("Class '%s' is not bound", other.getClass().getName()));
263 }
264 return ObjectUtils.asType(definition.deepCopyItem(other, parentInstance));
265 }
266
267
268
269
270
271 @Override
272 @SuppressWarnings({
273 "PMD.EmptyFinalizer",
274 "checkstyle:NoFinalizer" })
275 protected final void finalize() {
276
277 }
278
279 private static class ModuleLoader
280 extends BindingModuleLoader {
281
282 public ModuleLoader(
283 @NonNull IBindingContext bindingContext,
284 @NonNull ModuleLoadingPostProcessor postProcessor) {
285 super(bindingContext, postProcessor);
286 }
287
288 @Override
289 public IBindingMetaschemaModule load(URI resource) throws MetaschemaException, IOException {
290 IBindingMetaschemaModule module = super.load(resource);
291 getBindingContext().registerModule(module);
292 return module;
293 }
294
295 @Override
296 public IBindingMetaschemaModule load(Path path) throws MetaschemaException, IOException {
297 IBindingMetaschemaModule module = super.load(path);
298 getBindingContext().registerModule(module);
299 return module;
300 }
301
302 @Override
303 public IBindingMetaschemaModule load(URL url) throws MetaschemaException, IOException {
304 IBindingMetaschemaModule module = super.load(url);
305 getBindingContext().registerModule(module);
306 return module;
307 }
308
309 @Override
310 protected IBindingMetaschemaModule newModule(
311 URI resource,
312 METASCHEMA binding,
313 List<? extends IBindingMetaschemaModule> importedModules) throws MetaschemaException {
314 return super.newModule(resource, binding, importedModules);
315 }
316 }
317 }