org.opentripplanner.customize.ClassCustomizer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
package org.opentripplanner.customize;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtNewConstructor;
import javassist.Modifier;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.Bytecode;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.DuplicateMemberException;
import javassist.bytecode.FieldInfo;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
import javassist.util.proxy.FactoryHelper;
/**
* This creates a subclass of a given class by adding a new interface to it, and allows users to add
* any necessary fields
*
* The intended use is to allow optimizations or extensions that require per-edge data to massage an
* existing graph to meet their needs, without adding additional fields to the core.
*
* @author novalis
*
*/
public class ClassCustomizer {
private ClassFile classFile;
private CtClass ctClass;
private File extraClassPath;
public void setClassPath(File file) {
this.extraClassPath = file;
}
/**
*
* @param iface The interface the new class should implement
* @param oldlassName The class to be extended
* @param newClassName the name of the new class to be created
*/
public ClassCustomizer(Class> iface, String oldlassName, String newClassName) {
try {
ClassPool pool = ClassPool.getDefault();
ctClass = pool.makeClass(newClassName);
classFile = ctClass.getClassFile();
classFile.setSuperclass(oldlassName);
classFile.setName(newClassName);
classFile.setInterfaces(new String[] { iface.getName() });
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** Adds a new field of type double to the customized class */
public void addDoubleField(String fieldName) {
// FIXME: this should support default values but does not
ClassFile classFile = ctClass.getClassFile();
ConstPool constPool = classFile.getConstPool();
try {
// add field
FieldInfo fieldInfo = new FieldInfo(constPool, fieldName, "D");
classFile.addField(fieldInfo);
CtConstructor ctor = CtNewConstructor.defaultConstructor(ctClass);
ctClass.addConstructor(ctor);
addDoubleSetter(classFile, fieldName);
addDoubleGetter(classFile, fieldName);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** Writes the class file to the classpath and returns a class object */
public Class> saveClass() {
ClassFile classFile = ctClass.getClassFile();
try {
if (!extraClassPath.exists()) {
extraClassPath.mkdirs();
}
FactoryHelper.writeFile(classFile, extraClassPath.getPath());
ClassLoader loader = getClass().getClassLoader();
Class> cls = FactoryHelper.toClass(classFile, loader);
return cls;
// load class
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** Creates a clone of original but with the class NewClass (which extends original's class) */
public static T reclass(T original, Class extends T> newClass) {
Class> origClass = original.getClass();
T newObj;
try {
Constructor extends T> ctor = newClass.getConstructor();
newObj = ctor.newInstance();
while (origClass != null) {
Field[] fields = origClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
int modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers)) {
continue;
}
Object value = field.get(original);
field.set(newObj, value);
}
origClass = origClass.getSuperclass();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return newObj;
}
/**
* capitalize the first letter of the string
*
* @param str
* @return
*/
private String ucfirst(String str) {
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
/**
* Add a simple getter with signature "double getFoo()" to the class, which simply returns the value of the
* field fieldName
* @param ctClass
* @param classFile
* @param fieldName
* @throws DuplicateMemberException
*/
private void addDoubleGetter(ClassFile classFile, String fieldName)
throws DuplicateMemberException {
ConstPool constPool = classFile.getConstPool();
// double getFoo()
MethodInfo getter = new MethodInfo(constPool, "get" + ucfirst(fieldName), "()D");
Bytecode code = new Bytecode(constPool, 2, 1);
// load this
code.addAload(0);
code.addGetfield(ctClass, fieldName, "D");
// return with value
code.addOpcode(Opcode.DRETURN);
getter.setCodeAttribute(code.toCodeAttribute());
getter.setAccessFlags(AccessFlag.PUBLIC);
classFile.addMethod(getter);
}
private void addDoubleSetter(ClassFile classFile, String fieldName)
throws DuplicateMemberException {
ConstPool constPool = classFile.getConstPool();
// void setFoo(double)
MethodInfo setter = new MethodInfo(constPool, "set" + ucfirst(fieldName), "(D)V");
Bytecode code = new Bytecode(constPool, 3, 3);
// load this
code.addAload(0);
// load param
code.addDload(1);
code.addPutfield(ctClass, fieldName, "D");
code.addOpcode(Opcode.RETURN);
setter.setCodeAttribute(code.toCodeAttribute());
setter.setAccessFlags(AccessFlag.PUBLIC);
classFile.addMethod(setter);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy