blob: b9ad5c1743d6d2d75ec739789224c12c0e05145d [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.description.builder;
import org.apache.axis2.java.security.AccessController;
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.xml.namespace.QName;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
/**
*
*/
class DescriptionBuilderUtils {
private static final Log log = LogFactory.getLog(DescriptionBuilderUtils.class);
static String JAXWS_HOLDER_CLASS = "javax.xml.ws.Holder";
private static final String INT_PRIMITIVE = "int";
private static final String INT_PRIMITIVE_ENCODING = "I";
private static final String BYTE_PRIMITIVE = "byte";
private static final String BYTE_PRIMITIVE_ENCODING = "B";
private static final String CHAR_PRIMITIVE = "char";
private static final String CHAR_PRIMITIVE_ENCODING = "C";
private static final String SHORT_PRIMITIVE = "short";
private static final String SHORT_PRIMITIVE_ENCODING = "S";
private static final String BOOLEAN_PRIMITIVE = "boolean";
private static final String BOOLEAN_PRIMITIVE_ENCODING = "Z";
private static final String LONG_PRIMITIVE = "long";
private static final String LONG_PRIMITIVE_ENCODING = "J";
private static final String FLOAT_PRIMITIVE = "float";
private static final String FLOAT_PRIMITIVE_ENCODING = "F";
private static final String DOUBLE_PRIMITIVE = "double";
private static final String DOUBLE_PRIMITIVE_ENCODING = "D";
private static final String VOID_PRIMITIVE = "void";
// REVIEW: This may not be the correct encoding for Void
private static final String VOID_PRIMITIVE_ENCODING = "V";
/**
* Returns a string representing the outermost generic raw type class, or null if the argument
* is not a generic. For example if the string "javax.xml.ws.Holder<my.package.MyObject>" is
* passed in, the string "javax.xml.ws.Holder" will be returned.
* <p/>
* Note that generic arrays are supported. For example, for "Holder<List<String>[][]", the
* returned value will be "List[][]".
*
* @param inputType
* @return A string representing the generic raw type or null if there is no generic.
*/
static String getRawType(String inputType) {
String returnRawType = null;
int leftBracket = inputType.indexOf("<");
int rightBracket = inputType.lastIndexOf(">");
if (leftBracket > 0 && rightBracket > 0 && rightBracket > leftBracket) {
String part1 = inputType.substring(0, leftBracket);
if ((rightBracket + 1) == inputType.length()) {
// There is nothing after the closing ">" we need to append to the raw type
returnRawType = part1;
} else {
// Skip over the closing ">" then append the rest of the string to the raw type
// This would be an array declaration for example.
String part2 = inputType.substring(rightBracket + 1).trim();
returnRawType = part1 + part2;
}
}
return returnRawType;
}
/**
* Return the actual type in a JAX-WS holder declaration. For example, for the argument
* "javax.xml.ws.Holder<my.package.MyObject>", return "my.package.MyObject". If the actual type
* itself is a generic, then that raw type will be returned. For example,
* "javax.xml.ws.Holder<java.util.List<my.package.MyObject>>" will return "java.util.List".
* <p/>
* Note that Holders of Arrays and of Generic Arrays are also supported. For example, for
* "javax.xml.ws.Holder<String[]>", return "String[]". For an array of a generic, the array of
* the raw type is returned. For example, for "javax.xml.ws.Holder<List<String>[][]>", return
* "List[][]".
* <p/>
* Important note! The JAX-WS Holder generic only supports a single actual type, i.e. the
* generic is javax.xml.ws.Holder<T>. This method is not general purpose; it does not support
* generics with multiple types such as Generic<K,V> at the outermost level.
*
* @param holderInputString
* @return return the actual argument class name for a JAX-WS Holder; returns null if the
* argument is not a JAX-WS Holder
*/
static String getHolderActualType(String holderInputString) {
String returnString = null;
if (DescriptionBuilderUtils.isHolderType(holderInputString)) {
int leftBracket = holderInputString.indexOf("<");
int rightBracket = holderInputString.lastIndexOf(">");
if (leftBracket > 0 && rightBracket > leftBracket + 1) {
// Get everything between the outermost "<" and ">"
String actualType =
holderInputString.substring(leftBracket + 1, rightBracket).trim();
// If the holder contained a generic, then get the generic raw type (e.g. "List" for
// Holder<List<String>>).
String rawType = getRawType(actualType);
if (rawType != null) {
returnString = rawType;
} else {
return returnString = actualType;
}
}
}
return returnString;
}
/**
* Check if the input String is a JAX-WS Holder. For example "javax.xml.ws.Holder<my.package.MyObject>".
*
* @param checkType
* @return true if it is a JAX-WS Holder type; false otherwise.
*/
static boolean isHolderType(String checkType) {
boolean isHolder = false;
if (checkType != null) {
if (checkType.startsWith(JAXWS_HOLDER_CLASS)) {
isHolder = checkType.length() == JAXWS_HOLDER_CLASS.length() ||
checkType.charAt(JAXWS_HOLDER_CLASS.length()) == '<';
}
}
return isHolder;
}
/**
* Answers if the String representing the class contains an array declaration. For example
* "Foo[][]" would return true, as would "int[]".
*
* @param className
* @return
*/
static boolean isClassAnArray(String className) {
if (className != null && className.indexOf("[") > 0) {
return true;
} else {
return false;
}
}
/**
* For an class name that is an array, return the non-array declaration portion. For example
* "my.package.Foo[][]" would return "my.package.Foo". Returns null if the argument does not
* contain an array declaration.
*
* @param fullClassName
* @return
*/
static String getBaseArrayClassName(String fullClassName) {
String baseArrayClassName = null;
if (fullClassName != null) {
int firstArrayDimension = fullClassName.indexOf("[");
if (firstArrayDimension > 0) {
baseArrayClassName = fullClassName.substring(0, firstArrayDimension);
}
}
return baseArrayClassName;
}
/**
* Return a prefix suitable for passing to Class.forName(String) for an array. Each array
* dimension represented by "[]" will be represented by a single "[".
*
* @param arrayClassName
* @return
*/
static String getArrayDimensionPrefix(String arrayClassName) {
StringBuffer arrayDimPrefix = new StringBuffer();
if (arrayClassName != null) {
int arrayDimIndex = arrayClassName.indexOf("[]");
while (arrayDimIndex > 0) {
arrayDimPrefix.append("[");
// Skip over this "[]" and see if there are any more.
int startNext = arrayDimIndex + 2;
arrayDimIndex = arrayClassName.indexOf("[]", startNext);
}
}
if (arrayDimPrefix.length() > 0)
return arrayDimPrefix.toString();
else
return null;
}
/**
* For primitives, return the appropriate primitive class. Note that arrays of primitives are
* handled differently, like arrays of objects. Only non-array primitives are processed by this
* method. This method understands both the typical primitive declaration (e.g. "int") and the
* encoding used as for arrays (e.g. "I").
*
* @param classType
* @return
*/
static Class getPrimitiveClass(String classType) {
Class paramClass = null;
if (INT_PRIMITIVE.equals(classType) || INT_PRIMITIVE_ENCODING.equals(classType)) {
paramClass = int.class;
} else if (BYTE_PRIMITIVE.equals(classType) || BYTE_PRIMITIVE_ENCODING.equals(classType)) {
paramClass = byte.class;
} else if (CHAR_PRIMITIVE.equals(classType) || CHAR_PRIMITIVE_ENCODING.equals(classType)) {
paramClass = char.class;
} else
if (SHORT_PRIMITIVE.equals(classType) || SHORT_PRIMITIVE_ENCODING.equals(classType)) {
paramClass = short.class;
} else
if (BOOLEAN_PRIMITIVE.equals(classType) || BOOLEAN_PRIMITIVE_ENCODING.equals(classType)) {
paramClass = boolean.class;
} else if (LONG_PRIMITIVE.equals(classType) || LONG_PRIMITIVE_ENCODING.equals(classType)) {
paramClass = long.class;
} else
if (FLOAT_PRIMITIVE.equals(classType) || FLOAT_PRIMITIVE_ENCODING.equals(classType)) {
paramClass = float.class;
} else
if (DOUBLE_PRIMITIVE.equals(classType) || DOUBLE_PRIMITIVE_ENCODING.equals(classType)) {
paramClass = double.class;
} else if (VOID_PRIMITIVE.equals(classType) || VOID_PRIMITIVE_ENCODING.equals(classType)) {
paramClass = void.class;
}
return paramClass;
}
/**
* Returns the encoding used to represent a Class for an array of a primitive type. For
* example, an array of boolean is represented by "Z". This is as described in the javadoc for
* Class.getName(). If the argument is not a primitive type, a null will be returned.
* <p/>
* Note that arrays of voids are not allowed; a null will be returned.
*
* @param primitiveType
* @return
*/
static String getPrimitiveTypeArrayEncoding(String primitiveType) {
String encoding = null;
if (BOOLEAN_PRIMITIVE.equals(primitiveType)) {
encoding = BOOLEAN_PRIMITIVE_ENCODING;
} else if (BYTE_PRIMITIVE.equals(primitiveType)) {
encoding = BYTE_PRIMITIVE_ENCODING;
} else if (CHAR_PRIMITIVE.equals(primitiveType)) {
encoding = CHAR_PRIMITIVE_ENCODING;
} else if (DOUBLE_PRIMITIVE.equals(primitiveType)) {
encoding = DOUBLE_PRIMITIVE_ENCODING;
} else if (FLOAT_PRIMITIVE.equals(primitiveType)) {
encoding = FLOAT_PRIMITIVE_ENCODING;
} else if (INT_PRIMITIVE.equals(primitiveType)) {
encoding = INT_PRIMITIVE_ENCODING;
} else if (LONG_PRIMITIVE.equals(primitiveType)) {
encoding = LONG_PRIMITIVE_ENCODING;
} else if (SHORT_PRIMITIVE.equals(primitiveType)) {
encoding = SHORT_PRIMITIVE_ENCODING;
}
return encoding;
}
/**
* If the parameter represents and array, then the returned string is in a format that a
* Class.forName(String) can be done on it. This format is described by Class.getName(). If the
* parameter does not represent an array, the parememter is returned unmodified.
* <p/>
* Note that arrays of primitives are processed as well as arrays of objects.
*
* @param classToLoad
* @return
*/
static String reparseIfArray(String classToLoad) {
String reparsedClassName = classToLoad;
if (isClassAnArray(classToLoad)) {
String baseType = getBaseArrayClassName(classToLoad);
String dimensionPrefix = getArrayDimensionPrefix(classToLoad);
if (getPrimitiveTypeArrayEncoding(baseType) != null) {
reparsedClassName = dimensionPrefix + getPrimitiveTypeArrayEncoding(baseType);
} else {
reparsedClassName = dimensionPrefix + "L" + baseType + ";";
}
}
return reparsedClassName;
}
/**
* Load a class represented in a Description Builder Composite. If a classloader is specified,
* it will be used; otherwise the default classloader is used.
*
* @param classToLoad
* @param classLoader
* @return
*/
static Class loadClassFromComposite(String classToLoad, ClassLoader classLoader) {
Class returnClass = null;
// If this is an array, then create a string version as described by Class.getName.
// For example, "Foo[][]" becomes "[[LFoo". Note that arrays of primitives must also be parsed.
classToLoad = DescriptionBuilderUtils.reparseIfArray(classToLoad);
if (classLoader != null) {
// Use the specified classloader to load the class.
try {
returnClass = forName(classToLoad, false, classLoader);
}
//Catch Throwable as ClassLoader can throw an NoClassDefFoundError that
//does not extend Exception, so lets catch everything that extends Throwable
//rather than just Exception.
catch (Throwable ex) {
throw ExceptionFactory.makeWebServiceException(
Messages.getMessage("DBUClassNotFound", classToLoad, classLoader.toString()));
}
} else {
//Use the thread context class loader to load the class.
try {
returnClass = forName(classToLoad, false,
getContextClassLoader(null));
}
catch (Throwable ex) {
//Use the default classloader to load the class.
try {
returnClass = forName(classToLoad);
}
//Catch Throwable as ClassLoader can throw an NoClassDefFoundError that
//does not extend Exception
catch (Throwable ex2) {
throw ExceptionFactory.makeWebServiceException(
Messages.getMessage("DBUClassNotFound2", classToLoad));
}
}
}
return returnClass;
}
static boolean isEmpty(String string) {
return (string == null || "".equals(string));
}
static boolean isEmpty(QName qname) {
return qname == null || isEmpty(qname.getLocalPart());
}
/**
* Return the class for this name
*
* @return Class
*/
private static Class forName(final String className, final boolean initialize,
final ClassLoader classloader) throws ClassNotFoundException {
Class cl = null;
try {
cl = (Class) AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws ClassNotFoundException {
return Class.forName(className, initialize, classloader);
}
}
);
} catch (PrivilegedActionException e) {
if (log.isDebugEnabled()) {
log.debug("Exception thrown from AccessController: " + e.getMessage(), e);
}
throw (ClassNotFoundException) e.getException();
}
return cl;
}
/**
* Return the class for this name
*
* @return Class
*/
private static Class forName(final String className) throws ClassNotFoundException {
Class cl = null;
try {
cl = (Class) AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws ClassNotFoundException {
return Class.forName(className);
}
}
);
} catch (PrivilegedActionException e) {
if (log.isDebugEnabled()) {
log.debug("Exception thrown from AccessController: " + e.getMessage(), e);
}
throw (ClassNotFoundException) e.getException();
}
return cl;
}
/**
* @return ClassLoader
*/
private static ClassLoader getContextClassLoader(final ClassLoader classLoader) {
ClassLoader cl;
try {
cl = (ClassLoader) AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws ClassNotFoundException {
return classLoader != null ? classLoader : Thread.currentThread().getContextClassLoader();
}
}
);
} catch (PrivilegedActionException e) {
if (log.isDebugEnabled()) {
log.debug("Exception thrown from AccessController: " + e.getMessage(), e);
}
throw ExceptionFactory.makeWebServiceException(e.getException());
}
return cl;
}
}