package railo.commons.lang;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.Map;
import java.util.Set;
import railo.commons.collection.MapFactory;
import railo.commons.io.FileUtil;
import railo.commons.io.IOUtil;
import railo.commons.io.SystemUtil;
import railo.runtime.PageContextImpl;
import railo.runtime.config.Config;
import railo.runtime.engine.ThreadLocalPageContext;
import railo.runtime.exp.PageException;
import railo.runtime.op.Caster;
import railo.runtime.type.Array;
import railo.runtime.type.util.ListUtil;
public final class ClassUtil {
/**
* @param className
* @return
* @throws ClassException
* @throws PageException
*/
public static Class toClass(String className) throws ClassException {
return ClassUtil.loadClass(className);
}
private static Class checkPrimaryTypes(String className, Class defaultValue) {
String lcClassName=className.toLowerCase();
boolean isRef=false;
if(lcClassName.startsWith("java.lang.")){
lcClassName=lcClassName.substring(10);
isRef=true;
}
if(lcClassName.equals("boolean") || className.equals("[Z")) {
if(isRef) return Boolean.class;
return boolean.class;
}
if(lcClassName.equals("byte") || className.equals("[B")) {
if(isRef) return Byte.class;
return byte.class;
}
if(lcClassName.equals("int") || className.equals("[I")) {
return int.class;
}
if(lcClassName.equals("long") || className.equals("[J")) {
if(isRef) return Long.class;
return long.class;
}
if(lcClassName.equals("float") || className.equals("[F")) {
if(isRef) return Float.class;
return float.class;
}
if(lcClassName.equals("double") || className.equals("[D")) {
if(isRef) return Double.class;
return double.class;
}
if(lcClassName.equals("char") || className.equals("[C")) {
return char.class;
}
if(lcClassName.equals("short") || className.equals("[S")) {
if(isRef) return Short.class;
return short.class;
}
if(lcClassName.equals("integer")) return Integer.class;
if(lcClassName.equals("character")) return Character.class;
if(lcClassName.equals("object")) return Object.class;
if(lcClassName.equals("string")) return String.class;
if(lcClassName.equals("null")) return Object.class;
if(lcClassName.equals("numeric")) return Double.class;
return defaultValue;
}
/**
* loads a class from a String classname
* @param className
* @param defaultValue
* @return matching Class
*/
public static Class loadClass(String className, Class defaultValue) {
return loadClass(null,className,defaultValue);
}
/**
* loads a class from a String classname
* @param className
* @return matching Class
* @throws ClassException
*/
public static Class loadClass(String className) throws ClassException {
Config config = ThreadLocalPageContext.getConfig();
Class clazz = loadClass(config==null?null:config.getClassLoader(),className,null);
if(clazz!=null) return clazz;
throw new ClassException("cannot load class through its string name, because no definition for the class with the specified name ["+className+"] could be found");
}
/**
* loads a class from a specified Classloader with given classname
* @param className
* @param cl
* @return matching Class
*/
public static Class loadClass(ClassLoader cl,String className, Class defaultValue) {
className=className.trim();
Class clazz = checkPrimaryTypes(className, null);
if(clazz!=null) return clazz;
if(cl==null){
PageContextImpl pci = (PageContextImpl) ThreadLocalPageContext.get();
if(pci!=null){
try {
cl=pci.getClassLoader();
}
catch (IOException e) {}
}
if(cl==null) {
Config config = ThreadLocalPageContext.getConfig();
if(config!=null)cl=config.getClassLoader();
}
}
try {
if(cl==null)return Class.forName(className.trim());
return cl.loadClass(className.trim());
}
catch (ClassNotFoundException e) {
try {
return Class.forName(className, false, cl);
}
catch (ClassNotFoundException e1) {
// array in the format boolean[] or java.lang.String[]
if(!StringUtil.isEmpty(className) && className.endsWith("[]")) {
StringBuilder pureCN=new StringBuilder(className);
int dimensions=0;
do{
pureCN.delete(pureCN.length()-2, pureCN.length());
dimensions++;
}
while(pureCN.lastIndexOf("[]")==pureCN.length()-2);
clazz = loadClass(cl,pureCN.toString(),null);
if(clazz!=null) {
for(int i=0;i<dimensions;i++)clazz=toArrayClass(clazz);
return clazz;
}
}
// array in the format [C or [Ljava.lang.String;
else if(!StringUtil.isEmpty(className) && className.charAt(0)=='[') {
StringBuilder pureCN=new StringBuilder(className);
int dimensions=0;
do{
pureCN.delete(0, 1);
dimensions++;
}
while(pureCN.charAt(0)=='[');
clazz = loadClass(cl,pureCN.toString(),null);
if(clazz!=null) {
for(int i=0;i<dimensions;i++)clazz=toArrayClass(clazz);
return clazz;
}
}
// class in format Ljava.lang.String;
else if(!StringUtil.isEmpty(className) && className.charAt(0)=='L' && className.endsWith(";")) {
className=className.substring(1,className.length()-1).replace('/', '.');
return loadClass(cl, className,defaultValue);
}
return defaultValue;
}
}
}
/**
* loads a class from a specified Classloader with given classname
* @param className
* @param cl
* @return matching Class
* @throws ClassException
*/
public static Class loadClass(ClassLoader cl,String className) throws ClassException {
Class clazz = loadClass(cl,className,null);
if(clazz!=null) return clazz;
throw new ClassException("cannot load class through its string name, because no definition for the class with the specified name ["+className+"] could be found");
}
/**
* loads a class from a String classname
* @param clazz class to load
* @return matching Class
* @throws ClassException
*/
public static Object loadInstance(Class clazz) throws ClassException{
try {
return clazz.newInstance();
}
catch (InstantiationException e) {
throw new ClassException("the specified class object ["+clazz.getName()+"()] cannot be instantiated");
}
catch (IllegalAccessException e) {
throw new ClassException("can't load class because the currently executing method does not have access to the definition of the specified class");
}
}
public static Object loadInstance(String className) throws ClassException{
return loadInstance(loadClass(className));
}
public static Object loadInstance(ClassLoader cl, String className) throws ClassException{
return loadInstance(loadClass(cl,className));
}
/**
* loads a class from a String classname
* @param clazz class to load
* @return matching Class
*/
public static Object loadInstance(Class clazz, Object defaultValue){
try {
return clazz.newInstance();
}
catch (Throwable t) {
return defaultValue;
}
}
public static Object loadInstance(String className, Object deaultValue){
Class clazz = loadClass(className,null);
if(clazz==null) return deaultValue;
return loadInstance(clazz,deaultValue);
}
public static Object loadInstance(ClassLoader cl, String className, Object deaultValue) {
Class clazz = loadClass(cl,className,null);
if(clazz==null) return deaultValue;
return loadInstance(clazz,deaultValue);
}
/**
* loads a class from a String classname
* @param clazz class to load
* @param args
* @return matching Class
* @throws ClassException
* @throws ClassException
* @throws InvocationTargetException
*/
public static Object loadInstance(Class clazz, Object[] args) throws ClassException, InvocationTargetException {
if(args==null || args.length==0) return loadInstance(clazz);
Class[] cArgs=new Class[args.length];
for(int i=0;i<args.length;i++) {
cArgs[i]=args[i].getClass();
}
try {
Constructor c = clazz.getConstructor(cArgs);
return c.newInstance(args);
}
catch (SecurityException e) {
throw new ClassException("there is a security violation (throwed by security manager)");
}
catch (NoSuchMethodException e) {
StringBuilder sb=new StringBuilder(clazz.getName());
char del='(';
for(int i=0;i<cArgs.length;i++) {
sb.append(del);
sb.append(cArgs[i].getName());
del=',';
}
sb.append(')');
throw new ClassException("there is no constructor with this ["+sb+"] signature for the class ["+clazz.getName()+"]");
}
catch (IllegalArgumentException e) {
throw new ClassException("has been passed an illegal or inappropriate argument");
}
catch (InstantiationException e) {
throw new ClassException("the specified class object ["+clazz.getName()+"] cannot be instantiated because it is an interface or is an abstract class");
}
catch (IllegalAccessException e) {
throw new ClassException("can't load class because the currently executing method does not have access to the definition of the specified class");
}
}
public static Object loadInstance(String className, Object[] args) throws ClassException, InvocationTargetException{
return loadInstance(loadClass(className),args);
}
public static Object loadInstance(ClassLoader cl, String className, Object[] args) throws ClassException, InvocationTargetException{
return loadInstance(loadClass(cl,className),args);
}
/**
* loads a class from a String classname
* @param clazz class to load
* @param args
* @return matching Class
*/
public static Object loadInstance(Class clazz, Object[] args, Object defaultValue) {
if(args==null || args.length==0) return loadInstance(clazz,defaultValue);
try {
Class[] cArgs=new Class[args.length];
for(int i=0;i<args.length;i++) {
if(args[i]==null)cArgs[i]=Object.class;
else cArgs[i]=args[i].getClass();
}
Constructor c = clazz.getConstructor(cArgs);
return c.newInstance(args);
}
catch (Throwable t) {//print.printST(t);
return defaultValue;
}
}
public static Object loadInstance(String className, Object[] args, Object deaultValue){
Class clazz = loadClass(className,null);
if(clazz==null) return deaultValue;
return loadInstance(clazz,args,deaultValue);
}
public static Object loadInstance(ClassLoader cl, String className, Object[] args, Object deaultValue) {
Class clazz = loadClass(cl,className,null);
if(clazz==null) return deaultValue;
return loadInstance(clazz,args,deaultValue);
}
/**
* @return returns a string array of all pathes in classpath
*/
public static String[] getClassPath(Config config) {
Map<String,String> pathes=MapFactory.<String,String>getConcurrentMap();
String pathSeperator=System.getProperty("path.separator");
if(pathSeperator==null)pathSeperator=";";
// pathes from system properties
String strPathes=System.getProperty("java.class.path");
if(strPathes!=null) {
Array arr=ListUtil.listToArrayRemoveEmpty(strPathes,pathSeperator);
int len=arr.size();
for(int i=1;i<=len;i++) {
File file=FileUtil.toFile(Caster.toString(arr.get(i,""),"").trim());
if(file.exists())
try {
pathes.put(file.getCanonicalPath(),"");
} catch (IOException e) {}
}
}
// pathes from url class Loader (dynamic loaded classes)
getClassPathesFromLoader(new ClassUtil().getClass().getClassLoader(), pathes);
getClassPathesFromLoader(config.getClassLoader(), pathes);
Set set = pathes.keySet();
return (String[]) set.toArray(new String[set.size()]);
}
/**
* get class pathes from all url ClassLoaders
* @param cl URL Class Loader
* @param pathes Hashmap with allpathes
*/
private static void getClassPathesFromLoader(ClassLoader cl, Map pathes) {
if(cl instanceof URLClassLoader)
_getClassPathesFromLoader((URLClassLoader) cl, pathes);
}
private static void _getClassPathesFromLoader(URLClassLoader ucl, Map pathes) {
getClassPathesFromLoader(ucl.getParent(), pathes);
// get all pathes
URL[] urls=ucl.getURLs();
for(int i=0;i<urls.length;i++) {
File file=FileUtil.toFile(urls[i].getPath());
if(file.exists())
try {
pathes.put(file.getCanonicalPath(),"");
} catch (IOException e) {}
}
}
// CafeBabe (Java Magic Number)
private static final int ICA=202;//CA
private static final int IFE=254;//FE
private static final int IBA=186;//BA
private static final int IBE=190;//BE
// CF33 (Railo Magic Number)
private static final int ICF=207;//CF
private static final int I33=51;//33
private static final byte BCA=(byte)ICA;//CA
private static final byte BFE=(byte)IFE;//FE
private static final byte BBA=(byte)IBA;//BA
private static final byte BBE=(byte)IBE;//BE
private static final byte BCF=(byte)ICF;//CF
private static final byte B33=(byte)I33;//33
/**
* check if given stream is a bytecode stream, if yes remove bytecode mark
* @param is
* @return is bytecode stream
* @throws IOException
*/
public static boolean isBytecode(InputStream is) throws IOException {
if(!is.markSupported())
throw new IOException("can only read input streams that support mark/reset");
is.mark(-1);
//print(bytes);
int first=is.read();
int second=is.read();
boolean rtn=(first==ICF && second==I33) || (first==ICA && second==IFE && is.read()==IBA && is.read()==IBE);
is.reset();
return rtn;
}
public static boolean isBytecode(byte[] barr){
if(barr.length<4) return false;
return (barr[0]==BCF && barr[1]==B33) || (barr[0]==BCA && barr[1]==BFE && barr[2]==BBA && barr[3]==BBE);
}
public static boolean isRawBytecode(byte[] barr){
if(barr.length<4) return false;
return (barr[0]==BCA && barr[1]==BFE && barr[2]==BBA && barr[3]==BBE);
}
public static boolean hasCF33Prefix(byte[] barr) {
if(barr.length<4) return false;
return (barr[0]==BCF && barr[1]==B33);
}
public static byte[] removeCF33Prefix(byte[] barr) {
if(!hasCF33Prefix(barr)) return barr;
byte[] dest = new byte[barr.length-10];
System.arraycopy(barr, 10, dest, 0, 10);
return dest;
}
public static String getName(Class clazz) {
if(clazz.isArray()){
return getName(clazz.getComponentType())+"[]";
}
return clazz.getName();
}
public static Method getMethodIgnoreCase(Class clazz, String methodName, Class[] args) throws ClassException {
Method[] methods = clazz.getMethods();
Method method;
Class[] params;
outer:for(int i=0;i<methods.length;i++){
method=methods[i];
if(method.getName().equalsIgnoreCase(methodName)){
params = method.getParameterTypes();
if(params.length==args.length){
for(int y=0;y<params.length;y++){
if(!params[y].equals(args[y])){
continue outer;
}
}
return method;
}
}
}
throw new ClassException("class "+clazz.getName()+" has no method with name "+methodName);
}
/**
* return all field names as String array
* @param clazz class to get field names from
* @return field names
*/
public static String[] getFieldNames(Class clazz) {
Field[] fields = clazz.getFields();
String[] names=new String[fields.length];
for(int i=0;i<names.length;i++){
names[i]=fields[i].getName();
}
return names;
}
public static byte[] toBytes(Class clazz) throws IOException {
return IOUtil.toBytes(clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.','/')+".class"),true);
}
/**
* return a array class based on the given class (opposite from Class.getComponentType())
* @param clazz
* @return
*/
public static Class toArrayClass(Class clazz) {
return java.lang.reflect.Array.newInstance(clazz, 0).getClass();
}
public static Class<?> toComponentType(Class<?> clazz) {
Class<?> tmp;
while(true){
tmp=clazz.getComponentType();
if(tmp==null) break;
clazz=tmp;
}
return clazz;
}
/**
* returns the path to the directory or jar file that the class was loaded from
*
* @param clazz - the Class object to check, for a live object pass obj.getClass();
* @param defaultValue - a value to return in case the source could not be determined
* @return
*/
public static String getSourcePathForClass(Class clazz, String defaultValue) {
try {
String result = clazz.getProtectionDomain().getCodeSource().getLocation().getPath();
result = URLDecoder.decode(result, Charset.UTF8);
result = SystemUtil.fixWindowsPath(result);
return result;
}
catch (Throwable t) {}
return defaultValue;
}
/**
* tries to load the class and returns the path that it was loaded from
*
* @param className - the name of the class to check
* @param defaultValue - a value to return in case the source could not be determined
* @return
*/
public static String getSourcePathForClass(String className, String defaultValue) {
try {
return getSourcePathForClass(ClassUtil.loadClass(className), defaultValue);
}
catch (Throwable t) {}
return defaultValue;
}
}