001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.bind.v2.bytecode;
038:
039: import java.io.ByteArrayOutputStream;
040: import java.io.DataInputStream;
041: import java.io.DataOutputStream;
042: import java.io.IOException;
043: import java.io.InputStream;
044: import java.util.logging.Level;
045: import java.util.logging.Logger;
046:
047: import com.sun.xml.bind.Util;
048:
049: /**
050: * Replaces a few constant pool tokens from a class "template" and then loads it into the VM.
051: *
052: * @author Kohsuke Kawaguchi
053: */
054: public final class ClassTailor {
055:
056: private ClassTailor() {
057: } // no instanciation please
058:
059: private static final Logger logger = Util.getClassLogger();
060:
061: /**
062: * Returns the class name in the JVM format (such as "java/lang/String")
063: */
064: public static String toVMClassName(Class c) {
065: assert !c.isPrimitive();
066: if (c.isArray())
067: // I have no idea why it is designed like this, but javap says so.
068: return toVMTypeName(c);
069: return c.getName().replace('.', '/');
070: }
071:
072: public static String toVMTypeName(Class c) {
073: if (c.isArray()) {
074: // TODO: study how an array type is encoded.
075: return '[' + toVMTypeName(c.getComponentType());
076: }
077: if (c.isPrimitive()) {
078: if (c == Boolean.TYPE)
079: return "Z";
080: if (c == Character.TYPE)
081: return "C";
082: if (c == Byte.TYPE)
083: return "B";
084: if (c == Double.TYPE)
085: return "D";
086: if (c == Float.TYPE)
087: return "F";
088: if (c == Integer.TYPE)
089: return "I";
090: if (c == Long.TYPE)
091: return "J";
092: if (c == Short.TYPE)
093: return "S";
094:
095: throw new IllegalArgumentException(c.getName());
096: }
097: return 'L' + c.getName().replace('.', '/') + ';';
098: }
099:
100: public static byte[] tailor(Class templateClass,
101: String newClassName, String... replacements) {
102: String vmname = toVMClassName(templateClass);
103: return tailor(templateClass.getClassLoader()
104: .getResourceAsStream(vmname + ".class"), vmname,
105: newClassName, replacements);
106: }
107:
108: /**
109: * Customizes a class file by replacing constant pools.
110: *
111: * @param image
112: * The image of the template class.
113: * @param replacements
114: * A list of pair of strings that specify the substitution
115: * {@code String[]{search_0, replace_0, search_1, replace_1, ..., search_n, replace_n }}
116: *
117: * The search strings found in the constant pool will be replaced by the corresponding
118: * replacement string.
119: */
120: public static byte[] tailor(InputStream image,
121: String templateClassName, String newClassName,
122: String... replacements) {
123: DataInputStream in = new DataInputStream(image);
124:
125: try {
126: ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
127: DataOutputStream out = new DataOutputStream(baos);
128:
129: // skip until the constant pool count
130: long l = in.readLong();
131: out.writeLong(l);
132:
133: // read the constant pool size
134: short count = in.readShort();
135: out.writeShort(count);
136:
137: // replace constant pools
138: for (int i = 0; i < count; i++) {
139: byte tag = in.readByte();
140: out.writeByte(tag);
141: switch (tag) {
142: case 0:
143: // this isn't described in the spec,
144: // but class files often seem to have this '0' tag.
145: // we can apparently just ignore it, but not sure
146: // what this really means.
147: break;
148:
149: case 1: // CONSTANT_UTF8
150: {
151: String value = in.readUTF();
152: if (value.equals(templateClassName))
153: value = newClassName;
154: else {
155: for (int j = 0; j < replacements.length; j += 2)
156: if (value.equals(replacements[j])) {
157: value = replacements[j + 1];
158: break;
159: }
160: }
161: out.writeUTF(value);
162: }
163: break;
164:
165: case 3: // CONSTANT_Integer
166: case 4: // CONSTANT_Float
167: out.writeInt(in.readInt());
168: break;
169:
170: case 5: // CONSTANT_Long
171: case 6: // CONSTANT_Double
172: i++; // doubles and longs take two entries
173: out.writeLong(in.readLong());
174: break;
175:
176: case 7: // CONSTANT_Class
177: case 8: // CONSTANT_String
178: out.writeShort(in.readShort());
179: break;
180:
181: case 9: // CONSTANT_Fieldref
182: case 10: // CONSTANT_Methodref
183: case 11: // CONSTANT_InterfaceMethodref
184: case 12: // CONSTANT_NameAndType
185: out.writeInt(in.readInt());
186: break;
187:
188: default:
189: throw new IllegalArgumentException(
190: "Unknown constant type " + tag);
191: }
192: }
193:
194: // then copy the rest
195: byte[] buf = new byte[512];
196: int len;
197: while ((len = in.read(buf)) > 0)
198: out.write(buf, 0, len);
199:
200: in.close();
201: out.close();
202:
203: // by now we got the properly tailored class file image
204: return baos.toByteArray();
205:
206: } catch (IOException e) {
207: // never happen
208: logger.log(Level.WARNING, "failed to tailor", e);
209: return null;
210: }
211: }
212: }
|