/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