001/*
002 * Portions of this software was developed by employees of the National Institute
003 * of Standards and Technology (NIST), an agency of the Federal Government and is
004 * being made available as a public service. Pursuant to title 17 United States
005 * Code Section 105, works of NIST employees are not subject to copyright
006 * protection in the United States. This software may be subject to foreign
007 * copyright. Permission in the United States and in foreign countries, to the
008 * extent that NIST may hold copyright, to use, copy, modify, create derivative
009 * works, and distribute this software and its documentation without fee is hereby
010 * granted on a non-exclusive basis, provided that this notice and disclaimer
011 * of warranty appears in all copies.
012 *
013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
025 */
026
027package gov.nist.secauto.metaschema.databind.model;
028
029import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
030import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline;
031import gov.nist.secauto.metaschema.core.model.IChoiceInstance;
032import gov.nist.secauto.metaschema.core.model.IFlagContainerSupport;
033import gov.nist.secauto.metaschema.core.model.IModule;
034import gov.nist.secauto.metaschema.core.model.IModelContainerSupport;
035import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.InternalModelSource;
036import gov.nist.secauto.metaschema.core.model.constraint.IModelConstrained;
037import gov.nist.secauto.metaschema.core.util.ObjectUtils;
038import gov.nist.secauto.metaschema.databind.IBindingContext;
039import gov.nist.secauto.metaschema.databind.io.BindingException;
040import gov.nist.secauto.metaschema.databind.model.annotations.AssemblyConstraints;
041import gov.nist.secauto.metaschema.databind.model.annotations.BoundAssembly;
042import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaAssembly;
043import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
044
045import java.util.Objects;
046
047import javax.xml.namespace.QName;
048
049import edu.umd.cs.findbugs.annotations.NonNull;
050import edu.umd.cs.findbugs.annotations.Nullable;
051import nl.talsmasoftware.lazy4j.Lazy;
052
053public class DefaultAssemblyClassBinding // NOPMD - ok
054    extends AbstractClassBinding
055    implements IAssemblyClassBinding {
056
057  @NonNull
058  private final MetaschemaAssembly metaschemaAssembly;
059  private final QName xmlRootQName;
060  @NonNull
061  private final Lazy<ClassBindingFlagContainerSupport> flagContainer;
062  @NonNull
063  private final Lazy<ClassBindingModelContainerSupport> modelContainer;
064  @NonNull
065  private final Lazy<IModelConstrained> constraints;
066
067  /**
068   * Create a new {@link IClassBinding} for a Java bean annotated with the
069   * {@link BoundAssembly} annotation.
070   *
071   * @param clazz
072   *          the Java bean class
073   * @param bindingContext
074   *          information about how Java classes are bound to Module definitions
075   * @return the Module assembly binding for the class
076   */
077  @NonNull
078  public static DefaultAssemblyClassBinding createInstance(
079      @NonNull Class<?> clazz,
080      @NonNull IBindingContext bindingContext) {
081    return new DefaultAssemblyClassBinding(clazz, bindingContext);
082  }
083
084  /**
085   * Construct a new {@link IClassBinding} for a Java bean annotated with the
086   * {@link BoundAssembly} annotation.
087   *
088   * @param clazz
089   *          the Java bean class
090   * @param bindingContext
091   *          the class binding context for which this class is participating
092   */
093  protected DefaultAssemblyClassBinding(@NonNull Class<?> clazz, @NonNull IBindingContext bindingContext) {
094    super(clazz, bindingContext);
095    Objects.requireNonNull(clazz, "clazz");
096    if (!clazz.isAnnotationPresent(MetaschemaAssembly.class)) {
097      throw new IllegalArgumentException(
098          String.format("Class '%s' is missing the '%s' annotation.",
099              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}