1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.databind.model.impl;
7   
8   import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
9   import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline;
10  import gov.nist.secauto.metaschema.core.model.IAttributable;
11  import gov.nist.secauto.metaschema.core.model.IBoundObject;
12  import gov.nist.secauto.metaschema.core.model.ISource;
13  import gov.nist.secauto.metaschema.core.model.constraint.AssemblyConstraintSet;
14  import gov.nist.secauto.metaschema.core.model.constraint.IModelConstrained;
15  import gov.nist.secauto.metaschema.core.util.CollectionUtil;
16  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
17  import gov.nist.secauto.metaschema.databind.IBindingContext;
18  import gov.nist.secauto.metaschema.databind.io.BindingException;
19  import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelAssembly;
20  import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModel;
21  import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelAssembly;
22  import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelChoiceGroup;
23  import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelField;
24  import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelNamed;
25  import gov.nist.secauto.metaschema.databind.model.IBoundModule;
26  import gov.nist.secauto.metaschema.databind.model.IBoundProperty;
27  import gov.nist.secauto.metaschema.databind.model.annotations.AssemblyConstraints;
28  import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaAssembly;
29  import gov.nist.secauto.metaschema.databind.model.annotations.ModelUtil;
30  import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
31  
32  import java.util.Arrays;
33  import java.util.LinkedHashMap;
34  import java.util.Map;
35  import java.util.Set;
36  import java.util.stream.Collectors;
37  
38  import javax.xml.namespace.QName;
39  
40  import edu.umd.cs.findbugs.annotations.NonNull;
41  import edu.umd.cs.findbugs.annotations.Nullable;
42  import nl.talsmasoftware.lazy4j.Lazy;
43  
44  /**
45   * Implements a Metaschema module global assembly definition bound to a Java
46   * class.
47   */
48  @SuppressWarnings("PMD.CouplingBetweenObjects")
49  public final class DefinitionAssembly
50      extends AbstractBoundDefinitionModelComplex<MetaschemaAssembly>
51      implements IBoundDefinitionModelAssembly,
52      IFeatureBoundContainerModelAssembly<
53          IBoundInstanceModel<?>,
54          IBoundInstanceModelNamed<?>,
55          IBoundInstanceModelField<?>,
56          IBoundInstanceModelAssembly,
57          IBoundInstanceModelChoiceGroup> {
58  
59    @NonNull
60    private final Lazy<FlagContainerSupport> flagContainer;
61    @NonNull
62    private final Lazy<AssemblyModelContainerSupport> modelContainer;
63    @NonNull
64    private final Lazy<IModelConstrained> constraints;
65    @NonNull
66    private final Lazy<QName> xmlRootQName;
67    @NonNull
68    private final Lazy<Map<String, IBoundProperty<?>>> jsonProperties;
69    @NonNull
70    private final Lazy<Map<IAttributable.Key, Set<String>>> properties;
71  
72    /**
73     * Construct a new global assembly instance.
74     *
75     * @param clazz
76     *          the class the assembly is bound to
77     * @param annotation
78     *          the binding annotation associated with this class
79     * @param module
80     *          the module containing this class
81     * @param bindingContext
82     *          the Metaschema binding context managing this class used to lookup
83     *          binding information
84     * @return the definition
85     */
86    @NonNull
87    public static DefinitionAssembly newInstance(
88        @NonNull Class<? extends IBoundObject> clazz,
89        @NonNull MetaschemaAssembly annotation,
90        @NonNull IBoundModule module,
91        @NonNull IBindingContext bindingContext) {
92      return new DefinitionAssembly(clazz, annotation, module, bindingContext);
93    }
94  
95    private DefinitionAssembly(
96        @NonNull Class<? extends IBoundObject> clazz,
97        @NonNull MetaschemaAssembly annotation,
98        @NonNull IBoundModule module,
99        @NonNull IBindingContext bindingContext) {
100     super(clazz, annotation, module, bindingContext);
101 
102     String rootLocalName = ModelUtil.resolveNoneOrDefault(getAnnotation().rootName(), null);
103     this.xmlRootQName = ObjectUtils.notNull(Lazy.lazy(() -> rootLocalName == null
104         ? null
105         : getContainingModule().toModelQName(rootLocalName)));
106     this.flagContainer = ObjectUtils.notNull(Lazy.lazy(() -> new FlagContainerSupport(this, null)));
107     this.modelContainer = ObjectUtils.notNull(Lazy.lazy(() -> new AssemblyModelContainerSupport(this)));
108 
109     ISource moduleSource = module.getSource();
110     this.constraints = ObjectUtils.notNull(Lazy.lazy(() -> {
111       IModelConstrained retval = new AssemblyConstraintSet();
112       ValueConstraints valueAnnotation = getAnnotation().valueConstraints();
113       ConstraintSupport.parse(valueAnnotation, moduleSource, retval);
114 
115       AssemblyConstraints assemblyAnnotation = getAnnotation().modelConstraints();
116       ConstraintSupport.parse(assemblyAnnotation, moduleSource, retval);
117       return retval;
118     }));
119     this.jsonProperties = ObjectUtils.notNull(Lazy.lazy(() -> getJsonProperties(null)));
120     this.properties = ObjectUtils.notNull(
121         Lazy.lazy(() -> CollectionUtil.unmodifiableMap(ObjectUtils.notNull(
122             Arrays.stream(annotation.properties())
123                 .map(ModelUtil::toPropertyEntry)
124                 .collect(
125                     Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v2, LinkedHashMap::new))))));
126   }
127 
128   @Override
129   protected void deepCopyItemInternal(IBoundObject fromObject, IBoundObject toObject) throws BindingException {
130     // copy the flags
131     super.deepCopyItemInternal(fromObject, toObject);
132 
133     for (IBoundInstanceModel<?> instance : getModelInstances()) {
134       instance.deepCopy(fromObject, toObject);
135     }
136   }
137 
138   @Override
139   public Map<String, IBoundProperty<?>> getJsonProperties() {
140     return ObjectUtils.notNull(jsonProperties.get());
141   }
142 
143   // ------------------------------------------
144   // - Start annotation driven code - CPD-OFF -
145   // ------------------------------------------
146 
147   @Override
148   @SuppressWarnings("null")
149   @NonNull
150   public FlagContainerSupport getFlagContainer() {
151     return flagContainer.get();
152   }
153 
154   @Override
155   @SuppressWarnings("null")
156   @NonNull
157   public AssemblyModelContainerSupport getModelContainer() {
158     return modelContainer.get();
159   }
160 
161   @Override
162   @NonNull
163   public IModelConstrained getConstraintSupport() {
164     return ObjectUtils.notNull(constraints.get());
165   }
166 
167   @Override
168   @Nullable
169   public String getFormalName() {
170     return ModelUtil.resolveNoneOrValue(getAnnotation().formalName());
171   }
172 
173   @Override
174   @Nullable
175   public MarkupLine getDescription() {
176     return ModelUtil.resolveToMarkupLine(getAnnotation().description());
177   }
178 
179   @Override
180   @NonNull
181   public String getName() {
182     return getAnnotation().name();
183   }
184 
185   @Override
186   @Nullable
187   public Integer getIndex() {
188     return ModelUtil.resolveDefaultInteger(getAnnotation().index());
189   }
190 
191   @Override
192   public Map<Key, Set<String>> getProperties() {
193     return ObjectUtils.notNull(properties.get());
194   }
195 
196   @Override
197   @Nullable
198   public MarkupMultiline getRemarks() {
199     return ModelUtil.resolveToMarkupMultiline(getAnnotation().description());
200   }
201 
202   @Override
203   @Nullable
204   public QName getRootXmlQName() {
205     // Overriding this is more efficient, since it is already built
206     return xmlRootQName.get();
207   }
208 
209   @Override
210   public boolean isRoot() {
211     // Overriding this is more efficient, since the root name is derived from the
212     // XML QName
213     return getRootXmlQName() != null;
214   }
215 
216   @Override
217   @Nullable
218   public String getRootName() {
219     // Overriding this is more efficient, since it is already built
220     QName qname = getRootXmlQName();
221     return qname == null ? null : qname.getLocalPart();
222   }
223 
224   @Override
225   @Nullable
226   public Integer getRootIndex() {
227     return ModelUtil.resolveDefaultInteger(getAnnotation().rootIndex());
228   }
229 
230   // ----------------------------------------
231   // - End annotation driven code - CPD-OFF -
232   // ----------------------------------------
233 }