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.model.JsonGroupAsBehavior;
30  import gov.nist.secauto.metaschema.core.model.XmlGroupAsBehavior;
31  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
32  import gov.nist.secauto.metaschema.databind.io.BindingException;
33  import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs;
34  import gov.nist.secauto.metaschema.databind.model.info.IDataTypeHandler;
35  import gov.nist.secauto.metaschema.databind.model.info.IModelPropertyInfo;
36  import gov.nist.secauto.metaschema.databind.model.info.IPropertyCollector;
37  
38  import java.lang.reflect.Field;
39  import java.util.Collection;
40  
41  import edu.umd.cs.findbugs.annotations.NonNull;
42  import edu.umd.cs.findbugs.annotations.Nullable;
43  import nl.talsmasoftware.lazy4j.Lazy;
44  
45  abstract class AbstractNamedModelProperty // NOPMD - intentional
46      extends AbstractProperty<IAssemblyClassBinding>
47      implements IBoundNamedModelInstance {
48    // private static final Logger logger =
49    // LogManager.getLogger(AbstractNamedModelProperty.class);
50  
51    @NonNull
52    private static final IGroupAs SINGLETON_GROUP_AS = new IGroupAs() {
53      @Override
54      public String getGroupAsName() {
55        throw new UnsupportedOperationException();
56      }
57  
58      @Override
59      public String getGroupAsXmlNamespace() {
60        throw new UnsupportedOperationException();
61      }
62  
63      @Override
64      public JsonGroupAsBehavior getJsonGroupAsBehavior() {
65        return JsonGroupAsBehavior.NONE;
66      }
67  
68      @Override
69      public XmlGroupAsBehavior getXmlGroupAsBehavior() {
70        return XmlGroupAsBehavior.UNGROUPED;
71      }
72    };
73  
74    @NonNull
75    private final Field field;
76    @NonNull
77    private final IGroupAs groupAs;
78    @NonNull
79    private final Lazy<IModelPropertyInfo> propertyInfo;
80    @NonNull
81    private final Lazy<IDataTypeHandler> dataTypeHandler;
82  
83    /**
84     * Construct a new bound model instance based on a Java property. The name of
85     * the property is bound to the name of the instance.
86     *
87     * @param field
88     *          the field instance associated with this property
89     *
90     * @param parentClassBinding
91     *          the class binding for the field's containing class
92     */
93    protected AbstractNamedModelProperty(
94        @NonNull Field field,
95        @NonNull IAssemblyClassBinding parentClassBinding) {
96      super(parentClassBinding);
97      this.field = ObjectUtils.requireNonNull(field, "field");
98  
99      GroupAs annotation = field.getAnnotation(GroupAs.class);
100     // if (annotation == null && (getMaxOccurs() == -1 || getMaxOccurs() > 1)) {
101     // throw new IllegalStateException(String.format("Field '%s' on class '%s' is
102     // missing the '%s'
103     // annotation.",
104     // field.getName(), parentClassBinding.getBoundClass().getName(),
105     // GroupAs.class.getName()));
106     // }
107     this.groupAs = annotation == null ? SINGLETON_GROUP_AS : new SimpleGroupAs(annotation, parentClassBinding);
108     this.propertyInfo = ObjectUtils.notNull(Lazy.lazy(() -> IModelPropertyInfo.newPropertyInfo(this)));
109     this.dataTypeHandler = ObjectUtils.notNull(Lazy.lazy(this::newDataTypeHandler));
110 
111   }
112 
113   // REFACTOR: remove this method if possible
114   protected abstract IDataTypeHandler newDataTypeHandler();
115 
116   @Override
117   public IDataTypeHandler getDataTypeHandler() {
118     return ObjectUtils.notNull(dataTypeHandler.get());
119   }
120 
121   @Override
122   public Field getField() {
123     return field;
124   }
125 
126   @Override
127   public abstract int getMinOccurs();
128 
129   @Override
130   public abstract int getMaxOccurs();
131 
132   @Override
133   public String getGroupAsName() {
134     return groupAs.getGroupAsName();
135   }
136 
137   @Override
138   public String getGroupAsXmlNamespace() {
139     return groupAs.getGroupAsXmlNamespace();
140   }
141 
142   @Override
143   public JsonGroupAsBehavior getJsonGroupAsBehavior() {
144     return groupAs.getJsonGroupAsBehavior();
145   }
146 
147   @Override
148   public XmlGroupAsBehavior getXmlGroupAsBehavior() {
149     return groupAs.getXmlGroupAsBehavior();
150   }
151 
152   /**
153    * Gets information about the bound property.
154    *
155    * @return the property information for the bound property
156    */
157   @SuppressWarnings("null")
158   @Override
159   @NonNull
160   public IModelPropertyInfo getPropertyInfo() {
161     return propertyInfo.get();
162   }
163 
164   @Override
165   public Collection<? extends Object> getItemValues(Object value) {
166     return getPropertyInfo().getItemsFromValue(value);
167   }
168 
169   //
170   // @Override
171   // public void writeItem(Object parentInstance, IJsonParsingContext context) {
172   // IDataTypeHandler supplier = getBindingSupplier();
173   // return supplier.write(parentInstance, context);
174   // }
175   //
176   // @Override
177   // public void writeValue(Object parentInstance, IJsonParsingContext context) {
178   // IModelPropertyInfo info = getPropertyInfo();
179   // return info.writeValue(parentInstance, context);
180   // }
181 
182   @Override
183   public void copyBoundObject(@NonNull Object fromInstance, @NonNull Object toInstance) throws BindingException {
184     Object value = getValue(fromInstance);
185     if (value != null) {
186       IModelPropertyInfo propertyInfo = getPropertyInfo();
187       IPropertyCollector collector = propertyInfo.newPropertyCollector();
188 
189       propertyInfo.copy(fromInstance, toInstance, collector);
190 
191       value = collector.getValue();
192     }
193     setValue(toInstance, value);
194   }
195 
196   @Override
197   public Object copyItem(Object fromItem, Object toInstance) throws BindingException {
198     return getDataTypeHandler().copyItem(fromItem, toInstance);
199   }
200 
201   /**
202    * A data object to record the group as selections.
203    */
204   private interface IGroupAs {
205     @NonNull
206     String getGroupAsName();
207 
208     @Nullable
209     String getGroupAsXmlNamespace();
210 
211     @NonNull
212     JsonGroupAsBehavior getJsonGroupAsBehavior();
213 
214     @NonNull
215     XmlGroupAsBehavior getXmlGroupAsBehavior();
216   }
217 
218   private static final class SimpleGroupAs implements IGroupAs {
219     @NonNull
220     private final String name;
221     @NonNull
222     private final Lazy<String> namespace;
223     @NonNull
224     private final GroupAs annotation;
225 
226     private SimpleGroupAs(@NonNull GroupAs annotation, @NonNull IClassBinding parentDefinition) {
227       this.annotation = annotation;
228       {
229         String value = ModelUtil.resolveLocalName(annotation.name(), null);
230         if (value == null) {
231           throw new IllegalStateException(
232               String.format("The %s#groupName value '%s' resulted in an invalid null value",
233                   GroupAs.class.getName(),
234                   annotation.name()));
235         }
236         this.name = value;
237       }
238       this.namespace = ObjectUtils.notNull(
239           Lazy.lazy(() -> ModelUtil.resolveNamespace(annotation.namespace(), parentDefinition)));
240     }
241 
242     @Override
243     public String getGroupAsName() {
244       return name;
245     }
246 
247     @Override
248     public String getGroupAsXmlNamespace() {
249       return namespace.get();
250     }
251 
252     @Override
253     public JsonGroupAsBehavior getJsonGroupAsBehavior() {
254       return annotation.inJson();
255     }
256 
257     @Override
258     public XmlGroupAsBehavior getXmlGroupAsBehavior() {
259       return annotation.inXml();
260     }
261   }
262 
263 }