/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.ws.tools;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.rmi.Remote;
import java.util.Calendar;
import java.util.Date;
import javax.xml.namespace.QName;
import javax.xml.rpc.holders.ByteArrayHolder;
import javax.xml.rpc.holders.Holder;
import org.jboss.ws.Constants;
import org.jboss.ws.WSException;
import org.jboss.ws.core.jaxrpc.LiteralTypeMapping;
import org.jboss.ws.core.jaxrpc.binding.SimpleDeserializerFactory;
import org.jboss.ws.core.jaxrpc.binding.SimpleSerializerFactory;
import org.jboss.ws.metadata.wsdl.WSDLUtils;
import org.jboss.ws.metadata.wsdl.xsd.SchemaUtils;
import org.jboss.wsf.common.JavaUtils;
/**
* Util class for the JBossWS Tools project
* Contains important methods that deal with JAXB 1, JAXB2
* and JAXWS 2.0 specs
* @author <mailto:Anil.Saldhana@jboss.org>Anil Saldhana
* @since Jun 25, 2005
*/
public class ToolsUtils
{
/**
* Maintains a static reference to the TypeMapping just for
* the standard type mappings
*/
private static LiteralTypeMapping mapping = new LiteralTypeMapping();
//Hide the constructor - this is a class with static methods
private ToolsUtils()
{
throw new WSException("Cannot instantiate ToolsUtils.");
}
/**
* Checks whether the class extends java.rmi.Remote
*
* @param paramType
*/
public static void checkParameterType( Class paramType)
{
if (Remote.class.isAssignableFrom(paramType))
throw new IllegalArgumentException("JAXWS-2.0 Assertion::" + "Method param shouldn't extend Remote");
}
/**
* Ensure that the first alphabet is uppercase
* @param fname
* @return String that has first character upper case
*/
public static String firstLetterUpperCase(String fname)
{
if (fname == "" || fname == null)
throw new WSException("String passed is null");
//Ensure that the first character is uppercase
if (Character.isLowerCase(fname.charAt(0)))
{
char[] chars = fname.toCharArray();
char f = Character.toUpperCase(chars[0]);
chars[0] = f;
fname = new String(chars);
}
return fname;
}
/**
* Ensure that the first alphabet is lowercase
* @param fname
* @return String that has first character lower case
*/
public static String firstLetterLowerCase(String fname)
{
if (fname == "" || fname == null)
throw new WSException("String passed is null");
//Ensure that the first character is lowercase
if (Character.isUpperCase(fname.charAt(0)))
{
char[] chars = fname.toCharArray();
char f = Character.toLowerCase(chars[0]);
chars[0] = f;
fname = new String(chars);
}
return fname;
}
/**
* Returns a Java Identifier from a XML Name
* as specified by both JAXB 1 and JAXB 2 specs.
* Used for deriving class names, method names etc
* @param xmlName XML Name
* @return Legal Java Identifier
*/
public static String getJavaIdentifier(String xmlName)
{
if(xmlName == null || xmlName == "")
throw new IllegalArgumentException("xmlName is null");
xmlName = xmlName.trim(); //Get rid of whitespaces
//Remove leading and trailing punctuation marks
xmlName = trimPunctuationMarks( xmlName);
if(xmlName == null)
throw new WSException("xmlName has become null");
//Get rid of characters that are not legal characters
int lenStr = xmlName.length();
char[] mainArr = new char[lenStr];
int j = 0;
for(int i = 0; i < lenStr;i++)
{
char ch = xmlName.charAt(i);
if(Character.isJavaIdentifierPart(ch))
{
mainArr[j] = ch;
j++;
}
}
//Copy into a new array
char[] secArr = new char[j];
for(int x = 0; x < j ; x++)
{
secArr[x] = mainArr[x];
}
return new String(secArr);
}
/**
* Get valid Java Method name as per JAXB 1 and JAXB2.0 spec
* @param xmlName XML Name
* @param setter is this method for which the name is sought, a setter?
* @return Valid Java method name
*/
public static String getJavaMethodName( String xmlName, boolean setter)
{
xmlName = getJavaIdentifier(xmlName);
xmlName = firstLetterUpperCase(xmlName);
String lcase = xmlName.toLowerCase();
if( setter && !lcase.startsWith("set"))
{
xmlName = "set" + xmlName;
}
else
if(!setter && !lcase.startsWith("get"))
{
xmlName = "get" + xmlName;
}
return firstLetterLowerCase(xmlName);
}
/**
* Get a valid Java class name as per JAXB 1 and JAXB2.0 spec
* @param xmlName XML Name
* @return Valid class name
*/
public static String getValidClassName( String xmlName)
{
xmlName = getJavaIdentifier(xmlName);
//Ensure the first character is uppercase
return firstLetterUpperCase(xmlName);
}
/**
* Given a Java Class, return the xmlType
*
* @param javaClass
* @param ns
* @return
*/
public static QName getXMLType(Class javaClass, String ns)
{
WSDLUtils utils = WSDLUtils.getInstance();
QName xmlType = null;
if(void.class == javaClass)
return null;
/**
* Special boundary condition: When the javaClass is ByteArrayHolder,
* the xmlType should be xsd:base64Binary, but because of our algorithm,
* it will become xsd:byte - so need to bypass this.
*/
if(ByteArrayHolder.class == javaClass)
{
return Constants.TYPE_LITERAL_BASE64BINARY;
}
if(Holder.class.isAssignableFrom(javaClass))
javaClass = utils.getJavaTypeForHolder(javaClass);
xmlType = SchemaUtils.getInstance().getToolsOverrideInTypeMapping(javaClass);
if(xmlType != null)
return xmlType;
// Byte[] should always be mapped to an array of bytes
// String[] should always be mapped to an array of strings
if (javaClass != Byte[].class && javaClass != String[].class)
{
xmlType = mapping.getXMLType(javaClass,false);
if(xmlType != null)
return xmlType;
}
//Else create a QName
String typeName = utils.getJustClassName(javaClass);;
if(javaClass.isArray())
{
int dim = utils.getArrayDimension(javaClass);
Class cls = javaClass;
while(cls.isArray())
{
cls = cls.getComponentType();
}
typeName = WSDLUtils.getInstance().getJustClassName(cls);
while(dim-- > 0)
typeName += ".Array";
}
return new QName(ns, typeName);
/*SchemaCreatorIntf sc = new SchemaTypeCreator();
sc.setRestrictToTargetNamespace(restrictNS);
//For basic types, the following algorithm will return the simple types
JBossXSTypeDefinition xt = sc.generateTypeWithoutRegistration(null, javaClass,ns);
return new QName(xt.getNamespace(), xt.getName());*/
}
/**
* Get the XML Type for an attachment mime type
* (will be xsd:hexbinary)
*
* @param mimetype
* @return
*/
public static QName getXMLTypeForAttachmentType( String mimetype)
{
return Constants.TYPE_LITERAL_HEXBINARY;
}
/**
* Returns whether a character is a punctuation
* @param c
* @return true:if punctuation, false - otherwise
*/
public static boolean isPunctuation(char c)
{
boolean ret = true;
//Idea is if it is not a letter, a digit, a white space, it is a punct
if(Character.isDigit(c)) ret = false;
else
if(Character.isLetter(c)) ret = false;
else
if(Character.isISOControl(c)) ret = false;
else
if(Character.isWhitespace(c)) ret = false;
return ret;
}
/**
* Checks whether a class is a legal jaxrpc value type
* as defined by jaxrpc 1.l specification
*
* @param cls
* @return
*/
public static boolean isJaxrpcValueType(Class cls)
{
boolean result = true;
if(isJaxrpcRegularType(cls))
return false;
boolean defaultCTR = false;
//Does it define a default constructor?
Constructor[] cons = cls.getDeclaredConstructors();
for (int i=0; i<cons.length; i++)
{
Class[] paramTypes = cons[i].getParameterTypes();
if(paramTypes == null || paramTypes.length == 0)
{
defaultCTR = true;
break;
}
}
if(defaultCTR == false)
return false;
//Does the class extend Remote?
if(Remote.class.isAssignableFrom(cls))
return false;
//Check the public fields are Jaxprc types
Field[] fieldArr = cls.getDeclaredFields();
int length = Array.getLength(fieldArr);
for(int i = 0 ; i < length ; i++)
{
if(!isJaxrpcPermittedType(fieldArr[i].getType()) )
return false;
}
return result;
}
/**
* Basic check whether the passed argument: class
* contain properties
*
* @param cls
* @return
*/
public static boolean isJavaBean(Class cls)
{
boolean result = false;
if(isJaxrpcRegularType(cls))
return false;
try
{
BeanInfo bi = Introspector.getBeanInfo(cls);
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
if(pds != null && pds.length > 0)
result = true;
}
catch (java.beans.IntrospectionException e)
{
}
return result;
}
public static int getNumberOfParticles(Class javaBean)
{
if(isJavaBean(javaBean) == false)
throw new IllegalArgumentException("Illegal JavaBean argument");
//Get number of public fields
Field[] pubFields = javaBean.getFields();
int pub = pubFields != null ? pubFields.length : 0;
int props = 0;
//Get number of properties via bean introspection
try
{
BeanInfo bi = Introspector.getBeanInfo(javaBean);
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
props = pds != null ? Array.getLength(pds) : 0;
int len = 0;
for(int i = 0; i < props ; i++)
{
PropertyDescriptor pd = pds[i];
Method readMethod = pd.getReadMethod();
if("getClass".equals(readMethod.getName()))
continue;
len++;
}
props = len;
}
catch (java.beans.IntrospectionException e)
{
}
return pub + props;
}
/**
* Determines whether a Java Class can be unwrapped
*
* @param javaType
* @param isReturnType Does the javaType represent a return type
* of a method?
* @return
*/
public static boolean canUnWrap(Class javaType, boolean isReturnType)
{
boolean bool = false;
if(isReturnType)
{
bool = ( ToolsUtils.isJavaBean(javaType)
&& ToolsUtils.getNumberOfParticles(javaType) == 1 );
}
else
bool = ToolsUtils.isJaxrpcValueType(javaType);
return bool;
}
/**
* Checks whether the class type that is passed as argument is
* a legal Jaxrpc type as defined by jaxrpc 1.1 spec
*
* @param type
* @return
*/
public static boolean isJaxrpcPermittedType(Class type)
{
if(isJaxrpcRegularType(type) == false)
return isJaxrpcValueType(type);
else
return true;
}
/**
* Checks whether the class type that is passed as argument is
* a legal Jaxrpc type (and not a Value Type) as defined by jaxrpc 1.1 spec
*
* @param type
* @return
*/
public static boolean isJaxrpcRegularType(Class type)
{
boolean result = false;
//Check if it is a primitive type
if(JavaUtils.isPrimitive(type))
return true;
//Check if it is an array of primitive types
Class wrapperType = JavaUtils.getWrapperType(type);
if( wrapperType != null && wrapperType != type )
return true;
//Check if it is a wrapper type or array
Class primType = JavaUtils.getPrimitiveType(type);
if( primType != null && primType != type)
return true;
//Check standard jaxrpc 1.1 classes
if(String.class == type || Date.class == type || Calendar.class == type ||
BigInteger.class == type || BigDecimal.class == type || QName.class == type ||
URI.class == type)
return true;
return result;
}
public synchronized static void registerJavaType(LiteralTypeMapping typeMapping,
Class clazz, QName xmlType)
{
if(typeMapping.isRegistered(clazz,xmlType) == false )
typeMapping.register(clazz,xmlType, new SimpleSerializerFactory(), new SimpleDeserializerFactory());
}
/**
* Check all of the characters in the component and for any that are not valid
* in Java identifiers replace with an underscore.
*/
public static String convertInvalidCharacters(final String component)
{
String result = component;
for (int i = 0; i < result.length(); i++)
{
if (Character.isJavaIdentifierPart(result.charAt(i)) == false)
{
result = result.replace(result.charAt(i), '_');
}
}
return result;
}
//Private methods
private static String trimPunctuationMarks( String str)
{
if(str == null)
throw new IllegalArgumentException("Str is null");
//Check if the first character is permissible
if(Character.isJavaIdentifierStart(str.charAt(0)) == false)
str = str.substring(1);
else
//Check if the first character is a punctuation
if(isPunctuation(str.charAt( 0 )) )
str = str.substring(1);
int len = str.length();
//Check if the last character is a punctuation
if(isPunctuation(str.charAt( len -1 )) )
str = str.substring(0,len-1);
return str;
}
}