/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.enterprise.deployment.util; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.HashSet; import java.util.Hashtable; import java.util.Locale; import java.util.Set; import java.util.StringTokenizer; import java.util.Vector; /** * Datatype management utility methods */ public class TypeUtil { // map a decimal digit to its (real!) character private static final char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; // Map of primitive class name and its associated Class object private static Hashtable primitiveClasses_; static { primitiveClasses_ = new Hashtable(); primitiveClasses_.put(Character.TYPE.getName(), Character.TYPE); primitiveClasses_.put(Boolean.TYPE.getName(), Boolean.TYPE); primitiveClasses_.put(Byte.TYPE.getName(), Byte.TYPE); primitiveClasses_.put(Integer.TYPE.getName(), Integer.TYPE); primitiveClasses_.put(Long.TYPE.getName(), Long.TYPE); primitiveClasses_.put(Short.TYPE.getName(), Short.TYPE); primitiveClasses_.put(Float.TYPE.getName(), Float.TYPE); primitiveClasses_.put(Double.TYPE.getName(), Double.TYPE); } /** * Place a character representation of src into the buffer. * No formatting (e.g. localization) is done. * * @param src - the integer to convert. Must not be Integer.MIN_VALUE. * @param buf - the buf to put the result in * @param offset - the offset in buf to place the first digit * @return the number of bytes added to buf * @exception IllegalArgumentException if src is Integer.MIN_VALUE. */ public static int intGetChars( int src, char buf[], int offset ) { int power = 1000000000; // magnitude of highest digit this can handle int this_digit; boolean have_emitted = false; int init_offset = offset; // special case src is zero if (src == 0) { buf[offset] = digits[0]; return 1; } else if (src < 0) { if (src == Integer.MIN_VALUE) throw new IllegalArgumentException(); // emit the negation sign and continue as if positive buf[offset++] = '-'; src = Math.abs(src); } // iterate until there are no more digits to emit while (power > 0) { this_digit = src / power; if (this_digit != 0 || have_emitted) { // emit this digit have_emitted = true; buf[offset++] = digits[this_digit]; } src = src % power; power = power / 10; } return offset - init_offset; } // map a digit to its single byte character private static final byte[] charval = { (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',(byte) '6',(byte) '7',(byte) '8',(byte) '9' }; /** * Place a byte representation of src into the byte array buf. * No commas or any other formatting is done to the integer. * @param src - the integer to convert. Must not be Integer.MIN_VALUE. * @param buf - the buf to put the result in * @param offset - the offset in buf to place the first digit * @return the number of bytes added to buf * @exception IllegalArgumentException if src is Integer.MIN_VALUE. */ public static int intGetBytes( int src, byte buf[], int offset ) { int power = 1000000000; // magnitude of highest digit this can handle int this_digit; boolean have_emitted = false; int init_offset = offset; // special case src is zero if (src == 0) { buf[offset] = charval[0]; return 1; } else if (src < 0) { if (src == Integer.MIN_VALUE) throw new IllegalArgumentException(); // emit the negation sign and continue as if positive buf[offset++] = (byte) '-'; src = Math.abs(src); } // iterate until there are no more digits to emit while (power > 0) { this_digit = src / power; if (this_digit != 0 || have_emitted) { // emit this digit have_emitted = true; buf[offset++] = charval[this_digit]; } src = src % power; power = power / 10; } return offset - init_offset; } /** * Work around a performance bug in String.hashCode() for strings longer * than sixteen characters, by calculating a (slower) hash on all the * characters in the string. Not needed starting in the JDK 1.2 release. */ public static int hashCode(String s) { int length = s.length(); int h = 1; for (int i = 0; i < length; i++) h = (h * 37) + (int) s.charAt(i); return h; } /** * Word-wrap a string into an array of strings. Space is the only * separator character recognized. */ public static String[] wordWrap(String msg, int widthInChars) { int width = widthInChars; int nextBreak =0; int lastBreak = 0; int length = msg.length(); int lengthLeft = length; boolean breakFound = true; Vector v = new Vector(); int nextNewline = msg.indexOf("\n"); while (lengthLeft > width || nextNewline != -1) { // Find a convenient word break, always respecting explicit line // breaks. nextBreak = nextNewline; // If no newline, look for a space. if (nextBreak == -1 || nextBreak <= lastBreak || nextBreak > lastBreak + width) { nextBreak = msg.lastIndexOf(" ", lastBreak + width); } // No space, break it at the wrap width. if (nextBreak == -1 || nextBreak <= lastBreak) { nextBreak = lastBreak + width - 1; breakFound = false; if (nextBreak > length) { break; } } // Save the substring and adjust indexes. String substr = msg.substring(lastBreak, nextBreak); v.addElement(substr); lengthLeft -= substr.length(); lastBreak = nextBreak; if (breakFound) { ++lastBreak; } breakFound = true; nextNewline = msg.indexOf("\n", lastBreak); } v.addElement(msg.substring(lastBreak)); String[] lines = new String[v.size()]; v.copyInto(lines); return lines; } /** * Convert an array of strings to a single line with elements separated * by the given separator. Similar to Tcl's <code>join</code>. * @param from the array of strings to convert * @param separator the string to insert between each element */ public static String arrayToString(String[] from, String separator) { StringBuffer sb = new StringBuffer(100); String sep = ""; for (int i = 0; i < from.length; i++) { sb.append(sep); sb.append(from[i]); sep = separator; } return sb.toString(); } /** * Convert a string of delimited strings to an array of strings. * Similar to AWK's and Tcl's <code>split</code>. * @param from the string to convert * @param separator the delimiter */ public static String[] stringToArray(String from, String separator) { if (from == null) { return null; } if (separator == null) { separator = " "; } StringTokenizer toks = new StringTokenizer(from, separator); String[] result = new String[toks.countTokens()]; int i = 0; while (toks.hasMoreTokens()) { result[i++] = toks.nextToken().trim(); } return result; } /** * Truncate a float to the required number of significant digits. */ public static String truncateFloat(float f, int digits) { double factor = Math.pow(10, digits); f = (float)(Math.round(f * factor) / factor); return Float.toString(f); } /** * Add commas to a number for "123,456.7" style formatting. * @deprecated Use standard java.* APIs which create the correct * localized number format. */ public static String addCommas(float f) { String floatStr = truncateFloat(f, 0); return addCommas(floatStr); } /** * Add commas to a number for "123,456.7" style formatting. * @deprecated Use standard java.* APIs which create the correct * localized number format. */ public static String addCommas(String numStr) { int dotIndex = numStr.lastIndexOf('.'); String n; String fraction = ""; if (dotIndex >= 0) { fraction = numStr.substring(dotIndex); n = numStr.substring(0, dotIndex); } else { n = numStr; } String val = ""; int lastIndex = 0; for (int i = n.length(); i > 0; i -= 3) { String comma; if (i > 3) { comma = ","; } else { comma = ""; } int start = Math.max(i - 3, 0); val = comma + n.substring(start, i) + val; lastIndex = start; } val = n.substring(0, lastIndex) + val + fraction; return val; } /** * Test if a class is a subclass of another. * @deprecated Use <em>sup.isAssignableFrom(sub)</em> */ public static boolean isSubclassOf(Class sub, Class sup) { if (sub == sup) { return true; } Class superclass = sub.getSuperclass(); while (superclass != null && superclass != sup) { superclass = superclass.getSuperclass(); } return (superclass != null); } /** * Get all super-interfaces of a class, excluding the * given base interface. * Returns a set of strings containing class names. */ public static Set getSuperInterfaces(ClassLoader cl, String className, String baseClassName) throws ClassNotFoundException { Set allSuper = new HashSet(); if( !className.equals(baseClassName) ) { Class theClass = cl.loadClass(className); Class[] superInterfaces = theClass.getInterfaces(); for(int superIndex = 0; superIndex < superInterfaces.length; superIndex++) { Class currentClass = superInterfaces[superIndex]; String currentClassName = currentClass.getName(); if( !currentClassName.equals(baseClassName) ) { allSuper.add(currentClassName); allSuper.addAll(getSuperInterfaces(cl, currentClassName, baseClassName)); } } // End for -- each super interface } return allSuper; } public static Method getMethod(Class declaringClass, ClassLoader loader, String name, String[] paramClassNames) throws Exception { Class[] parameterTypes=null; if (paramClassNames!=null) { parameterTypes = new Class[paramClassNames.length]; for(int pIndex = 0; pIndex < parameterTypes.length; pIndex++) { String next = paramClassNames[pIndex]; if( primitiveClasses_.containsKey(next) ) { parameterTypes[pIndex] = (Class) primitiveClasses_.get(next); } else { parameterTypes[pIndex] = Class.forName(next, true, loader); } } } return declaringClass.getMethod(name, parameterTypes); } public static Method getDeclaredMethod(Class declaringClass, ClassLoader loader, String name, String[] paramClassNames) throws Exception { Class[] parameterTypes=null; if (paramClassNames!=null) { parameterTypes = new Class[paramClassNames.length]; for(int pIndex = 0; pIndex < parameterTypes.length; pIndex++) { String next = paramClassNames[pIndex]; if( primitiveClasses_.containsKey(next) ) { parameterTypes[pIndex] = (Class) primitiveClasses_.get(next); } else { parameterTypes[pIndex] = Class.forName(next, true, loader); } } } return declaringClass.getDeclaredMethod(name, parameterTypes); } /** * Compares the signatures of two methods to see if they * have the same numer of parameters and same parameter types. * * Note that this equality check does NOT cover : * 1) declaring class 2) exceptions 3) method name * 4) return type * */ public static boolean sameParamTypes(Method m1, Method m2) { boolean same = false; Type[] gpm1 = m1.getGenericParameterTypes(); Type[] gpm2 = m2.getGenericParameterTypes(); if ((gpm1.length == gpm2.length)) { same = true; for (int i = 0; i < gpm1.length; i++) { if (!gpm1[i].equals(gpm2[i])) { if (gpm1[i] instanceof TypeVariable || gpm2[i] instanceof TypeVariable) { continue; } else if(gpm1[i] instanceof ParameterizedType || gpm2[i] instanceof ParameterizedType) { //See issue 15595 (ClassFormatError: Duplicate method name thrown in deployment) //For ParameterizedType params, compare their non-generics parameter types. same = m1.getParameterTypes()[i].equals(m2.getParameterTypes()[i]); if(!same) { break; } } else { same = false; break; } } } } return same; } /** * Compares the signatures of two methods to see if they * have the same method name, parameters, and return type. * * Note that this equality check does NOT cover : * 1) declaring class 2) exceptions * */ public static boolean sameMethodSignature(Method m1, Method m2) { boolean same = false; if(m1.getName().equals(m2.getName())) { same = sameParamTypes(m1, m2) && sameReturnTypes(m1, m2); } return same; } /** * Compares the return types of 2 methods. * @param m1 method 1 * @param m2 method 2 * @return true if the return types of the 2 methods are the same, or if * one of them is instance of java.lang.reflect.TypeVariable. */ private static boolean sameReturnTypes(Method m1, Method m2) { if(m1.getReturnType().equals(m2.getReturnType())) { return true; } Type grt1 = m1.getGenericReturnType(); Type grt2 = m2.getGenericReturnType(); if(grt1.equals(grt2)) { return true; } if(grt1 instanceof TypeVariable || grt2 instanceof TypeVariable) { return true; } return false; } /** * Convert a java beans setter method to its property name. */ public static String setterMethodToPropertyName(String setterMethodName) { if( (setterMethodName == null) || (setterMethodName.length() <= 3) || !setterMethodName.startsWith("set") ) { throw new IllegalArgumentException("Invalid setter method name " + setterMethodName); } return ( setterMethodName.substring(3, 4).toLowerCase(Locale.ENGLISH) + setterMethodName.substring(4) ); } /** * Convert a java beans property name to a setter method name */ public static String propertyNameToSetterMethod(String propertyName) { if( (propertyName == null) || (propertyName.length() == 0) ) { throw new IllegalArgumentException("Invalid property name " + propertyName); } return ( "set" + propertyName.substring(0, 1).toUpperCase(Locale.ENGLISH) + propertyName.substring(1) ); } /** * Convert String array of class names into array of Classes. */ public static Class[] paramClassNamesToTypes(String[] paramClassNames, ClassLoader loader) throws Exception { Class[] parameterTypes = null; if (paramClassNames != null) { parameterTypes = new Class[paramClassNames.length]; for(int pIndex = 0; pIndex < parameterTypes.length; pIndex++) { String next = paramClassNames[pIndex]; if( primitiveClasses_.containsKey(next) ) { parameterTypes[pIndex] = (Class) primitiveClasses_.get(next); } else { parameterTypes[pIndex] = Class.forName(next, true, loader); } } } return parameterTypes; } }