/Users/lyon/j4p/src/javassist/SerialVersionUID.java
|
1 /*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16 package javassist;
17
18 import java.io.*;
19
20 import javassist.bytecode.*;
21
22 import java.util.*;
23 import java.security.*;
24
25 /**
26 * Utility for calculating serialVersionUIDs for Serializable classes.
27 *
28 * @author Bob Lee (crazybob@crazybob.org)
29 * @author modified by Shigeru Chiba
30 */
31 public class SerialVersionUID {
32
33 /**
34 * Adds serialVersionUID if one does not already exist. Call this before
35 * modifying a class to maintain serialization compatability.
36 */
37 public static void setSerialVersionUID(CtClass clazz)
38 throws CannotCompileException, NotFoundException {
39 // check for pre-existing field.
40 try {
41 clazz.getDeclaredField("serialVersionUID");
42 return;
43 } catch (NotFoundException e) {
44 }
45
46 // check if the class is serializable.
47 if (!isSerializable(clazz))
48 return;
49
50 // add field with default value.
51 CtField field = new CtField(CtClass.longType, "serialVersionUID",
52 clazz);
53 field.setModifiers(Modifier.PRIVATE | Modifier.STATIC |
54 Modifier.FINAL);
55 clazz.addField(field, calculateDefault(clazz) + "L");
56 }
57
58 /**
59 * Does the class implement Serializable?
60 */
61 private static boolean isSerializable(CtClass clazz)
62 throws NotFoundException {
63 ClassPool pool = clazz.getClassPool();
64 return clazz.subtypeOf(pool.get("java.io.Serializable"));
65 }
66
67 /**
68 * Calculate default value. See Java Serialization Specification, Stream
69 * Unique Identifiers.
70 */
71 static long calculateDefault(CtClass clazz)
72 throws CannotCompileException {
73 try {
74 ByteArrayOutputStream bout = new ByteArrayOutputStream();
75 DataOutputStream out = new DataOutputStream(bout);
76 ClassFile classFile = clazz.getClassFile();
77
78 // class name.
79 String javaName = javaName(clazz);
80 out.writeUTF(javaName);
81
82 // class modifiers.
83 out.writeInt(clazz.getModifiers() & (Modifier.PUBLIC |
84 Modifier.FINAL | Modifier.INTERFACE | Modifier.ABSTRACT));
85
86 // interfaces.
87 String[] interfaces = classFile.getInterfaces();
88 for (int i = 0; i < interfaces.length; i++)
89 interfaces[i] = javaName(interfaces[i]);
90
91 Arrays.sort(interfaces);
92 for (int i = 0; i < interfaces.length; i++)
93 out.writeUTF(interfaces[i]);
94
95 // fields.
96 CtField[] fields = clazz.getDeclaredFields();
97 Arrays.sort(fields, new Comparator() {
98 public int compare(Object o1, Object o2) {
99 CtField field1 = (CtField) o1;
100 CtField field2 = (CtField) o2;
101 return field1.getName().compareTo(field2.getName());
102 }
103 });
104
105 for (int i = 0; i < fields.length; i++) {
106 CtField field = (CtField) fields[i];
107 int mods = field.getModifiers();
108 if (((mods & Modifier.PRIVATE) == 0) ||
109 ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0)) {
110 out.writeUTF(field.getName());
111 out.writeInt(mods);
112 out.writeUTF(field.getFieldInfo2().getDescriptor());
113 }
114 }
115
116 // static initializer.
117 if (classFile.getStaticInitializer() != null) {
118 out.writeUTF("<clinit>");
119 out.writeInt(Modifier.STATIC);
120 out.writeUTF("()V");
121 }
122
123 // constructors.
124 CtConstructor[] constructors = clazz.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.getMethodInfo2().getDescriptor().compareTo(
130 c2.getMethodInfo2().getDescriptor());
131 }
132 });
133
134 for (int i = 0; i < constructors.length; i++) {
135 CtConstructor constructor = constructors[i];
136 int mods = constructor.getModifiers();
137 if ((mods & Modifier.PRIVATE) == 0) {
138 out.writeUTF("<init>");
139 out.writeInt(mods);
140 out.writeUTF(constructor.getMethodInfo2()
141 .getDescriptor().replace('/', '.'));
142 }
143 }
144
145 // methods.
146 CtMethod[] methods = clazz.getDeclaredMethods();
147 Arrays.sort(methods, new Comparator() {
148 public int compare(Object o1, Object o2) {
149 CtMethod m1 = (CtMethod) o1;
150 CtMethod m2 = (CtMethod) o2;
151 int value = m1.getName().compareTo(m2.getName());
152 if (value == 0)
153 value = m1.getMethodInfo2().getDescriptor()
154 .compareTo(m2.getMethodInfo2().getDescriptor());
155
156 return value;
157 }
158 });
159
160 for (int i = 0; i < methods.length; i++) {
161 CtMethod method = methods[i];
162 int mods = method.getModifiers();
163 if ((mods & Modifier.PRIVATE) == 0) {
164 out.writeUTF(method.getName());
165 out.writeInt(mods);
166 out.writeUTF(method.getMethodInfo2()
167 .getDescriptor().replace('/', '.'));
168 }
169 }
170
171 // calculate hash.
172 out.flush();
173 MessageDigest digest = MessageDigest.getInstance("SHA");
174 byte[] digested = digest.digest(bout.toByteArray());
175 long hash = 0;
176 for (int i = Math.min(digested.length, 8) - 1; i >= 0; i--)
177 hash = (hash << 8) | (digested[i] & 0xFF);
178
179 return hash;
180 } catch (IOException e) {
181 throw new CannotCompileException(e);
182 } catch (NoSuchAlgorithmException e) {
183 throw new CannotCompileException(e);
184 }
185 }
186
187 private static String javaName(CtClass clazz) {
188 return Descriptor.toJavaName(Descriptor.toJvmName(clazz));
189 }
190
191 private static String javaName(String name) {
192 return Descriptor.toJavaName(Descriptor.toJvmName(name));
193 }
194 }
195