/* XXL: The eXtensible and fleXible Library for data processing Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department of Mathematics and Computer Science University of Marburg Germany This library 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 3 of the License, or (at your option) any later version. This library 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 library; If not, see <http://www.gnu.org/licenses/>. http://code.google.com/p/xxl/ */ package xxl.core.util.reflect; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.io.Serializable; import java.util.HashMap; /** * This class allows to extend a class at runtime in order to have a MetaData * field and matching setter and getter-methods. Existing objects can be * modified that way at runtime as well. */ public class RuntimeExtender { /** * Subclass of ClassLoader needed to call the protected methos definaClass. */ public static class ExtenderClassLoader extends ClassLoader { Class load (String name, byte[] b, int off, int len) throws ClassFormatError { return super.defineClass(name,b,off,len); } } final static byte[] extenderByteCodeStart = { -54, -2, -70, -66, 0, 0, 0, 46, 0, 45, 1, 0 }; final static byte[] extenderByteCodeMiddle = { 7, 0, 1, 1, 0 }; final static byte[] extenderByteCodeEnd = { 7, 0, 3, 1, 0, 30, 120, 120, 108, 47, 99, 111, 114, 101, 47, 117, 116, 105, 108, 47, 77, 101, 116, 97, 68, 97, 116, 97, 80, 114, 111, 118, 105, 100, 101, 114, 7, 0, 5, 1, 0, 20, 106, 97, 118, 97, 47, 105, 111, 47, 83, 101, 114, 105, 97, 108, 105, 122, 97, 98, 108, 101, 7, 0, 7, 1, 0, 8, 109, 101, 116, 97, 68, 97, 116, 97, 1, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 12, 0, 11, 0, 12, 10, 0, 4, 0, 14, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 18, 76, 111, 99, 97, 108, 86, 97, 114, 105, 97, 98, 108, 101, 84, 97, 98, 108, 101, 1, 0, 4, 116, 104, 105, 115, 1, 0, 10, 76, 69, 120, 116, 101, 110, 100, 101, 114, 59, 1, 0, 11, 103, 101, 116, 77, 101, 116, 97, 68, 97, 116, 97, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 12, 0, 9, 0, 10, 9, 0, 2, 0, 22, 1, 0, 11, 115, 101, 116, 77, 101, 116, 97, 68, 97, 116, 97, 1, 0, 21, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 41, 86, 1, 0, 10, 114, 101, 97, 100, 79, 98, 106, 101, 99, 116, 1, 0, 30, 40, 76, 106, 97, 118, 97, 47, 105, 111, 47, 79, 98, 106, 101, 99, 116, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109, 59, 41, 86, 1, 0, 10, 69, 120, 99, 101, 112, 116, 105, 111, 110, 115, 1, 0, 19, 106, 97, 118, 97, 47, 105, 111, 47, 73, 79, 69, 120, 99, 101, 112, 116, 105, 111, 110, 7, 0, 29, 1, 0, 32, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 67, 108, 97, 115, 115, 78, 111, 116, 70, 111, 117, 110, 100, 69, 120, 99, 101, 112, 116, 105, 111, 110, 7, 0, 31, 1, 0, 25, 106, 97, 118, 97, 47, 105, 111, 47, 79, 98, 106, 101, 99, 116, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109, 7, 0, 33, 1, 0, 17, 100, 101, 102, 97, 117, 108, 116, 82, 101, 97, 100, 79, 98, 106, 101, 99, 116, 12, 0, 35, 0, 12, 10, 0, 34, 0, 36, 1, 0, 6, 115, 116, 114, 101, 97, 109, 1, 0, 27, 76, 106, 97, 118, 97, 47, 105, 111, 47, 79, 98, 106, 101, 99, 116, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109, 59, 1, 0, 10, 105, 110, 105, 116, 105, 97, 108, 105, 122, 101, 12, 0, 26, 0, 27, 10, 0, 2, 0, 41, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 13, 69, 120, 116, 101, 110, 100, 101, 114, 46, 106, 97, 118, 97, 0, 33, 0, 2, 0, 4, 0, 2, 0, 6, 0, 8, 0, 1, 0, 0, 0, 9, 0, 10, 0, 0, 0, 5, 0, 1, 0, 11, 0, 12, 0, 1, 0, 13, 0, 0, 0, 47, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 15, -79, 0, 0, 0, 2, 0, 16, 0, 0, 0, 6, 0, 1, 0, 0, 0, 42, 0, 17, 0, 0, 0, 12, 0, 1, 0, 0, 0, 5, 0, 18, 0, 19, 0, 0, 0, 1, 0, 20, 0, 21, 0, 1, 0, 13, 0, 0, 0, 47, 0, 1, 0, 1, 0, 0, 0, 5, 42, -76, 0, 23, -80, 0, 0, 0, 2, 0, 16, 0, 0, 0, 6, 0, 1, 0, 0, 0, 54, 0, 17, 0, 0, 0, 12, 0, 1, 0, 0, 0, 5, 0, 18, 0, 19, 0, 0, 0, 1, 0, 24, 0, 25, 0, 1, 0, 13, 0, 0, 0, 62, 0, 2, 0, 2, 0, 0, 0, 6, 42, 43, -75, 0, 23, -79, 0, 0, 0, 2, 0, 16, 0, 0, 0, 10, 0, 2, 0, 0, 0, 62, 0, 5, 0, 63, 0, 17, 0, 0, 0, 22, 0, 2, 0, 0, 0, 6, 0, 18, 0, 19, 0, 0, 0, 0, 0, 6, 0, 9, 0, 10, 0, 1, 0, 2, 0, 26, 0, 27, 0, 2, 0, 28, 0, 0, 0, 6, 0, 2, 0, 30, 0, 32, 0, 13, 0, 0, 0, 61, 0, 1, 0, 2, 0, 0, 0, 5, 43, -74, 0, 37, -79, 0, 0, 0, 2, 0, 16, 0, 0, 0, 10, 0, 2, 0, 0, 0, 66, 0, 4, 0, 67, 0, 17, 0, 0, 0, 22, 0, 2, 0, 0, 0, 5, 0, 18, 0, 19, 0, 0, 0, 0, 0, 5, 0, 38, 0, 39, 0, 1, 0, 1, 0, 40, 0, 27, 0, 2, 0, 28, 0, 0, 0, 6, 0, 2, 0, 30, 0, 32, 0, 13, 0, 0, 0, 62, 0, 2, 0, 2, 0, 0, 0, 6, 42, 43, -73, 0, 42, -79, 0, 0, 0, 2, 0, 16, 0, 0, 0, 10, 0, 2, 0, 0, 0, 70, 0, 5, 0, 71, 0, 17, 0, 0, 0, 22, 0, 2, 0, 0, 0, 6, 0, 18, 0, 19, 0, 0, 0, 0, 0, 6, 0, 38, 0, 39, 0, 1, 0, 1, 0, 43, 0, 0, 0, 2, 0, 44 }; private static ExtenderClassLoader classloader = new ExtenderClassLoader(); /** * A HashMap containing {@link java.lang.Class classes} which have already * been generated at runtime. */ protected static HashMap classes = new HashMap(); /** * A HashMap containing the serialized form of prototype objects * from classes for which an instance was extended at runtime. */ protected static HashMap prototypes = new HashMap(); /** * Converts a string to an array of bytes. * * @param s the string to convert. * @return the resulting array. */ public static byte[] StringToByteArray(String s) { byte [] res = new byte[s.length()]; for (int i=0; i<res.length; i++) res[i] = (byte)s.charAt(i); return res; } /** * Extends a class at runtime. The resulting class supports * <code>public void setMetaData(Object)</code> and * <code>public Object getMetaData()</code> which both can be * used by using javas reflection mechanism {@link #main(String [])}. * * @param cl the class to extend * @return a new class which is a subclass of <code>cl</code> */ public static Class extendClass(Class cl) { try { String name = cl.getName().replace('.','/'); if (classes.containsKey(name)) return (Class)classes.get(name); String classname = cl.getName().replace('.','_')+"_Extended"; byte [] code = new byte[extenderByteCodeStart.length+extenderByteCodeMiddle.length+extenderByteCodeEnd.length+name.length()+classname.length()+2]; int pos=0; System.arraycopy(extenderByteCodeStart,0,code,pos,extenderByteCodeStart.length); code[pos+=extenderByteCodeStart.length]=(byte)classname.length(); byte [] classnamea = StringToByteArray(classname); System.arraycopy(classnamea,0,code,pos+=1,classnamea.length); System.arraycopy(extenderByteCodeMiddle,0,code,pos+=classnamea.length,extenderByteCodeMiddle.length); code[pos+=extenderByteCodeMiddle.length]=(byte)name.length(); byte [] namea = StringToByteArray(name); System.arraycopy(namea,0,code,pos+=1,namea.length); System.arraycopy(extenderByteCodeEnd,0,code,pos+=namea.length,extenderByteCodeEnd.length); Class result = classloader.load(classname,code,0,code.length); classes.put(name,result); return result; } catch (Exception e) { } return null; } /** * Returns a modified version of the given object which is a member * of the subclass defined in {@link #extendClass(Class)}. * * @param object the source object * @return a new object which is a modified version of <source>object</source> */ public static Object extendObject(Serializable object) { try { final Class classOfObject = extendClass(object.getClass()); String name = classOfObject.getName(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); (new ObjectOutputStream(baos)).writeObject(object); byte [] serializedObject = baos.toByteArray(); byte [] prototype; if (prototypes.containsKey(name)) { prototype = (byte [])prototypes.get(name); } else { Object prototypeinstance = classOfObject.newInstance(); ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); (new ObjectOutputStream(baos2)).writeObject(prototypeinstance); prototype = baos2.toByteArray(); prototypes.put(name,prototype); } byte [] serializedNewObject = new byte[serializedObject.length+48+name.length()]; System.arraycopy(prototype,0,serializedNewObject,0,54+name.length()); System.arraycopy(serializedObject,7,serializedNewObject,54+name.length(),serializedObject.length-7); serializedNewObject[serializedNewObject.length-1] = (byte)112; ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(serializedNewObject)) { protected Class resolveClass (ObjectStreamClass desc) throws IOException, ClassNotFoundException { if (desc.getName().equals(classOfObject.getName())) return classOfObject; else return super.resolveClass(desc); } }; return in.readObject(); } catch (Exception e) { } return null; } }