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.model;
28  
29  import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
30  import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline;
31  import gov.nist.secauto.metaschema.core.model.IChoiceInstance;
32  import gov.nist.secauto.metaschema.core.model.IFlagContainerSupport;
33  import gov.nist.secauto.metaschema.core.model.IModule;
34  import gov.nist.secauto.metaschema.core.model.IModelContainerSupport;
35  import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.InternalModelSource;
36  import gov.nist.secauto.metaschema.core.model.constraint.IModelConstrained;
37  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
38  import gov.nist.secauto.metaschema.databind.IBindingContext;
39  import gov.nist.secauto.metaschema.databind.io.BindingException;
40  import gov.nist.secauto.metaschema.databind.model.annotations.AssemblyConstraints;
41  import gov.nist.secauto.metaschema.databind.model.annotations.BoundAssembly;
42  import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaAssembly;
43  import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
44  
45  import java.util.Objects;
46  
47  import javax.xml.namespace.QName;
48  
49  import edu.umd.cs.findbugs.annotations.NonNull;
50  import edu.umd.cs.findbugs.annotations.Nullable;
51  import nl.talsmasoftware.lazy4j.Lazy;
52  
53  public class DefaultAssemblyClassBinding // NOPMD - ok
54      extends AbstractClassBinding
55      implements IAssemblyClassBinding {
56  
57    @NonNull
58    private final MetaschemaAssembly metaschemaAssembly;
59    private final QName xmlRootQName;
60    @NonNull
61    private final Lazy<ClassBindingFlagContainerSupport> flagContainer;
62    @NonNull
63    private final Lazy<ClassBindingModelContainerSupport> modelContainer;
64    @NonNull
65    private final Lazy<IModelConstrained> constraints;
66  
67    /**
68     * Create a new {@link IClassBinding} for a Java bean annotated with the
69     * {@link BoundAssembly} annotation.
70     *
71     * @param clazz
72     *          the Java bean class
73     * @param bindingContext
74     *          information about how Java classes are bound to Module definitions
75     * @return the Module assembly binding for the class
76     */
77    @NonNull
78    public static DefaultAssemblyClassBinding createInstance(
79        @NonNull Class<?> clazz,
80        @NonNull IBindingContext bindingContext) {
81      return new DefaultAssemblyClassBinding(clazz, bindingContext);
82    }
83  
84    /**
85     * Construct a new {@link IClassBinding} for a Java bean annotated with the
86     * {@link BoundAssembly} annotation.
87     *
88     * @param clazz
89     *          the Java bean class
90     * @param bindingContext
91     *          the class binding context for which this class is participating
92     */
93    protected DefaultAssemblyClassBinding(@NonNull Class<?> clazz, @NonNull IBindingContext bindingContext) {
94      super(clazz, bindingContext);
95      Objects.requireNonNull(clazz, "clazz");
96      if (!clazz.isAnnotationPresent(MetaschemaAssembly.class)) {
97        throw new IllegalArgumentException(
98            String.format("Class '%s' is missing the '%s' annotation.",
99                clazz.getName(),
100               MetaschemaAssembly.class.getName())); // NOPMD
101     }
102     this.metaschemaAssembly = ObjectUtils.notNull(clazz.getAnnotation(MetaschemaAssembly.class));
103     String namespace = ObjectUtils.notNull(ModelUtil.resolveNamespace(this.metaschemaAssembly.rootNamespace(), this));
104     String localName = ModelUtil.resolveLocalName(this.metaschemaAssembly.rootName(), null);
105 
106     this.xmlRootQName = localName == null ? null : new QName(namespace, localName);
107 
108     this.flagContainer = ObjectUtils.notNull(Lazy.lazy(() -> new ClassBindingFlagContainerSupport(this, null)));
109     this.modelContainer = ObjectUtils.notNull(Lazy.lazy(() -> new ClassBindingModelContainerSupport(this)));
110     this.constraints = ObjectUtils.notNull(Lazy.lazy(() -> new AssemblyConstraintSupport(
111         clazz.getAnnotation(ValueConstraints.class),
112         clazz.getAnnotation(AssemblyConstraints.class),
113         InternalModelSource.instance())));
114   }
115 
116   @SuppressWarnings("null")
117   @Override
118   public IFlagContainerSupport<IBoundFlagInstance> getFlagContainer() {
119     return flagContainer.get();
120   }
121 
122   @SuppressWarnings("null")
123   @Override
124   public IModelContainerSupport<
125       IBoundNamedModelInstance,
126       IBoundNamedModelInstance,
127       IBoundFieldInstance,
128       IBoundAssemblyInstance,
129       IChoiceInstance> getModelContainer() {
130     return modelContainer.get();
131   }
132 
133   /**
134    * Get the {@link MetaschemaAssembly} annotation associated with this class.
135    * This annotation provides information used by this class binding to control
136    * binding behavior.
137    *
138    * @return the annotation
139    */
140   @NonNull
141   private MetaschemaAssembly getMetaschemaAssemblyAnnotation() {
142     return metaschemaAssembly;
143   }
144 
145   @Override
146   public String getFormalName() {
147     return ModelUtil.resolveToString(getMetaschemaAssemblyAnnotation().formalName());
148   }
149 
150   @Override
151   public MarkupLine getDescription() {
152     return ModelUtil.resolveToMarkupLine(getMetaschemaAssemblyAnnotation().description());
153   }
154 
155   @Override
156   public @Nullable MarkupMultiline getRemarks() {
157     return ModelUtil.resolveToMarkupMultiline(getMetaschemaAssemblyAnnotation().description());
158   }
159 
160   @Override
161   public String getName() {
162     return getMetaschemaAssemblyAnnotation().name();
163   }
164 
165   @Override
166   public boolean isInline() {
167     return false;
168   }
169 
170   @Override
171   public IBoundAssemblyInstance getInlineInstance() {
172     return null;
173   }
174 
175   @Override
176   public boolean isRoot() {
177     // Overriding this is more efficient, since the root name is derived from the
178     // XML QName
179     return getRootXmlQName() != null;
180   }
181 
182   @Override
183   public String getRootName() {
184     // Overriding this is more efficient, since it is already built
185     QName qname = getRootXmlQName();
186     return qname == null ? null : qname.getLocalPart();
187   }
188 
189   @Override
190   public QName getRootXmlQName() {
191     // Overriding this is more efficient, since it is already built
192     return xmlRootQName;
193   }
194 
195   @Override
196   public IModelConstrained getConstraintSupport() {
197     return ObjectUtils.notNull(constraints.get());
198   }
199 
200   @Override
201   protected void copyBoundObjectInternal(@NonNull Object fromInstance, @NonNull Object toInstance)
202       throws BindingException {
203     super.copyBoundObjectInternal(fromInstance, toInstance);
204 
205     for (IBoundNamedModelInstance property : getModelInstances()) {
206       property.copyBoundObject(fromInstance, toInstance);
207     }
208   }
209 
210   @Override
211   protected Class<? extends IModule> getModuleClass() {
212     return getMetaschemaAssemblyAnnotation().moduleClass();
213   }
214 
215 }