001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.xml.internal.bind.v2.bytecode;
027:
028: import java.io.ByteArrayOutputStream;
029: import java.io.DataInputStream;
030: import java.io.DataOutputStream;
031: import java.io.IOException;
032: import java.io.InputStream;
033: import java.util.logging.Level;
034: import java.util.logging.Logger;
035:
036: import com.sun.xml.internal.bind.Util;
037:
038: /**
039: * Replaces a few constant pool tokens from a class "template" and then loads it into the VM.
040: *
041: * @author Kohsuke Kawaguchi
042: */
043: public final class ClassTailor {
044:
045: private ClassTailor() {
046: } // no instanciation please
047:
048: private static final Logger logger = Util.getClassLogger();
049:
050: /**
051: * Returns the class name in the JVM format (such as "java/lang/String")
052: */
053: public static final String toVMClassName(Class c) {
054: assert !c.isPrimitive();
055: if (c.isArray())
056: // I have no idea why it is designed like this, but javap says so.
057: return toVMTypeName(c);
058: return c.getName().replace('.', '/');
059: }
060:
061: public static final String toVMTypeName(Class c) {
062: if (c.isArray()) {
063: // TODO: study how an array type is encoded.
064: return '[' + toVMTypeName(c.getComponentType());
065: }
066: if (c.isPrimitive()) {
067: if (c == Boolean.TYPE)
068: return "Z";
069: if (c == Character.TYPE)
070: return "C";
071: if (c == Byte.TYPE)
072: return "B";
073: if (c == Double.TYPE)
074: return "D";
075: if (c == Float.TYPE)
076: return "F";
077: if (c == Integer.TYPE)
078: return "I";
079: if (c == Long.TYPE)
080: return "J";
081: if (c == Short.TYPE)
082: return "S";
083:
084: throw new IllegalArgumentException(c.getName());
085: }
086: return 'L' + c.getName().replace('.', '/') + ';';
087: }
088:
089: public static final byte[] tailor(Class templateClass,
090: String newClassName, String... replacements) {
091: String vmname = toVMClassName(templateClass);
092: return tailor(templateClass.getClassLoader()
093: .getResourceAsStream(vmname + ".class"), vmname,
094: newClassName, replacements);
095: }
096:
097: /**
098: * Customizes a class file by replacing constant pools.
099: *
100: * @param image
101: * The image of the template class.
102: * @param replacements
103: * A list of pair of strings that specify the substitution
104: * {@code String[]{search_0, replace_0, search_1, replace_1, ..., search_n, replace_n }}
105: *
106: * The search strings found in the constant pool will be replaced by the corresponding
107: * replacement string.
108: */
109: public static final byte[] tailor(InputStream image,
110: String templateClassName, String newClassName,
111: String... replacements) {
112: DataInputStream in = new DataInputStream(image);
113:
114: try {
115: ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
116: DataOutputStream out = new DataOutputStream(baos);
117:
118: // skip until the constant pool count
119: long l = in.readLong();
120: out.writeLong(l);
121:
122: // read the constant pool size
123: short count = in.readShort();
124: out.writeShort(count);
125:
126: // replace constant pools
127: for (int i = 0; i < count; i++) {
128: byte tag = in.readByte();
129: out.writeByte(tag);
130: switch (tag) {
131: case 0:
132: // this isn't described in the spec,
133: // but class files often seem to have this '0' tag.
134: // we can apparently just ignore it, but not sure
135: // what this really means.
136: break;
137:
138: case 1: // CONSTANT_UTF8
139: {
140: String value = in.readUTF();
141: if (value.equals(templateClassName))
142: value = newClassName;
143: else {
144: for (int j = 0; j < replacements.length; j += 2)
145: if (value.equals(replacements[j])) {
146: value = replacements[j + 1];
147: break;
148: }
149: }
150: out.writeUTF(value);
151: }
152: break;
153:
154: case 3: // CONSTANT_Integer
155: case 4: // CONSTANT_Float
156: out.writeInt(in.readInt());
157: break;
158:
159: case 5: // CONSTANT_Long
160: case 6: // CONSTANT_Double
161: i++; // doubles and longs take two entries
162: out.writeLong(in.readLong());
163: break;
164:
165: case 7: // CONSTANT_Class
166: case 8: // CONSTANT_String
167: out.writeShort(in.readShort());
168: break;
169:
170: case 9: // CONSTANT_Fieldref
171: case 10: // CONSTANT_Methodref
172: case 11: // CONSTANT_InterfaceMethodref
173: case 12: // CONSTANT_NameAndType
174: out.writeInt(in.readInt());
175: break;
176:
177: default:
178: throw new IllegalArgumentException(
179: "Unknown constant type " + tag);
180: }
181: }
182:
183: // then copy the rest
184: byte[] buf = new byte[512];
185: int len;
186: while ((len = in.read(buf)) > 0)
187: out.write(buf, 0, len);
188:
189: in.close();
190: out.close();
191:
192: // by now we got the properly tailored class file image
193: return baos.toByteArray();
194:
195: } catch (IOException e) {
196: // never happen
197: logger.log(Level.WARNING, "failed to tailor", e);
198: return null;
199: }
200: }
201: }
|