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