001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist;
017:
018: import java.io.*;
019: import javassist.bytecode.*;
020: import java.util.*;
021: import java.security.*;
022:
023: /**
024: * Utility for calculating serialVersionUIDs for Serializable classes.
025: *
026: * @author Bob Lee (crazybob@crazybob.org)
027: * @author modified by Shigeru Chiba
028: */
029: public class SerialVersionUID {
030:
031: /**
032: * Adds serialVersionUID if one does not already exist. Call this before
033: * modifying a class to maintain serialization compatability.
034: */
035: public static void setSerialVersionUID(CtClass clazz)
036: throws CannotCompileException, NotFoundException {
037: // check for pre-existing field.
038: try {
039: clazz.getDeclaredField("serialVersionUID");
040: return;
041: } catch (NotFoundException e) {
042: }
043:
044: // check if the class is serializable.
045: if (!isSerializable(clazz))
046: return;
047:
048: // add field with default value.
049: CtField field = new CtField(CtClass.longType,
050: "serialVersionUID", clazz);
051: field.setModifiers(Modifier.PRIVATE | Modifier.STATIC
052: | Modifier.FINAL);
053: clazz.addField(field, calculateDefault(clazz) + "L");
054: }
055:
056: /**
057: * Does the class implement Serializable?
058: */
059: private static boolean isSerializable(CtClass clazz)
060: throws NotFoundException {
061: ClassPool pool = clazz.getClassPool();
062: return clazz.subtypeOf(pool.get("java.io.Serializable"));
063: }
064:
065: /**
066: * Calculate default value. See Java Serialization Specification, Stream
067: * Unique Identifiers.
068: */
069: static long calculateDefault(CtClass clazz)
070: throws CannotCompileException {
071: try {
072: ByteArrayOutputStream bout = new ByteArrayOutputStream();
073: DataOutputStream out = new DataOutputStream(bout);
074: ClassFile classFile = clazz.getClassFile();
075:
076: // class name.
077: String javaName = javaName(clazz);
078: out.writeUTF(javaName);
079:
080: // class modifiers.
081: out.writeInt(clazz.getModifiers()
082: & (Modifier.PUBLIC | Modifier.FINAL
083: | Modifier.INTERFACE | Modifier.ABSTRACT));
084:
085: // interfaces.
086: String[] interfaces = classFile.getInterfaces();
087: for (int i = 0; i < interfaces.length; i++)
088: interfaces[i] = javaName(interfaces[i]);
089:
090: Arrays.sort(interfaces);
091: for (int i = 0; i < interfaces.length; i++)
092: out.writeUTF(interfaces[i]);
093:
094: // fields.
095: CtField[] fields = clazz.getDeclaredFields();
096: Arrays.sort(fields, new Comparator() {
097: public int compare(Object o1, Object o2) {
098: CtField field1 = (CtField) o1;
099: CtField field2 = (CtField) o2;
100: return field1.getName().compareTo(field2.getName());
101: }
102: });
103:
104: for (int i = 0; i < fields.length; i++) {
105: CtField field = (CtField) fields[i];
106: int mods = field.getModifiers();
107: if (((mods & Modifier.PRIVATE) == 0)
108: || ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0)) {
109: out.writeUTF(field.getName());
110: out.writeInt(mods);
111: out.writeUTF(field.getFieldInfo2().getDescriptor());
112: }
113: }
114:
115: // static initializer.
116: if (classFile.getStaticInitializer() != null) {
117: out.writeUTF("<clinit>");
118: out.writeInt(Modifier.STATIC);
119: out.writeUTF("()V");
120: }
121:
122: // constructors.
123: CtConstructor[] constructors = clazz
124: .getDeclaredConstructors();
125: Arrays.sort(constructors, new Comparator() {
126: public int compare(Object o1, Object o2) {
127: CtConstructor c1 = (CtConstructor) o1;
128: CtConstructor c2 = (CtConstructor) o2;
129: return c1
130: .getMethodInfo2()
131: .getDescriptor()
132: .compareTo(
133: c2.getMethodInfo2().getDescriptor());
134: }
135: });
136:
137: for (int i = 0; i < constructors.length; i++) {
138: CtConstructor constructor = constructors[i];
139: int mods = constructor.getModifiers();
140: if ((mods & Modifier.PRIVATE) == 0) {
141: out.writeUTF("<init>");
142: out.writeInt(mods);
143: out.writeUTF(constructor.getMethodInfo2()
144: .getDescriptor().replace('/', '.'));
145: }
146: }
147:
148: // methods.
149: CtMethod[] methods = clazz.getDeclaredMethods();
150: Arrays.sort(methods, new Comparator() {
151: public int compare(Object o1, Object o2) {
152: CtMethod m1 = (CtMethod) o1;
153: CtMethod m2 = (CtMethod) o2;
154: int value = m1.getName().compareTo(m2.getName());
155: if (value == 0)
156: value = m1.getMethodInfo2().getDescriptor()
157: .compareTo(
158: m2.getMethodInfo2()
159: .getDescriptor());
160:
161: return value;
162: }
163: });
164:
165: for (int i = 0; i < methods.length; i++) {
166: CtMethod method = methods[i];
167: int mods = method.getModifiers();
168: if ((mods & Modifier.PRIVATE) == 0) {
169: out.writeUTF(method.getName());
170: out.writeInt(mods);
171: out.writeUTF(method.getMethodInfo2()
172: .getDescriptor().replace('/', '.'));
173: }
174: }
175:
176: // calculate hash.
177: out.flush();
178: MessageDigest digest = MessageDigest.getInstance("SHA");
179: byte[] digested = digest.digest(bout.toByteArray());
180: long hash = 0;
181: for (int i = Math.min(digested.length, 8) - 1; i >= 0; i--)
182: hash = (hash << 8) | (digested[i] & 0xFF);
183:
184: return hash;
185: } catch (IOException e) {
186: throw new CannotCompileException(e);
187: } catch (NoSuchAlgorithmException e) {
188: throw new CannotCompileException(e);
189: }
190: }
191:
192: private static String javaName(CtClass clazz) {
193: return Descriptor.toJavaName(Descriptor.toJvmName(clazz));
194: }
195:
196: private static String javaName(String name) {
197: return Descriptor.toJavaName(Descriptor.toJvmName(name));
198: }
199: }
|