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