001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */package org.apache.cxf.aegis.type.basic;
019:
020: import java.lang.reflect.Array;
021: import java.util.ArrayList;
022: import java.util.Collection;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Set;
027:
028: import javax.xml.namespace.QName;
029:
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032: import org.apache.cxf.aegis.Context;
033: import org.apache.cxf.aegis.DatabindingException;
034: import org.apache.cxf.aegis.type.Type;
035: import org.apache.cxf.aegis.type.TypeUtil;
036: import org.apache.cxf.aegis.util.NamespaceHelper;
037: import org.apache.cxf.aegis.util.XmlConstants;
038: import org.apache.cxf.aegis.xml.MessageReader;
039: import org.apache.cxf.aegis.xml.MessageWriter;
040: import org.jdom.Attribute;
041: import org.jdom.Element;
042: import org.jdom.Namespace;
043:
044: /**
045: * An ArrayType.
046: *
047: * @author <a href="mailto:dan@envoisolutions.com">Dan Diephouse</a>
048: */
049: public class ArrayType extends Type {
050: private static final Log LOG = LogFactory.getLog(ArrayType.class);
051:
052: private QName componentName;
053: private long minOccurs;
054: private long maxOccurs = Long.MAX_VALUE;
055: private boolean flat;
056:
057: public ArrayType() {
058: }
059:
060: @Override
061: public Object readObject(MessageReader reader, Context context)
062: throws DatabindingException {
063: try {
064: Collection values = readCollection(reader, context);
065:
066: return makeArray(getComponentType().getTypeClass(), values);
067: } catch (IllegalArgumentException e) {
068: throw new DatabindingException("Illegal argument.", e);
069: }
070: }
071:
072: protected Collection<Object> createCollection() {
073: return new ArrayList<Object>();
074: }
075:
076: protected Collection readCollection(MessageReader reader,
077: Context context) throws DatabindingException {
078: Collection<Object> values = createCollection();
079:
080: while (reader.hasMoreElementReaders()) {
081: MessageReader creader = reader.getNextElementReader();
082: Type compType = TypeUtil.getReadType(creader
083: .getXMLStreamReader(), context, getComponentType());
084:
085: if (creader.isXsiNil()) {
086: values.add(null);
087: creader.readToEnd();
088: } else {
089: values.add(compType.readObject(creader, context));
090: }
091:
092: // check max occurs
093: int size = values.size();
094: if (size > maxOccurs) {
095: throw new DatabindingException(
096: "The number of elements in " + getSchemaType()
097: + " exceeds the maximum of "
098: + maxOccurs);
099: }
100:
101: }
102:
103: // check min occurs
104: if (values.size() < minOccurs) {
105: throw new DatabindingException("The number of elements in "
106: + getSchemaType()
107: + " does not meet the minimum of " + minOccurs);
108: }
109: return values;
110: }
111:
112: @SuppressWarnings("unchecked")
113: protected Object makeArray(Class arrayType, Collection values) {
114: int i;
115: int n;
116: Object array = null;
117: if (Integer.TYPE.equals(arrayType)) {
118: Object[] objects = values.toArray();
119: array = Array.newInstance(Integer.TYPE, objects.length);
120: for (i = 0, n = objects.length; i < n; i++) {
121: Array.set(array, i, objects[i]);
122: }
123: } else if (Long.TYPE.equals(arrayType)) {
124: Object[] objects = values.toArray();
125: array = Array.newInstance(Long.TYPE, objects.length);
126: for (i = 0, n = objects.length; i < n; i++) {
127: Array.set(array, i, objects[i]);
128: }
129: } else if (Short.TYPE.equals(arrayType)) {
130: Object[] objects = values.toArray();
131: array = Array.newInstance(Short.TYPE, objects.length);
132: for (i = 0, n = objects.length; i < n; i++) {
133: Array.set(array, i, objects[i]);
134: }
135: } else if (Double.TYPE.equals(arrayType)) {
136: Object[] objects = values.toArray();
137: array = Array.newInstance(Double.TYPE, objects.length);
138: for (i = 0, n = objects.length; i < n; i++) {
139: Array.set(array, i, objects[i]);
140: }
141: } else if (Float.TYPE.equals(arrayType)) {
142: Object[] objects = values.toArray();
143: array = Array.newInstance(Float.TYPE, objects.length);
144: for (i = 0, n = objects.length; i < n; i++) {
145: Array.set(array, i, objects[i]);
146: }
147: } else if (Byte.TYPE.equals(arrayType)) {
148: Object[] objects = values.toArray();
149: array = Array.newInstance(Byte.TYPE, objects.length);
150: for (i = 0, n = objects.length; i < n; i++) {
151: Array.set(array, i, objects[i]);
152: }
153: } else if (Boolean.TYPE.equals(arrayType)) {
154: Object[] objects = values.toArray();
155: array = Array.newInstance(Boolean.TYPE, objects.length);
156: for (i = 0, n = objects.length; i < n; i++) {
157: Array.set(array, i, objects[i]);
158: }
159: } else if (Character.TYPE.equals(arrayType)) {
160: Object[] objects = values.toArray();
161: array = Array.newInstance(Character.TYPE, objects.length);
162: for (i = 0, n = objects.length; i < n; i++) {
163: Array.set(array, i, objects[i]);
164: }
165: }
166: return array == null ? values.toArray((Object[]) Array
167: .newInstance(getComponentType().getTypeClass(), values
168: .size())) : array;
169: }
170:
171: @Override
172: public void writeObject(Object values, MessageWriter writer,
173: Context context) throws DatabindingException {
174: if (values == null) {
175: return;
176: }
177:
178: Type type = getComponentType();
179:
180: String ns = null;
181: if (type.isAbstract()) {
182: ns = getSchemaType().getNamespaceURI();
183: } else {
184: ns = type.getSchemaType().getNamespaceURI();
185: }
186:
187: String name = type.getSchemaType().getLocalPart();
188:
189: if (type == null) {
190: throw new DatabindingException("Couldn't find type for "
191: + type.getTypeClass() + ".");
192: }
193:
194: Class arrayType = type.getTypeClass();
195:
196: int i;
197: int n;
198: if (Object.class.isAssignableFrom(arrayType)) {
199: Object[] objects = (Object[]) values;
200: for (i = 0, n = objects.length; i < n; i++) {
201: writeValue(objects[i], writer, context, type, name, ns);
202: }
203: } else if (Integer.TYPE.equals(arrayType)) {
204: int[] objects = (int[]) values;
205: for (i = 0, n = objects.length; i < n; i++) {
206: writeValue(new Integer(objects[i]), writer, context,
207: type, name, ns);
208: }
209: } else if (Long.TYPE.equals(arrayType)) {
210: long[] objects = (long[]) values;
211: for (i = 0, n = objects.length; i < n; i++) {
212: writeValue(new Long(objects[i]), writer, context, type,
213: name, ns);
214: }
215: } else if (Short.TYPE.equals(arrayType)) {
216: short[] objects = (short[]) values;
217: for (i = 0, n = objects.length; i < n; i++) {
218: writeValue(new Short(objects[i]), writer, context,
219: type, name, ns);
220: }
221: } else if (Double.TYPE.equals(arrayType)) {
222: double[] objects = (double[]) values;
223: for (i = 0, n = objects.length; i < n; i++) {
224: writeValue(new Double(objects[i]), writer, context,
225: type, name, ns);
226: }
227: } else if (Float.TYPE.equals(arrayType)) {
228: float[] objects = (float[]) values;
229: for (i = 0, n = objects.length; i < n; i++) {
230: writeValue(new Float(objects[i]), writer, context,
231: type, name, ns);
232: }
233: } else if (Byte.TYPE.equals(arrayType)) {
234: byte[] objects = (byte[]) values;
235: for (i = 0, n = objects.length; i < n; i++) {
236: writeValue(new Byte(objects[i]), writer, context, type,
237: name, ns);
238: }
239: } else if (Boolean.TYPE.equals(arrayType)) {
240: boolean[] objects = (boolean[]) values;
241: for (i = 0, n = objects.length; i < n; i++) {
242: writeValue(Boolean.valueOf(objects[i]), writer,
243: context, type, name, ns);
244: }
245: } else if (Character.TYPE.equals(arrayType)) {
246: char[] objects = (char[]) values;
247: for (i = 0, n = objects.length; i < n; i++) {
248: writeValue(new Character(objects[i]), writer, context,
249: type, name, ns);
250: }
251: }
252: }
253:
254: protected void writeValue(Object value, MessageWriter writer,
255: Context context, Type type, String name, String ns)
256: throws DatabindingException {
257: type = TypeUtil.getWriteType(context, value, type);
258: MessageWriter cwriter;
259: if (type.isWriteOuter()) {
260: cwriter = writer.getElementWriter(name, ns);
261: } else {
262: cwriter = writer;
263: }
264:
265: if (value == null && type.isNillable()) {
266: cwriter.writeXsiNil();
267: } else {
268: type.writeObject(value, cwriter, context);
269: }
270:
271: cwriter.close();
272: }
273:
274: @Override
275: public void writeSchema(Element root) {
276: try {
277: if (hasDefinedArray(root)) {
278: return;
279: }
280:
281: Element complex = new Element("complexType",
282: XmlConstants.XSD_PREFIX, XmlConstants.XSD);
283: complex.setAttribute(new Attribute("name", getSchemaType()
284: .getLocalPart()));
285: root.addContent(complex);
286:
287: Element seq = new Element("sequence",
288: XmlConstants.XSD_PREFIX, XmlConstants.XSD);
289: complex.addContent(seq);
290:
291: Element element = new Element("element",
292: XmlConstants.XSD_PREFIX, XmlConstants.XSD);
293: seq.addContent(element);
294:
295: Type componentType = getComponentType();
296: String prefix = NamespaceHelper.getUniquePrefix(root,
297: componentType.getSchemaType().getNamespaceURI());
298:
299: String typeName = prefix + ":"
300: + componentType.getSchemaType().getLocalPart();
301:
302: element.setAttribute(new Attribute("name", componentType
303: .getSchemaType().getLocalPart()));
304: element.setAttribute(new Attribute("type", typeName));
305:
306: if (componentType.isNillable()) {
307: element.setAttribute(new Attribute("nillable", "true"));
308: }
309:
310: element.setAttribute(new Attribute("minOccurs", Long
311: .valueOf(getMinOccurs()).toString()));
312:
313: if (maxOccurs == Long.MAX_VALUE) {
314: element.setAttribute(new Attribute("maxOccurs",
315: "unbounded"));
316: } else {
317: element.setAttribute(new Attribute("maxOccurs", Long
318: .valueOf(getMaxOccurs()).toString()));
319: }
320:
321: } catch (IllegalArgumentException e) {
322: throw new DatabindingException("Illegal argument.", e);
323: }
324: }
325:
326: /**
327: * Since both an Array and a List can have the same type definition, double check
328: * that there isn't already a defined type already.
329: * @param root
330: * @return
331: */
332: private boolean hasDefinedArray(Element root) {
333: List children = root.getChildren("complexType", Namespace
334: .getNamespace(XmlConstants.XSD));
335: for (Iterator itr = children.iterator(); itr.hasNext();) {
336: Element e = (Element) itr.next();
337:
338: if (e.getAttributeValue("name").equals(
339: getSchemaType().getLocalPart())) {
340: return true;
341: }
342: }
343: return false;
344: }
345:
346: /**
347: * We need to write a complex type schema for Beans, so return true.
348: *
349: * @see org.apache.cxf.aegis.type.Type#isComplex()
350: */
351: @Override
352: public boolean isComplex() {
353: return true;
354: }
355:
356: public QName getComponentName() {
357: return componentName;
358: }
359:
360: public void setComponentName(QName componentName) {
361: this .componentName = componentName;
362: }
363:
364: /**
365: * @see org.apache.cxf.aegis.type.Type#getDependencies()
366: */
367: @Override
368: public Set<Type> getDependencies() {
369: Set<Type> deps = new HashSet<Type>();
370:
371: deps.add(getComponentType());
372:
373: return deps;
374: }
375:
376: /**
377: * Get the <code>Type</code> of the elements in the array.
378: *
379: * @return
380: */
381: public Type getComponentType() {
382: Class compType = getTypeClass().getComponentType();
383:
384: Type type;
385:
386: if (componentName == null) {
387: type = getTypeMapping().getType(compType);
388: } else {
389: type = getTypeMapping().getType(componentName);
390:
391: // We couldn't find the type the user specified. One is created
392: // below instead.
393: if (type == null) {
394: LOG.debug("Couldn't find array component type "
395: + componentName + ". Creating one instead.");
396: }
397: }
398:
399: if (type == null) {
400: type = getTypeMapping().getTypeCreator().createType(
401: compType);
402: getTypeMapping().register(type);
403: }
404:
405: return type;
406: }
407:
408: public long getMaxOccurs() {
409: return maxOccurs;
410: }
411:
412: public void setMaxOccurs(long maxOccurs) {
413: this .maxOccurs = maxOccurs;
414: }
415:
416: public long getMinOccurs() {
417: return minOccurs;
418: }
419:
420: public void setMinOccurs(long minOccurs) {
421: this .minOccurs = minOccurs;
422: }
423:
424: public boolean isFlat() {
425: return flat;
426: }
427:
428: public void setFlat(boolean flat) {
429: setWriteOuter(!flat);
430: this.flat = flat;
431: }
432: }
|