1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package gov.nist.secauto.metaschema.databind.model.info;
28
29 import com.fasterxml.jackson.core.JsonGenerator;
30 import com.fasterxml.jackson.core.JsonParser;
31 import com.fasterxml.jackson.core.JsonToken;
32
33 import gov.nist.secauto.metaschema.core.model.JsonGroupAsBehavior;
34 import gov.nist.secauto.metaschema.core.model.util.JsonUtil;
35 import gov.nist.secauto.metaschema.core.model.util.XmlEventUtil;
36 import gov.nist.secauto.metaschema.core.util.CollectionUtil;
37 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
38 import gov.nist.secauto.metaschema.databind.io.BindingException;
39 import gov.nist.secauto.metaschema.databind.io.json.IJsonParsingContext;
40 import gov.nist.secauto.metaschema.databind.io.json.IJsonWritingContext;
41 import gov.nist.secauto.metaschema.databind.io.xml.IXmlParsingContext;
42 import gov.nist.secauto.metaschema.databind.io.xml.IXmlWritingContext;
43 import gov.nist.secauto.metaschema.databind.model.IBoundNamedModelInstance;
44
45 import org.codehaus.stax2.XMLEventReader2;
46
47 import java.io.IOException;
48 import java.lang.reflect.ParameterizedType;
49 import java.util.List;
50
51 import javax.xml.namespace.QName;
52 import javax.xml.stream.XMLStreamException;
53 import javax.xml.stream.events.StartElement;
54 import javax.xml.stream.events.XMLEvent;
55
56 import edu.umd.cs.findbugs.annotations.NonNull;
57
58 class ListPropertyInfo
59 extends AbstractModelPropertyInfo {
60
61 public ListPropertyInfo(
62 @NonNull IBoundNamedModelInstance property) {
63 super(property);
64 }
65
66 @Override
67 public Class<?> getItemType() {
68 ParameterizedType actualType = (ParameterizedType) getProperty().getType();
69
70 return ObjectUtils.notNull((Class<?>) actualType.getActualTypeArguments()[0]);
71 }
72
73 @Override
74 public ListPropertyCollector newPropertyCollector() {
75 return new ListPropertyCollector();
76 }
77
78 @Override
79 public List<? extends Object> getItemsFromParentInstance(Object parentInstance) {
80 Object value = getProperty().getValue(parentInstance);
81 return getItemsFromValue(value);
82 }
83
84 @Override
85 public List<? extends Object> getItemsFromValue(Object value) {
86 return value == null ? CollectionUtil.emptyList() : (List<?>) value;
87 }
88
89 @Override
90 public int getItemCount(Object value) {
91 return value == null ? 0 : ((List<?>) value).size();
92 }
93
94 @Override
95 public boolean readValues(
96 IPropertyCollector collector,
97 Object parentInstance,
98 StartElement start,
99 IXmlParsingContext context) throws IOException, XMLStreamException {
100 XMLEventReader2 eventReader = context.getReader();
101
102
103
104 XmlEventUtil.skipWhitespace(eventReader);
105
106 QName expectedFieldItemQName = getProperty().getXmlQName();
107
108 boolean handled = false;
109 XMLEvent event;
110
111 while ((event = eventReader.peek()).isStartElement()
112 && expectedFieldItemQName.equals(event.asStartElement().getName())) {
113
114 Object value = context.readModelInstanceValue(getProperty(), parentInstance, start);
115 if (value != null) {
116 collector.add(value);
117 handled = true;
118 }
119
120
121 XmlEventUtil.skipWhitespace(eventReader);
122 }
123
124 return handled;
125 }
126
127 @SuppressWarnings({
128 "resource",
129 "PMD.ImplicitSwitchFallThrough"
130 })
131
132 @Override
133 public void readValues(
134 IPropertyCollector collector,
135 Object parentInstance,
136 IJsonParsingContext context)
137 throws IOException {
138 JsonParser parser = context.getReader();
139
140 switch (parser.currentToken()) {
141 case START_ARRAY: {
142
143 JsonUtil.assertAndAdvance(parser, JsonToken.START_ARRAY);
144
145
146 while (!JsonToken.END_ARRAY.equals(parser.currentToken())) {
147 Object value = getProperty().getDataTypeHandler().readItem(parentInstance, context);
148 collector.add(value);
149 }
150
151
152 JsonUtil.assertAndAdvance(parser, JsonToken.END_ARRAY);
153 break;
154 }
155 case VALUE_NULL: {
156 JsonUtil.assertAndAdvance(parser, JsonToken.VALUE_NULL);
157 break;
158 }
159 default:
160
161 Object value = getProperty().getDataTypeHandler().readItem(parentInstance, context);
162 collector.add(value);
163 }
164 }
165
166 @Override
167 public void writeValues(Object value, QName parentName, IXmlWritingContext context)
168 throws XMLStreamException, IOException {
169 IBoundNamedModelInstance property = getProperty();
170 List<? extends Object> items = getItemsFromValue(value);
171 for (Object item : items) {
172 context.writeInstanceValue(property, ObjectUtils.requireNonNull(item), parentName);
173 }
174 }
175
176 @Override
177 public void writeValues(Object parentInstance, IJsonWritingContext context) throws IOException {
178 List<? extends Object> items = getItemsFromParentInstance(parentInstance);
179
180 @SuppressWarnings("resource")
181 JsonGenerator writer = context.getWriter();
182
183 boolean writeArray = false;
184 if (JsonGroupAsBehavior.LIST.equals(getProperty().getJsonGroupAsBehavior())
185 || JsonGroupAsBehavior.SINGLETON_OR_LIST.equals(getProperty().getJsonGroupAsBehavior()) && items.size() > 1) {
186
187 writeArray = true;
188 writer.writeStartArray();
189 }
190
191 for (Object targetObject : items) {
192 assert targetObject != null;
193 getProperty().getDataTypeHandler().writeItem(targetObject, context);
194 }
195
196 if (writeArray) {
197
198 writer.writeEndArray();
199 }
200 }
201
202 @Override
203 public boolean isValueSet(Object parentInstance) throws IOException {
204 List<? extends Object> items = getItemsFromParentInstance(parentInstance);
205 return !items.isEmpty();
206 }
207
208 @Override
209 public void copy(@NonNull Object fromInstance, @NonNull Object toInstance, @NonNull IPropertyCollector collector)
210 throws BindingException {
211 IBoundNamedModelInstance property = getProperty();
212
213 for (Object item : getItemsFromParentInstance(fromInstance)) {
214 collector.add(property.copyItem(ObjectUtils.requireNonNull(item), toInstance));
215 }
216 }
217 }