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.lazy(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 registerModule(MetaschemaModelModule.class);
102 }
103
104 @Override
105 @NonNull
106 public final IModuleLoaderStrategy getModuleLoaderStrategy() {
107 return moduleLoaderStrategy;
108 }
109
110 @Override
111 public IBindingModuleLoader newModuleLoader() {
112 return new ModuleLoader(this, getModuleLoaderStrategy());
113 }
114
115 @Override
116 @NonNull
117 public final IBoundModule registerModule(@NonNull Class<? extends IBoundModule> clazz) {
118 IModuleLoaderStrategy strategy = getModuleLoaderStrategy();
119 IBoundModule module = strategy.loadModule(clazz, this);
120 registerImportedModules(module);
121 return strategy.registerModule(module, this);
122 }
123
124 private void registerImportedModules(@NonNull IBoundModule module) {
125 IModuleLoaderStrategy strategy = getModuleLoaderStrategy();
126 module.getImportedModules().stream()
127 .forEachOrdered(parentModule -> {
128 registerImportedModules(ObjectUtils.notNull(parentModule));
129 strategy.registerModule(ObjectUtils.notNull(parentModule), this);
130 });
131 }
132
133
134
135
136
137
138 @NonNull
139 protected Collection<IBindingMatcher> getBindingMatchers() {
140 return getModuleLoaderStrategy().getBindingMatchers();
141 }
142
143 @Override
144 public final IBoundDefinitionModelComplex registerClassBinding(IBoundDefinitionModelComplex definition) {
145 Class<?> clazz = definition.getBoundClass();
146 return boundClassToStrategyMap.computeIfAbsent(clazz, k -> definition);
147 }
148
149 @Override
150 public final IBoundDefinitionModelComplex getBoundDefinitionForClass(@NonNull Class<? extends IBoundObject> clazz) {
151 return moduleLoaderStrategy.getBoundDefinitionForClass(clazz, this);
152 }
153
154
155
156
157
158
159 @Override
160 public <CLASS extends IBoundObject> ISerializer<CLASS> newSerializer(
161 @NonNull Format format,
162 @NonNull Class<CLASS> clazz) {
163 Objects.requireNonNull(format, "format");
164 IBoundDefinitionModelAssembly definition;
165 try {
166 definition = IBoundDefinitionModelAssembly.class.cast(getBoundDefinitionForClass(clazz));
167 } catch (ClassCastException ex) {
168 throw new IllegalStateException(
169 String.format("Class '%s' is not a bound assembly.", clazz.getClass().getName()), ex);
170 }
171 if (definition == null) {
172 throw new IllegalStateException(String.format("Class '%s' is not bound", clazz.getClass().getName()));
173 }
174 ISerializer<CLASS> retval;
175 switch (format) {
176 case JSON:
177 retval = new DefaultJsonSerializer<>(definition);
178 break;
179 case XML:
180 retval = new DefaultXmlSerializer<>(definition);
181 break;
182 case YAML:
183 retval = new DefaultYamlSerializer<>(definition);
184 break;
185 default:
186 throw new UnsupportedOperationException(String.format("Unsupported format '%s'", format));
187 }
188 return retval;
189 }
190
191
192
193
194
195
196 @Override
197 public <CLASS extends IBoundObject> IDeserializer<CLASS> newDeserializer(
198 @NonNull Format format,
199 @NonNull Class<CLASS> clazz) {
200 IBoundDefinitionModelAssembly definition;
201 try {
202 definition = IBoundDefinitionModelAssembly.class.cast(getBoundDefinitionForClass(clazz));
203 } catch (ClassCastException ex) {
204 throw new IllegalStateException(
205 String.format("Class '%s' is not a bound assembly.", clazz.getClass().getName()),
206 ex);
207 }
208 if (definition == null) {
209 throw new IllegalStateException(String.format("Class '%s' is not bound", clazz.getName()));
210 }
211 IDeserializer<CLASS> retval;
212 switch (format) {
213 case JSON:
214 retval = new DefaultJsonDeserializer<>(definition);
215 break;
216 case XML:
217 retval = new DefaultXmlDeserializer<>(definition);
218 break;
219 case YAML:
220 retval = new DefaultYamlDeserializer<>(definition);
221 break;
222 default:
223 throw new UnsupportedOperationException(String.format("Unsupported format '%s'", format));
224 }
225
226 return retval;
227 }
228
229 @Override
230 public Class<? extends IBoundObject> getBoundClassForRootXmlQName(@NonNull QName rootQName) {
231 Class<? extends IBoundObject> retval = null;
232 for (IBindingMatcher matcher : getBindingMatchers()) {
233 retval = matcher.getBoundClassForXmlQName(rootQName);
234 if (retval != null) {
235 break;
236 }
237 }
238 return retval;
239 }
240
241 @Override
242 public Class<? extends IBoundObject> getBoundClassForRootJsonName(@NonNull String rootName) {
243 Class<? extends IBoundObject> retval = null;
244 for (IBindingMatcher matcher : getBindingMatchers()) {
245 retval = matcher.getBoundClassForJsonName(rootName);
246 if (retval != null) {
247 break;
248 }
249 }
250 return retval;
251 }
252
253 @Override
254 public <CLASS extends IBoundObject> CLASS deepCopy(@NonNull CLASS other, IBoundObject parentInstance)
255 throws BindingException {
256 IBoundDefinitionModelComplex definition = getBoundDefinitionForClass(other.getClass());
257 if (definition == null) {
258 throw new IllegalStateException(String.format("Class '%s' is not bound", other.getClass().getName()));
259 }
260 return ObjectUtils.asType(definition.deepCopyItem(other, parentInstance));
261 }
262
263 private static class ModuleLoader
264 extends BindingModuleLoader {
265
266 public ModuleLoader(
267 @NonNull IBindingContext bindingContext,
268 @NonNull ModuleLoadingPostProcessor postProcessor) {
269 super(bindingContext, postProcessor);
270 }
271
272 @Override
273 public IBindingMetaschemaModule load(URI resource) throws MetaschemaException, IOException {
274 IBindingMetaschemaModule module = super.load(resource);
275 getBindingContext().registerModule(module);
276 return module;
277 }
278
279 @Override
280 public IBindingMetaschemaModule load(Path path) throws MetaschemaException, IOException {
281 IBindingMetaschemaModule module = super.load(path);
282 getBindingContext().registerModule(module);
283 return module;
284 }
285
286 @Override
287 public IBindingMetaschemaModule load(URL url) throws MetaschemaException, IOException {
288 IBindingMetaschemaModule module = super.load(url);
289 getBindingContext().registerModule(module);
290 return module;
291 }
292
293 @Override
294 protected IBindingMetaschemaModule newModule(
295 URI resource,
296 METASCHEMA binding,
297 List<? extends IBindingMetaschemaModule> importedModules) throws MetaschemaException {
298 return super.newModule(resource, binding, importedModules);
299 }
300
301 }
302 }