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: */
019: package org.apache.axis2.jaxws.message.databinding;
020:
021: import org.apache.axis2.jaxws.i18n.Messages;
022:
023: import javax.xml.datatype.DatatypeConfigurationException;
024: import javax.xml.datatype.XMLGregorianCalendar;
025: import javax.xml.namespace.QName;
026: import java.lang.reflect.Array;
027: import java.lang.reflect.Constructor;
028: import java.lang.reflect.InvocationTargetException;
029: import java.lang.reflect.Method;
030: import java.text.ParseException;
031: import java.text.SimpleDateFormat;
032: import java.util.ArrayList;
033: import java.util.Date;
034: import java.util.GregorianCalendar;
035: import java.util.List;
036: import java.util.StringTokenizer;
037:
038: /** Utilities to convert to/from xsd:list String to Object[]/List values. */
039: public class XSDListUtils {
040: /** Constructor is intentionally private */
041: private XSDListUtils() {
042: }
043:
044: // Example:
045: // <xsd:simpleType name="LongList">
046: // <xsd:list>
047: // <xsd:simpleType>
048: // <xsd:restriction base="xsd:unsignedInt"/>
049: // </xsd:simpleType>
050: // </xsd:list>
051: // </xsd:simpleType>
052: // <element name="myLong" nillable="true" type="impl:LongList"/>
053: //
054: // LongList will be represented as an int[]
055: // On the wire myLong will be represented as a list of integers
056: // with intervening whitespace
057: // <myLong>1 2 3</myLong>
058: //
059: // Unfortunately, sometimes we want to marshal by type. Therefore
060: // we want to marshal an element (foo) that is unknown to schema.
061: // If we use the normal marshal code, the wire will look like
062: // this (which is incorrect):
063: // <foo><item>1</item><item>2</item><item>3</item></foo>
064: //
065: // The solution is to detect this situation and marshal the
066: // String instead. Then we get the correct wire format:
067: // <foo>1 2 3</foo>
068: //
069: // This utility contains code to convert from Array/List -> String
070: // and from String -> Array/List
071:
072: /**
073: * Convert to String that can be used as an xsd:list content
074: *
075: * @param container Object
076: * @return xsd:list String
077: */
078: public static String toXSDListString(Object container)
079: throws NoSuchMethodException, IllegalArgumentException,
080: InstantiationException, IllegalAccessException,
081: InvocationTargetException {
082: // TODO only supports arrays right now. Need to implement this for List
083: if (container != null && container.getClass().isArray()) {
084: String xsdString = "";
085: for (int i = 0; i < Array.getLength(container); i++) {
086: Object component = Array.get(container, i);
087: if (xsdString.length() != 0) {
088: xsdString += " ";
089: }
090: xsdString += getAsText(component);
091: }
092: return xsdString;
093:
094: } else if (container != null
095: && List.class.isAssignableFrom(container.getClass())) {
096: String xsdString = "";
097: List containerAsList = (List) container;
098: for (Object component : containerAsList) {
099: if (xsdString.length() != 0) {
100: xsdString += " ";
101: }
102: xsdString += getAsText(component);
103: }
104: return xsdString;
105:
106: } else {
107: throw new IllegalArgumentException(container.getClass()
108: .toString());
109: }
110: }
111:
112: /**
113: * Convert from xsdListString to an array/list
114: *
115: * @param xsdListString
116: * @param type Class of return
117: * @return Array or List
118: * @throws InvocationTargetException
119: * @throws IllegalAccessException
120: * @throws InstantiationException
121: * @throws NoSuchMethodException
122: * @throws IllegalArgumentException
123: */
124: public static Object fromXSDListString(String xsdListString,
125: Class type) throws IllegalArgumentException,
126: NoSuchMethodException, InstantiationException,
127: IllegalAccessException, InvocationTargetException,
128: ParseException, DatatypeConfigurationException {
129: // TODO only supports arrays right now. Need to implement this for List
130: if (type.isArray()) {
131: Class componentType = type.getComponentType();
132: List list = new ArrayList();
133:
134: // Parse the tokens based on whitespace
135: StringTokenizer st = new StringTokenizer(xsdListString);
136: while (st.hasMoreTokens()) {
137: String text = st.nextToken();
138: Object componentObject = getFromText(text,
139: componentType);
140: list.add(componentObject);
141: }
142: Class arrayType = componentType;
143: if (componentType.isPrimitive()) {
144: Class boxedType = getBoxedType(componentType);
145: if (boxedType != null) {
146: arrayType = boxedType;
147: }
148: }
149: Object array = Array.newInstance(arrayType, list.size());
150: return list.toArray((Object[]) array);
151: } else {
152: throw new IllegalArgumentException(type.toString());
153: }
154: }
155:
156: /**
157: * @param obj
158: * @return xml text for this object
159: */
160: private static String getAsText(Object obj)
161: throws NoSuchMethodException, IllegalArgumentException,
162: InstantiationException, IllegalAccessException,
163: InvocationTargetException {
164: // TODO Need to upgrade to handle more complicated objects like calendar and qname
165: if (obj instanceof QName) {
166: throw new RuntimeException(Messages.getMessage(
167: "XSDListNotSupported", QName.class.getName()));
168: } else if (obj instanceof XMLGregorianCalendar) {
169: throw new RuntimeException(Messages.getMessage(
170: "XSDListNotSupported", XMLGregorianCalendar.class
171: .getName()));
172: } else if (obj.getClass().isEnum()) {
173: // TODO Method should be cached for performance
174: Method method = obj.getClass().getDeclaredMethod("value",
175: new Class[] {});
176: return (String) method.invoke(obj, new Object[] {});
177:
178: }
179: return obj.toString();
180: }
181:
182: /**
183: * @param value
184: * @param componentType
185: * @return Object constructed from the specified xml text (value)
186: * @throws NoSuchMethodException
187: * @throws IllegalArgumentException
188: * @throws InstantiationException
189: * @throws IllegalAccessException
190: * @throws InvocationTargetException
191: */
192: private static Object getFromText(String value, Class componentType)
193: throws NoSuchMethodException, IllegalArgumentException,
194: InstantiationException, IllegalAccessException,
195: InvocationTargetException, ParseException,
196: DatatypeConfigurationException {
197: // TODO This needs to be upgraded to handle more complicated objects (enum, calendar, primitive, etc.)
198: if (componentType == String.class) {
199: return value;
200: }
201: if (componentType.isEnum()) {
202: // If you get an exception here, consider adding the code to convert the String value to the required component object
203: // Default: Call the constructor
204: // TODO Method should be cached for performance
205: Method method = componentType.getDeclaredMethod(
206: "fromValue", new Class[] { String.class });
207: Object obj = method.invoke(null, new Object[] { value });
208: return obj;
209: }
210:
211: if (componentType == byte.class) {
212: componentType = Byte.class;
213: }
214: if (componentType == short.class) {
215: componentType = Short.class;
216: }
217: if (componentType == int.class) {
218: componentType = Integer.class;
219: }
220: if (componentType == float.class) {
221: componentType = Float.class;
222: }
223: if (componentType == double.class) {
224: componentType = Double.class;
225: }
226: if (componentType == char.class) {
227: Character ch = null;
228: if (value != null && value.length() > 0) {
229: ch = new Character(value.charAt(0));
230: }
231: return ch;
232: }
233: if (componentType == boolean.class) {
234: componentType = Boolean.class;
235: }
236:
237: if (componentType.equals(QName.class)) {
238: // TODO Missing Support
239:
240: throw new IllegalArgumentException(Messages.getMessage(
241: "XSDListNotSupported", componentType.getName()));
242: } else if (componentType.equals(XMLGregorianCalendar.class)) {
243: // TODO Missing Support
244: throw new IllegalArgumentException(Messages.getMessage(
245: "XSDListNotSupported", componentType.getName()));
246: }
247:
248: // If you get an exception here, consider adding the code to convert the String value to the required component object
249: // Default: Call the constructor
250: Constructor constructor = componentType
251: .getConstructor(new Class[] { String.class });
252: Object obj = constructor.newInstance(new Object[] { value });
253: return obj;
254: }
255:
256: private static Class getBoxedType(Class primitiveType) {
257: if (primitiveType == byte.class) {
258: return Byte.class;
259: }
260: if (primitiveType == short.class) {
261: return Short.class;
262: }
263: if (primitiveType == int.class) {
264: return Integer.class;
265: }
266: if (primitiveType == long.class) {
267: return Long.class;
268: }
269: if (primitiveType == float.class) {
270: return Float.class;
271: }
272: if (primitiveType == double.class) {
273: return Double.class;
274: }
275: if (primitiveType == char.class) {
276: return Character.class;
277: }
278: if (primitiveType == boolean.class) {
279: return Boolean.class;
280: }
281: return null;
282: }
283:
284: private static GregorianCalendar toGregorianCalendar(String value)
285: throws ParseException {
286: Date d = new SimpleDateFormat().parse(value);
287: GregorianCalendar gc = new GregorianCalendar();
288: gc.setTime(d);
289: return gc;
290: }
291: }
|