/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.axis2.datasource.jaxb; import org.apache.axis2.jaxws.i18n.Messages; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.StringTokenizer; /** Utilities to convert to/from xsd:list String to Object[]/List values. */ public class XSDListUtils { /** Constructor is intentionally private */ private XSDListUtils() { } // Example: // <xsd:simpleType name="LongList"> // <xsd:list> // <xsd:simpleType> // <xsd:restriction base="xsd:unsignedInt"/> // </xsd:simpleType> // </xsd:list> // </xsd:simpleType> // <element name="myLong" nillable="true" type="impl:LongList"/> // // LongList will be represented as an int[] // On the wire myLong will be represented as a list of integers // with intervening whitespace // <myLong>1 2 3</myLong> // // Unfortunately, sometimes we want to marshal by type. Therefore // we want to marshal an element (foo) that is unknown to schema. // If we use the normal marshal code, the wire will look like // this (which is incorrect): // <foo><item>1</item><item>2</item><item>3</item></foo> // // The solution is to detect this situation and marshal the // String instead. Then we get the correct wire format: // <foo>1 2 3</foo> // // This utility contains code to convert from Array/List -> String // and from String -> Array/List /** * Convert to String that can be used as an xsd:list content * * @param container Object * @return xsd:list String */ public static String toXSDListString(Object container) throws NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { // TODO only supports arrays right now. Need to implement this for List if (container !=null && container.getClass().isArray()) { String xsdString = ""; for (int i = 0; i < Array.getLength(container); i++) { Object component = Array.get(container, i); if (xsdString.length() != 0) { xsdString += " "; } xsdString += getAsText(component); } return xsdString; } else if(container!=null && List.class.isAssignableFrom(container.getClass())){ String xsdString = ""; List containerAsList = (List)container; for (Object component:containerAsList) { if (xsdString.length() != 0) { xsdString += " "; } xsdString += getAsText(component); } return xsdString; } else { throw new IllegalArgumentException(container.getClass().toString()); } } /** * Convert from xsdListString to an array/list * * @param xsdListString * @param type Class of return * @return Array or List * @throws InvocationTargetException * @throws IllegalAccessException * @throws InstantiationException * @throws NoSuchMethodException * @throws IllegalArgumentException */ public static Object fromXSDListString(String xsdListString, Class type) throws IllegalArgumentException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, ParseException, DatatypeConfigurationException { // TODO only supports arrays right now. Need to implement this for List if (type.isArray()) { Class componentType = type.getComponentType(); List list = new ArrayList(); // Parse the tokens based on whitespace StringTokenizer st = new StringTokenizer(xsdListString); while (st.hasMoreTokens()) { String text = st.nextToken(); Object componentObject = getFromText(text, componentType); list.add(componentObject); } Class arrayType = componentType; if (componentType.isPrimitive()) { Class boxedType = getBoxedType(componentType); if (boxedType != null) { arrayType = boxedType; } } Object array = Array.newInstance(arrayType, list.size()); return list.toArray((Object[])array); }else { throw new IllegalArgumentException(type.toString()); } } public static Object fromStringArray(String[] items, Class type) throws Exception { if (type.isArray()) { Class componentType = type.getComponentType(); List list = new ArrayList(); for (String item : items) { Object componentObject = getFromText(item, componentType); list.add(componentObject); } Class arrayType = componentType; if (componentType.isPrimitive()) { Class boxedType = getBoxedType(componentType); if (boxedType != null) { arrayType = boxedType; } } Object array = Array.newInstance(arrayType, list.size()); return list.toArray((Object[])array); } else { throw new IllegalArgumentException(type.toString()); } } public static String[] toStringArraay(Object container) throws Exception { if (container != null && container.getClass().isArray()) { int size = Array.getLength(container); String [] strArray = new String[size]; for (int i = 0; i < size; i++) { Object component = Array.get(container, i); strArray[i] = getAsText(component); } return strArray; } else if(container != null && List.class.isAssignableFrom(container.getClass())){ List containerAsList = (List)container; int size = containerAsList.size(); String [] strArray = new String[size]; for (int i = 0; i < size; i++) { strArray[i] = getAsText(containerAsList.get(i)); } return strArray; } else { throw new IllegalArgumentException(container.getClass().toString()); } } /** * @param obj * @return xml text for this object */ private static String getAsText(Object obj) throws NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { // TODO Need to upgrade to handle more complicated objects like calendar and qname if (obj instanceof QName) { throw new RuntimeException( Messages.getMessage("XSDListNotSupported", QName.class.getName())); } else if (obj instanceof XMLGregorianCalendar) { throw new RuntimeException(Messages.getMessage("XSDListNotSupported", XMLGregorianCalendar.class.getName())); } else if (obj.getClass().isEnum()) { // TODO Method should be cached for performance Method method = obj.getClass().getDeclaredMethod("value", new Class[] { }); return (String)method.invoke(obj, new Object[] { }); } return obj.toString(); } /** * @param value * @param componentType * @return Object constructed from the specified xml text (value) * @throws NoSuchMethodException * @throws IllegalArgumentException * @throws InstantiationException * @throws IllegalAccessException * @throws InvocationTargetException */ private static Object getFromText(String value, Class componentType) throws NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, ParseException, DatatypeConfigurationException { // TODO This needs to be upgraded to handle more complicated objects (enum, calendar, primitive, etc.) if (componentType == String.class) { return value; } if (componentType.isEnum()) { // If you get an exception here, consider adding the code to convert the String value to the required component object // Default: Call the constructor // TODO Method should be cached for performance Method method = componentType.getDeclaredMethod("fromValue", new Class[] { String.class }); Object obj = method.invoke(null, new Object[] { value }); return obj; } if (componentType == byte.class) { componentType = Byte.class; } if (componentType == short.class) { componentType = Short.class; } if (componentType == int.class) { componentType = Integer.class; } if (componentType == float.class) { componentType = Float.class; } if (componentType == double.class) { componentType = Double.class; } if (componentType == char.class) { Character ch = null; if (value != null && value.length() > 0) { ch = Character.valueOf(value.charAt(0)); } return ch; } if (componentType == boolean.class) { componentType = Boolean.class; } if (componentType.equals(QName.class)) { // TODO Missing Support throw new IllegalArgumentException( Messages.getMessage("XSDListNotSupported", componentType.getName())); } else if (componentType.equals(XMLGregorianCalendar.class)) { // TODO Missing Support throw new IllegalArgumentException( Messages.getMessage("XSDListNotSupported", componentType.getName())); } // If you get an exception here, consider adding the code to convert the String value to the required component object // Default: Call the constructor Constructor constructor = componentType.getConstructor(new Class[] { String.class }); Object obj = constructor.newInstance(new Object[] { value }); return obj; } private static Class getBoxedType(Class primitiveType) { if (primitiveType == byte.class) { return Byte.class; } if (primitiveType == short.class) { return Short.class; } if (primitiveType == int.class) { return Integer.class; } if (primitiveType == long.class) { return Long.class; } if (primitiveType == float.class) { return Float.class; } if (primitiveType == double.class) { return Double.class; } if (primitiveType == char.class) { return Character.class; } if (primitiveType == boolean.class) { return Boolean.class; } return null; } private static GregorianCalendar toGregorianCalendar(String value) throws ParseException { Date d = new SimpleDateFormat().parse(value); GregorianCalendar gc = new GregorianCalendar(); gc.setTime(d); return gc; } }