View Javadoc
1   /*
2    * Portions of this software was developed by employees of the National Institute
3    * of Standards and Technology (NIST), an agency of the Federal Government and is
4    * being made available as a public service. Pursuant to title 17 United States
5    * Code Section 105, works of NIST employees are not subject to copyright
6    * protection in the United States. This software may be subject to foreign
7    * copyright. Permission in the United States and in foreign countries, to the
8    * extent that NIST may hold copyright, to use, copy, modify, create derivative
9    * works, and distribute this software and its documentation without fee is hereby
10   * granted on a non-exclusive basis, provided that this notice and disclaimer
11   * of warranty appears in all copies.
12   *
13   * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
14   * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
15   * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
17   * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
18   * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
19   * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
20   * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
21   * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
22   * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
23   * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
24   * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
25   */
26  
27  package gov.nist.secauto.metaschema.databind;
28  
29  import gov.nist.secauto.metaschema.core.datatype.DataTypeService;
30  import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
31  import gov.nist.secauto.metaschema.core.model.IModule;
32  import gov.nist.secauto.metaschema.core.model.constraint.IConstraintSet;
33  import gov.nist.secauto.metaschema.core.util.CollectionUtil;
34  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
35  import gov.nist.secauto.metaschema.databind.io.BindingException;
36  import gov.nist.secauto.metaschema.databind.io.DefaultBoundLoader;
37  import gov.nist.secauto.metaschema.databind.io.Format;
38  import gov.nist.secauto.metaschema.databind.io.IBoundLoader;
39  import gov.nist.secauto.metaschema.databind.io.IDeserializer;
40  import gov.nist.secauto.metaschema.databind.io.ISerializer;
41  import gov.nist.secauto.metaschema.databind.io.json.DefaultJsonDeserializer;
42  import gov.nist.secauto.metaschema.databind.io.json.DefaultJsonSerializer;
43  import gov.nist.secauto.metaschema.databind.io.xml.DefaultXmlDeserializer;
44  import gov.nist.secauto.metaschema.databind.io.xml.DefaultXmlSerializer;
45  import gov.nist.secauto.metaschema.databind.io.yaml.DefaultYamlDeserializer;
46  import gov.nist.secauto.metaschema.databind.io.yaml.DefaultYamlSerializer;
47  import gov.nist.secauto.metaschema.databind.model.IAssemblyClassBinding;
48  import gov.nist.secauto.metaschema.databind.model.IClassBinding;
49  
50  import java.util.LinkedList;
51  import java.util.List;
52  import java.util.Map;
53  import java.util.Objects;
54  import java.util.Set;
55  
56  import javax.xml.namespace.QName;
57  
58  import edu.umd.cs.findbugs.annotations.NonNull;
59  
60  /**
61   * The implementation of a {@link IBindingContext} provided by this library.
62   * <p>
63   * This implementation caches Module information, which can dramatically improve
64   * read and write performance at the cost of some memory use. Thus, using the
65   * same singleton of this class across multiple I/O operations will improve
66   * overall read and write performance when processing the same types of data.
67   * <p>
68   * Serializers and deserializers provided by this class using the
69   * {@link #newSerializer(Format, Class)} and
70   * {@link #newDeserializer(Format, Class)} methods will
71   * <p>
72   * This class is synchronized and is thread-safe.
73   */
74  public class DefaultBindingContext implements IBindingContext {
75    private static DefaultBindingContext singleton;
76    @NonNull
77    private final IModuleLoaderStrategy moduleLoaderStrategy;
78    @NonNull
79    private final List<IBindingMatcher> bindingMatchers = new LinkedList<>();
80  
81    /**
82     * Get the singleton instance of this binding context.
83     *
84     * @return the binding context
85     */
86    @NonNull
87    public static DefaultBindingContext instance() {
88      synchronized (DefaultBindingContext.class) {
89        if (singleton == null) {
90          singleton = new DefaultBindingContext();
91        }
92      }
93      return ObjectUtils.notNull(singleton);
94    }
95  
96    /**
97     * Construct a new binding context.
98     *
99     * @param externalConstraintSets
100    *          the set of external constraints to configure this binding to use
101    */
102   public DefaultBindingContext(@NonNull Set<IConstraintSet> externalConstraintSets) {
103     // only allow extended classes
104     moduleLoaderStrategy = new ExternalConstraintsModuleLoaderStrategy(this, externalConstraintSets);
105   }
106 
107   /**
108    * Construct a new binding context.
109    */
110   public DefaultBindingContext() {
111     // only allow extended classes
112     moduleLoaderStrategy = new SimpleModuleLoaderStrategy(this);
113   }
114 
115   @Override
116   public IModule getModuleByClass(@NonNull Class<? extends IModule> clazz) {
117     return moduleLoaderStrategy.getModuleByClass(clazz);
118   }
119 
120   @Override
121   public IClassBinding getClassBinding(@NonNull Class<?> clazz) {
122     return moduleLoaderStrategy.getClassBinding(clazz);
123   }
124 
125   @Override
126   public Map<Class<?>, IClassBinding> getClassBindingsByClass() {
127     return moduleLoaderStrategy.getClassBindingsByClass();
128   }
129 
130   @Override
131   public <TYPE extends IDataTypeAdapter<?>> TYPE getJavaTypeAdapterInstance(@NonNull Class<TYPE> clazz) {
132     return DataTypeService.getInstance().getJavaTypeAdapterByClass(clazz);
133   }
134 
135   /**
136    * {@inheritDoc}
137    * <p>
138    * A serializer returned by this method is thread-safe.
139    */
140   @Override
141   public <CLASS> ISerializer<CLASS> newSerializer(@NonNull Format format, @NonNull Class<CLASS> clazz) {
142     Objects.requireNonNull(format, "format");
143     IAssemblyClassBinding classBinding = (IAssemblyClassBinding) getClassBinding(clazz);
144     if (classBinding == null) {
145       throw new IllegalStateException(String.format("Class '%s' is not bound", clazz.getClass().getName()));
146     }
147     ISerializer<CLASS> retval;
148     switch (format) {
149     case JSON:
150       retval = new DefaultJsonSerializer<>(classBinding);
151       break;
152     case XML:
153       retval = new DefaultXmlSerializer<>(classBinding);
154       break;
155     case YAML:
156       retval = new DefaultYamlSerializer<>(classBinding);
157       break;
158     default:
159       throw new UnsupportedOperationException(String.format("Unsupported format '%s'", format));
160     }
161     return retval;
162   }
163 
164   /**
165    * {@inheritDoc}
166    * <p>
167    * A deserializer returned by this method is thread-safe.
168    */
169   @Override
170   public <CLASS> IDeserializer<CLASS> newDeserializer(@NonNull Format format, @NonNull Class<CLASS> clazz) {
171     IAssemblyClassBinding classBinding = (IAssemblyClassBinding) getClassBinding(clazz);
172     if (classBinding == null) {
173       throw new IllegalStateException(String.format("Class '%s' is not bound", clazz.getName()));
174     }
175     IDeserializer<CLASS> retval;
176     switch (format) {
177     case JSON:
178       retval = new DefaultJsonDeserializer<>(classBinding);
179       break;
180     case XML:
181       retval = new DefaultXmlDeserializer<>(classBinding);
182       break;
183     case YAML:
184       retval = new DefaultYamlDeserializer<>(classBinding);
185       break;
186     default:
187       throw new UnsupportedOperationException(String.format("Unsupported format '%s'", format));
188     }
189 
190     return retval;
191   }
192 
193   @Override
194   public DefaultBindingContext registerBindingMatcher(@NonNull IBindingMatcher matcher) {
195     synchronized (this) {
196       bindingMatchers.add(matcher);
197     }
198     return this;
199   }
200 
201   /**
202    * Get the binding matchers that are associated with this class.
203    *
204    * @return the list of matchers
205    * @see #registerBindingMatcher(IBindingMatcher)
206    */
207   @NonNull
208   protected List<? extends IBindingMatcher> getBindingMatchers() {
209     synchronized (this) {
210       return CollectionUtil.unmodifiableList(bindingMatchers);
211     }
212   }
213 
214   @Override
215   public Class<?> getBoundClassForXmlQName(@NonNull QName rootQName) {
216     Class<?> retval = null;
217     for (IBindingMatcher matcher : getBindingMatchers()) {
218       retval = matcher.getBoundClassForXmlQName(rootQName);
219       if (retval != null) {
220         break;
221       }
222     }
223     return retval;
224   }
225 
226   @Override
227   public Class<?> getBoundClassForJsonName(@NonNull String rootName) {
228     Class<?> retval = null;
229     for (IBindingMatcher matcher : getBindingMatchers()) {
230       retval = matcher.getBoundClassForJsonName(rootName);
231       if (retval != null) {
232         break;
233       }
234     }
235     return retval;
236   }
237 
238   @Override
239   public IBoundLoader newBoundLoader() {
240     return new DefaultBoundLoader(this);
241   }
242 
243   @Override
244   public <CLASS> CLASS copyBoundObject(@NonNull CLASS other, Object parentInstance) throws BindingException {
245     IClassBinding classBinding = getClassBinding(other.getClass());
246     if (classBinding == null) {
247       throw new IllegalStateException(String.format("Class '%s' is not bound", other.getClass().getName()));
248     }
249     @SuppressWarnings("unchecked") CLASS retval = (CLASS) classBinding.copyBoundObject(other, parentInstance);
250     return retval;
251   }
252 }