/*
* Copyright 1999-2011 Alibaba Group.
*
* Licensed 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 com.alibaba.dubbo.common.bytecode;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewConstructor;
import javassist.CtNewMethod;
import javassist.LoaderClassPath;
import javassist.NotFoundException;
import com.alibaba.dubbo.common.utils.ReflectUtils;
/**
* ClassGenerator
*
* @author qian.lei
*/
public final class ClassGenerator
{
public static interface DC{} // dynamic class tag interface.
private static final AtomicLong CLASS_NAME_COUNTER = new AtomicLong(0);
private static final String SIMPLE_NAME_TAG = "<init>";
private static final Map<ClassLoader, ClassPool> POOL_MAP = new ConcurrentHashMap<ClassLoader, ClassPool>(); //ClassLoader - ClassPool
public static ClassGenerator newInstance()
{
return new ClassGenerator(getClassPool(Thread.currentThread().getContextClassLoader()));
}
public static ClassGenerator newInstance(ClassLoader loader)
{
return new ClassGenerator(getClassPool(loader));
}
public static boolean isDynamicClass(Class<?> cl)
{
return ClassGenerator.DC.class.isAssignableFrom(cl);
}
public static ClassPool getClassPool(ClassLoader loader)
{
if( loader == null )
return ClassPool.getDefault();
ClassPool pool = POOL_MAP.get(loader);
if( pool == null )
{
pool = new ClassPool(true);
pool.appendClassPath(new LoaderClassPath(loader));
POOL_MAP.put(loader, pool);
}
return pool;
}
private ClassPool mPool;
private CtClass mCtc;
private String mClassName, mSuperClass;
private Set<String> mInterfaces;
private List<String> mFields, mConstructors, mMethods;
private Map<String, Method> mCopyMethods; // <method desc,method instance>
private Map<String, Constructor<?>> mCopyConstructors; // <constructor desc,constructor instance>
private boolean mDefaultConstructor = false;
private ClassGenerator(){}
private ClassGenerator(ClassPool pool)
{
mPool = pool;
}
public String getClassName()
{
return mClassName;
}
public ClassGenerator setClassName(String name)
{
mClassName = name;
return this;
}
public ClassGenerator addInterface(String cn)
{
if( mInterfaces == null )
mInterfaces = new HashSet<String>();
mInterfaces.add(cn);
return this;
}
public ClassGenerator addInterface(Class<?> cl)
{
return addInterface(cl.getName());
}
public ClassGenerator setSuperClass(String cn)
{
mSuperClass = cn;
return this;
}
public ClassGenerator setSuperClass(Class<?> cl)
{
mSuperClass = cl.getName();
return this;
}
public ClassGenerator addField(String code)
{
if( mFields == null )
mFields = new ArrayList<String>();
mFields.add(code);
return this;
}
public ClassGenerator addField(String name, int mod, Class<?> type)
{
return addField(name, mod, type, null);
}
public ClassGenerator addField(String name, int mod, Class<?> type, String def)
{
StringBuilder sb = new StringBuilder();
sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(type)).append(' ');
sb.append(name);
if( def != null && def.length() > 0 )
{
sb.append('=');
sb.append(def);
}
sb.append(';');
return addField(sb.toString());
}
public ClassGenerator addMethod(String code)
{
if( mMethods == null )
mMethods = new ArrayList<String>();
mMethods.add(code);
return this;
}
public ClassGenerator addMethod(String name, int mod, Class<?> rt, Class<?>[] pts, String body)
{
return addMethod(name, mod, rt, pts, null, body);
}
public ClassGenerator addMethod(String name, int mod, Class<?> rt, Class<?>[] pts, Class<?>[] ets, String body)
{
StringBuilder sb = new StringBuilder();
sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(rt)).append(' ').append(name);
sb.append('(');
for(int i=0;i<pts.length;i++)
{
if( i > 0 )
sb.append(',');
sb.append(ReflectUtils.getName(pts[i]));
sb.append(" arg").append(i);
}
sb.append(')');
if( ets != null && ets.length > 0 )
{
sb.append(" throws ");
for(int i=0;i<ets.length;i++)
{
if( i > 0 )
sb.append(',');
sb.append(ReflectUtils.getName(ets[i]));
}
}
sb.append('{').append(body).append('}');
return addMethod(sb.toString());
}
public ClassGenerator addMethod(Method m)
{
addMethod(m.getName(), m);
return this;
}
public ClassGenerator addMethod(String name, Method m)
{
String desc = name + ReflectUtils.getDescWithoutMethodName(m);
addMethod(':' + desc);
if( mCopyMethods == null )
mCopyMethods = new ConcurrentHashMap<String, Method>(8);
mCopyMethods.put(desc, m);
return this;
}
public ClassGenerator addConstructor(String code)
{
if( mConstructors == null )
mConstructors = new LinkedList<String>();
mConstructors.add(code);
return this;
}
public ClassGenerator addConstructor(int mod, Class<?>[] pts, String body)
{
return addConstructor(mod, pts, null, body);
}
public ClassGenerator addConstructor(int mod, Class<?>[] pts, Class<?>[] ets, String body)
{
StringBuilder sb = new StringBuilder();
sb.append(modifier(mod)).append(' ').append(SIMPLE_NAME_TAG);
sb.append('(');
for(int i=0;i<pts.length;i++)
{
if( i > 0 )
sb.append(',');
sb.append(ReflectUtils.getName(pts[i]));
sb.append(" arg").append(i);
}
sb.append(')');
if( ets != null && ets.length > 0 )
{
sb.append(" throws ");
for(int i=0;i<ets.length;i++)
{
if( i > 0 )
sb.append(',');
sb.append(ReflectUtils.getName(ets[i]));
}
}
sb.append('{').append(body).append('}');
return addConstructor(sb.toString());
}
public ClassGenerator addConstructor(Constructor<?> c)
{
String desc = ReflectUtils.getDesc(c);
addConstructor(":"+desc);
if( mCopyConstructors == null )
mCopyConstructors = new ConcurrentHashMap<String, Constructor<?>>(4);
mCopyConstructors.put(desc, c);
return this;
}
public ClassGenerator addDefaultConstructor()
{
mDefaultConstructor = true;
return this;
}
public ClassPool getClassPool() {
return mPool;
}
public Class<?> toClass(){
return toClass(getClass().getClassLoader(), getClass().getProtectionDomain());
}
public Class<?> toClass(ClassLoader loader, ProtectionDomain pd)
{
if( mCtc != null )
mCtc.detach();
long id = CLASS_NAME_COUNTER.getAndIncrement();
try
{
CtClass ctcs = mSuperClass == null ? null : mPool.get(mSuperClass);
if( mClassName == null )
mClassName = ( mSuperClass == null || javassist.Modifier.isPublic(ctcs.getModifiers())
? ClassGenerator.class.getName() : mSuperClass + "$sc" ) + id;
mCtc = mPool.makeClass(mClassName);
if( mSuperClass != null )
mCtc.setSuperclass(ctcs);
mCtc.addInterface(mPool.get(DC.class.getName())); // add dynamic class tag.
if( mInterfaces != null )
for( String cl : mInterfaces ) mCtc.addInterface(mPool.get(cl));
if( mFields != null )
for( String code : mFields ) mCtc.addField(CtField.make(code, mCtc));
if( mMethods != null )
{
for( String code : mMethods )
{
if( code.charAt(0) == ':' )
mCtc.addMethod(CtNewMethod.copy(getCtMethod(mCopyMethods.get(code.substring(1))), code.substring(1, code.indexOf('(')), mCtc, null));
else
mCtc.addMethod(CtNewMethod.make(code, mCtc));
}
}
if( mDefaultConstructor )
mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));
if( mConstructors != null )
{
for( String code : mConstructors )
{
if( code.charAt(0) == ':' )
{
mCtc.addConstructor(CtNewConstructor.copy(getCtConstructor(mCopyConstructors.get(code.substring(1))), mCtc, null));
}
else
{
String[] sn = mCtc.getSimpleName().split("\\$+"); // inner class name include $.
mCtc.addConstructor(CtNewConstructor.make(code.replaceFirst(SIMPLE_NAME_TAG, sn[sn.length-1]), mCtc));
}
}
}
return mCtc.toClass(loader, pd);
}
catch(RuntimeException e)
{
throw e;
}
catch(NotFoundException e)
{
throw new RuntimeException(e.getMessage(), e);
}
catch(CannotCompileException e)
{
throw new RuntimeException(e.getMessage(), e);
}
}
public void release()
{
if( mCtc != null ) mCtc.detach();
if( mInterfaces != null ) mInterfaces.clear();
if( mFields != null ) mFields.clear();
if( mMethods != null ) mMethods.clear();
if( mConstructors != null ) mConstructors.clear();
if( mCopyMethods != null ) mCopyMethods.clear();
if( mCopyConstructors != null ) mCopyConstructors.clear();
}
private CtClass getCtClass(Class<?> c) throws NotFoundException
{
return mPool.get(c.getName());
}
private CtMethod getCtMethod(Method m) throws NotFoundException
{
return getCtClass(m.getDeclaringClass()).getMethod(m.getName(),ReflectUtils.getDescWithoutMethodName(m));
}
private CtConstructor getCtConstructor(Constructor<?> c) throws NotFoundException
{
return getCtClass(c.getDeclaringClass()).getConstructor(ReflectUtils.getDesc(c));
}
private static String modifier(int mod)
{
if( Modifier.isPublic(mod) ) return "public";
if( Modifier.isProtected(mod) ) return "protected";
if( Modifier.isPrivate(mod) ) return "private";
return "";
}
}