1   
2   
3   
4   
5   
6   package gov.nist.secauto.metaschema.databind.model.info;
7   
8   import gov.nist.secauto.metaschema.core.model.IBoundObject;
9   import gov.nist.secauto.metaschema.core.model.JsonGroupAsBehavior;
10  import gov.nist.secauto.metaschema.databind.io.BindingException;
11  import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModel;
12  
13  import java.io.IOException;
14  import java.lang.reflect.Field;
15  import java.lang.reflect.ParameterizedType;
16  import java.lang.reflect.Type;
17  import java.util.Collection;
18  import java.util.List;
19  import java.util.Map;
20  
21  import edu.umd.cs.findbugs.annotations.NonNull;
22  import edu.umd.cs.findbugs.annotations.Nullable;
23  
24  
25  public interface IModelInstanceCollectionInfo<ITEM> {
26  
27    @SuppressWarnings("PMD.ShortMethodName")
28    @NonNull
29    static <T> IModelInstanceCollectionInfo<T> of(
30        @NonNull IBoundInstanceModel<T> instance) {
31  
32      
33      Type type = instance.getType();
34      Field field = instance.getField();
35  
36      IModelInstanceCollectionInfo<T> retval;
37      if (instance.getMaxOccurs() == -1 || instance.getMaxOccurs() > 1) {
38        
39        JsonGroupAsBehavior jsonGroupAs = instance.getJsonGroupAsBehavior();
40  
41        
42        if (!(type instanceof ParameterizedType)) {
43          switch (jsonGroupAs) {
44          case KEYED:
45            throw new IllegalStateException(
46                String.format("The field '%s' on class '%s' has data type of '%s'," + " but should have a type of '%s'.",
47                    field.getName(),
48                    field.getDeclaringClass().getName(),
49                    field.getType().getName(), Map.class.getName()));
50          case LIST:
51          case SINGLETON_OR_LIST:
52            throw new IllegalStateException(
53                String.format("The field '%s' on class '%s' has data type of '%s'," + " but should have a type of '%s'.",
54                    field.getName(),
55                    field.getDeclaringClass().getName(),
56                    field.getType().getName(), List.class.getName()));
57          default:
58            
59            throw new IllegalStateException(jsonGroupAs.name());
60          }
61        }
62  
63        Class<?> rawType = (Class<?>) ((ParameterizedType) type).getRawType();
64        if (JsonGroupAsBehavior.KEYED.equals(jsonGroupAs)) {
65          if (!Map.class.isAssignableFrom(rawType)) {
66            throw new IllegalArgumentException(String.format(
67                "The field '%s' on class '%s' has data type '%s', which is not the expected '%s' derived data type.",
68                field.getName(),
69                field.getDeclaringClass().getName(),
70                field.getType().getName(),
71                Map.class.getName()));
72          }
73          retval = new MapCollectionInfo<>(instance);
74        } else {
75          if (!List.class.isAssignableFrom(rawType)) {
76            throw new IllegalArgumentException(String.format(
77                "The field '%s' on class '%s' has data type '%s', which is not the expected '%s' derived data type.",
78                field.getName(),
79                field.getDeclaringClass().getName(),
80                field.getType().getName(),
81                List.class.getName()));
82          }
83          retval = new ListCollectionInfo<>(instance);
84        }
85      } else {
86        
87        if (type instanceof ParameterizedType) {
88          throw new IllegalStateException(String.format(
89              "The field '%s' on class '%s' has a data parmeterized type of '%s',"
90                  + " but the occurance is not multi-valued.",
91              field.getName(),
92              field.getDeclaringClass().getName(),
93              field.getType().getName()));
94        }
95        retval = new SingletonCollectionInfo<>(instance);
96      }
97      return retval;
98    }
99  
100   
101 
102 
103 
104 
105   @NonNull
106   IBoundInstanceModel<ITEM> getInstance();
107 
108   
109 
110 
111 
112 
113 
114 
115   int size(@Nullable Object value);
116 
117   
118 
119 
120 
121 
122 
123 
124 
125   boolean isEmpty(@Nullable Object value);
126 
127   
128 
129 
130 
131 
132   @NonNull
133   Class<? extends ITEM> getItemType();
134 
135   @NonNull
136   default Collection<? extends ITEM> getItemsFromParentInstance(@NonNull Object parentInstance) {
137     Object value = getInstance().getValue(parentInstance);
138     return getItemsFromValue(value);
139   }
140 
141   @NonNull
142   Collection<? extends ITEM> getItemsFromValue(Object value);
143 
144   Object emptyValue();
145 
146   Object deepCopyItems(@NonNull IBoundObject fromObject, @NonNull IBoundObject toObject) throws BindingException;
147 
148   
149 
150 
151 
152 
153 
154 
155 
156 
157 
158 
159 
160   @Nullable
161   Object readItems(@NonNull IModelInstanceReadHandler<ITEM> handler) throws IOException;
162 
163   void writeItems(
164       @NonNull IModelInstanceWriteHandler<ITEM> handler,
165       @NonNull Object value) throws IOException;
166 }