/*
 * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.xml.internal.bind.v2.bytecode;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.sun.xml.internal.bind.Util;

Replaces a few constant pool tokens from a class "template" and then loads it into the VM.
Author:Kohsuke Kawaguchi
/** * Replaces a few constant pool tokens from a class "template" and then loads it into the VM. * * @author Kohsuke Kawaguchi */
public final class ClassTailor { private ClassTailor() {} // no instanciation please private static final Logger logger = Util.getClassLogger();
Returns the class name in the JVM format (such as "java/lang/String")
/** * Returns the class name in the JVM format (such as "java/lang/String") */
public static String toVMClassName( Class c ) { assert !c.isPrimitive(); if(c.isArray()) // I have no idea why it is designed like this, but javap says so. return toVMTypeName(c); return c.getName().replace('.','/'); } public static String toVMTypeName( Class c ) { if(c.isArray()) { // TODO: study how an array type is encoded. return '['+toVMTypeName(c.getComponentType()); } if(c.isPrimitive()) { if(c==Boolean.TYPE) return "Z"; if(c==Character.TYPE) return "C"; if(c==Byte.TYPE) return "B"; if(c==Double.TYPE) return "D"; if(c==Float.TYPE) return "F"; if(c==Integer.TYPE) return "I"; if(c==Long.TYPE) return "J"; if(c==Short.TYPE) return "S"; throw new IllegalArgumentException(c.getName()); } return 'L'+c.getName().replace('.','/')+';'; } public static byte[] tailor( Class templateClass, String newClassName, String... replacements ) { String vmname = toVMClassName(templateClass); return tailor( SecureLoader.getClassClassLoader(templateClass).getResourceAsStream(vmname+".class"), vmname, newClassName, replacements ); }
Customizes a class file by replacing constant pools.
Params:
  • image – The image of the template class.
  • replacements – A list of pair of strings that specify the substitution String[]{search_0, replace_0, search_1, replace_1, ..., search_n, replace_n } The search strings found in the constant pool will be replaced by the corresponding replacement string.
/** * Customizes a class file by replacing constant pools. * * @param image * The image of the template class. * @param replacements * A list of pair of strings that specify the substitution * {@code String[]{search_0, replace_0, search_1, replace_1, ..., search_n, replace_n }} * * The search strings found in the constant pool will be replaced by the corresponding * replacement string. */
public static byte[] tailor( InputStream image, String templateClassName, String newClassName, String... replacements ) { DataInputStream in = new DataInputStream(image); try { ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); DataOutputStream out = new DataOutputStream(baos); // skip until the constant pool count long l = in.readLong(); out.writeLong(l); // read the constant pool size short count = in.readShort(); out.writeShort(count); // replace constant pools for( int i=0; i<count; i++ ) { byte tag = in.readByte(); out.writeByte(tag); switch(tag) { case 0: // this isn't described in the spec, // but class files often seem to have this '0' tag. // we can apparently just ignore it, but not sure // what this really means. break; case 1: // CONSTANT_UTF8 { String value = in.readUTF(); if(value.equals(templateClassName)) value = newClassName; else { for( int j=0; j<replacements.length; j+=2 ) if(value.equals(replacements[j])) { value = replacements[j+1]; break; } } out.writeUTF(value); } break; case 3: // CONSTANT_Integer case 4: // CONSTANT_Float out.writeInt(in.readInt()); break; case 5: // CONSTANT_Long case 6: // CONSTANT_Double i++; // doubles and longs take two entries out.writeLong(in.readLong()); break; case 7: // CONSTANT_Class case 8: // CONSTANT_String out.writeShort(in.readShort()); break; case 9: // CONSTANT_Fieldref case 10: // CONSTANT_Methodref case 11: // CONSTANT_InterfaceMethodref case 12: // CONSTANT_NameAndType out.writeInt(in.readInt()); break; default: throw new IllegalArgumentException("Unknown constant type "+tag); } } // then copy the rest byte[] buf = new byte[512]; int len; while((len=in.read(buf))>0) out.write(buf,0,len); in.close(); out.close(); // by now we got the properly tailored class file image return baos.toByteArray(); } catch( IOException e ) { // never happen logger.log(Level.WARNING,"failed to tailor",e); return null; } } }