blob: c8c1b3e893a3f7b949737842eee8a56a88c2a38d [file] [log] [blame]
/*
* 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.jaxws.utility;
import org.apache.axis2.jaxws.ExceptionFactory;
import org.apache.axis2.jaxws.i18n.Messages;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.activation.DataHandler;
import javax.imageio.ImageIO;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.WebServiceException;
import java.awt.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* Provides utilities to convert an object into a different kind of Object. For example, convert a
* String[] into a List<String>
*/
public class ConvertUtils {
private static final Log log = LogFactory.getLog(ConvertUtils.class);
/**
* This method should return true if the convert method will succeed.
* <p/>
* Note that any changes to isConvertable() must also be accompanied by similar changes to
* convert()
*
* @param obj source object or class
* @param dest destination class
* @return boolean true if convert(..) can convert obj to the destination class
*/
public static boolean isConvertable(Object obj, Class dest) {
Class src = null;
if (obj != null) {
if (obj instanceof Class) {
src = (Class)obj;
} else {
src = obj.getClass();
}
}
if (dest == null) {
return false;
}
if (src == null) {
return true;
}
// If we're directly assignable, we're good.
if (dest.isAssignableFrom(src)) {
return true;
}
// If it's a wrapping conversion, we're good.
if (JavaUtils.getWrapperClass(src) == dest) {
return true;
}
if (JavaUtils.getWrapperClass(dest) == src) {
return true;
}
// If it's List -> Array or vice versa, we're good.
if ((Collection.class.isAssignableFrom(src) || src.isArray()) &&
(Collection.class.isAssignableFrom(dest) || dest.isArray())) {
// TODO this should consider the component types instead of returning true.
return true;
}
// Allow mapping of HashMaps to Hashtables
if (src == HashMap.class && dest == Hashtable.class)
return true;
// Allow mapping of Calendar to Date
if (Calendar.class.isAssignableFrom(src) && dest == Date.class) {
return true;
}
if (src.isPrimitive()) {
return isConvertable(JavaUtils.getWrapperClass(src), dest);
}
if (InputStream.class.isAssignableFrom(src) && dest == byte[].class) {
return true;
}
if (Source.class.isAssignableFrom(src) && dest == byte[].class) {
return true;
}
if (DataHandler.class.isAssignableFrom(src) && isConvertable(byte[].class, dest)) {
return true;
}
if (DataHandler.class.isAssignableFrom(src) && dest == Image.class) {
return true;
}
if (DataHandler.class.isAssignableFrom(src) && dest == Source.class) {
return true;
}
if (byte[].class.isAssignableFrom(src) && dest == String.class) {
return true;
}
// If it's a MIME type mapping and we want a DataHandler,
// then we're good.
// REVIEW Do we want to support this
/*
if (dest.getName().equals("javax.activation.DataHandler")) {
String name = src.getName();
if (src == String.class
|| src == java.awt.Image.class
|| name.equals("javax.mail.internet.MimeMultipart")
|| name.equals("javax.xml.transform.Source"))
return true;
}
*/
return false;
}
/**
* Utility function to convert an Object to some desired Class.
* <p/>
* Normally this is used for T[] to List<T> processing. Other conversions are also done (i.e.
* HashMap <->Hashtable, etc.)
* <p/>
* Use the isConvertable() method to determine if conversion is possible. Note that any changes
* to convert() must also be accompanied by similar changes to isConvertable()
*
* @param arg the array to convert
* @param destClass the actual class we want
* @return object of destClass if conversion possible, otherwise returns arg
*/
public static Object convert(Object arg, Class destClass) throws WebServiceException {
if (destClass == null) {
return arg;
}
if (arg != null && destClass.isAssignableFrom(arg.getClass())) {
return arg;
}
if (log.isDebugEnabled()) {
String clsName = "null";
if (arg != null) clsName = arg.getClass().getName();
log.debug("Converting an object of type " + clsName + " to an object of type " +
destClass.getName());
}
// Convert between Calendar and Date
if (arg instanceof Calendar && destClass == Date.class) {
return ((Calendar)arg).getTime();
}
// Convert between HashMap and Hashtable
if (arg instanceof HashMap && destClass == Hashtable.class) {
return new Hashtable((HashMap)arg);
}
if (arg instanceof InputStream && destClass == byte[].class) {
try {
InputStream is = (InputStream) arg;
return getBytesFromStream(is);
} catch (IOException e) {
throw ExceptionFactory.makeWebServiceException(e);
}
}
if (arg instanceof Source && destClass == byte[].class) {
try {
if (arg instanceof StreamSource) {
InputStream is = ((StreamSource) arg).getInputStream();
if (is != null) {
return getBytesFromStream(is);
}
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
Result result = new StreamResult(out);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform((Source) arg, result);
byte[] bytes = out.toByteArray();
return bytes;
} catch (Exception e) {
throw ExceptionFactory.makeWebServiceException(e);
}
}
if (arg instanceof DataHandler) {
try {
InputStream is = ((DataHandler) arg).getInputStream();
if (destClass == Image.class) {
return ImageIO.read(is);
} else if (destClass == Source.class) {
return new StreamSource(is);
}
byte[] bytes = getBytesFromStream(is);
return convert(bytes, destClass);
} catch (Exception e) {
throw ExceptionFactory.makeWebServiceException(e);
}
}
if (arg instanceof byte[] && destClass == String.class) {
return new String((byte[]) arg);
}
// If the destination is an array and the source
// is a suitable component, return an array with
// the single item.
/* REVIEW do we need to support atomic to array conversion ?
if (arg != null &&
destClass.isArray() &&
!destClass.getComponentType().equals(Object.class) &&
destClass.getComponentType().isAssignableFrom(arg.getClass())) {
Object array =
Array.newInstance(destClass.getComponentType(), 1);
Array.set(array, 0, arg);
return array;
}
*/
// Return if no conversion is available
if (!(arg instanceof Collection ||
(arg != null && arg.getClass().isArray()))) {
return arg;
}
if (arg == null) {
return null;
}
// The arg may be an array or List
Object destValue = null;
int length = 0;
if (arg.getClass().isArray()) {
length = Array.getLength(arg);
} else {
length = ((Collection)arg).size();
}
try {
if (destClass.isArray()) {
if (destClass.getComponentType().isPrimitive()) {
Object array = Array.newInstance(destClass.getComponentType(),
length);
// Assign array elements
if (arg.getClass().isArray()) {
for (int i = 0; i < length; i++) {
Array.set(array, i, Array.get(arg, i));
}
} else {
int idx = 0;
for (Iterator i = ((Collection)arg).iterator();
i.hasNext();) {
Array.set(array, idx++, i.next());
}
}
destValue = array;
} else {
Object [] array;
try {
array = (Object [])Array.newInstance(destClass.getComponentType(),
length);
} catch (Exception e) {
return arg;
}
// Use convert to assign array elements.
if (arg.getClass().isArray()) {
for (int i = 0; i < length; i++) {
array[i] = convert(Array.get(arg, i),
destClass.getComponentType());
}
} else {
int idx = 0;
for (Iterator i = ((Collection)arg).iterator();
i.hasNext();) {
array[idx++] = convert(i.next(),
destClass.getComponentType());
}
}
destValue = array;
}
} else if (Collection.class.isAssignableFrom(destClass)) {
Collection newList = null;
try {
// if we are trying to create an interface, build something
// that implements the interface
if (destClass == Collection.class || destClass == List.class) {
newList = new ArrayList();
} else if (destClass == Set.class) {
newList = new HashSet();
} else {
newList = (Collection)destClass.newInstance();
}
} catch (Exception e) {
// No FFDC code needed
// Couldn't build one for some reason... so forget it.
return arg;
}
if (arg.getClass().isArray()) {
for (int j = 0; j < length; j++) {
newList.add(Array.get(arg, j));
}
} else {
for (Iterator j = ((Collection)arg).iterator();
j.hasNext();) {
newList.add(j.next());
}
}
destValue = newList;
} else {
destValue = arg;
}
} catch (Throwable t) {
throw ExceptionFactory.
makeWebServiceException(
Messages.getMessage("convertUtils",
arg.getClass().toString(),
destClass.toString()),
t);
}
return destValue;
}
private static byte[] getBytesFromStream(InputStream is) throws IOException {
// TODO This code assumes that available is the length of the stream.
byte[] bytes = new byte[is.available()];
is.read(bytes);
return bytes;
}
}