001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package EDU.purdue.cs.bloat.editor;
022:
023: import java.io.*;
024: import java.lang.reflect.*;
025: import java.security.*;
026: import java.util.*;
027:
028: import EDU.purdue.cs.bloat.reflect.*;
029:
030: /**
031: * <P>
032: * This class computes the serial version UID of a class modeled by a
033: * <code>ClassEditor</code>. Otherwise, we would have to load the class in
034: * order to compute its serial version UID. That would suck.
035: * </P>
036: *
037: * <P>
038: * The algorithm for computing the serial version UID can be found in the <A
039: * href="http://java.sun.com/j2se/1.3/docs/guide/serialization/spec">serialization
040: * spec</A>
041: * </P>
042: */
043: public class SerialVersionUID {
044:
045: /**
046: * Returns <code>true</code> if the class modeled by the given
047: * <code>ClassEditor</code> implements {@link java.io.Serializable
048: * Serializable}. It checks superclasses.
049: */
050: public static boolean implements Serializable(final ClassEditor ce) {
051: if (ce.type().equals(Type.OBJECT)) {
052: // Stop the recursion!
053: return (false);
054: }
055:
056: final Type serializable = Type
057: .getType("Ljava/io/Serializable;");
058: final Type[] interfaces = ce.interfaces();
059: for (int i = 0; i < interfaces.length; i++) {
060: if (interfaces[i].equals(serializable)) {
061: return (true);
062: }
063: }
064:
065: // Does its superclass implement Serializable?
066:
067: final Type super class = ce.super class();
068: final ClassInfoLoader loader = ce.classInfo().loader();
069: try {
070: final ClassInfo ci = loader.loadClass(super class
071: .className());
072: final ClassEditor sce = new ClassEditor(ce.context(), ci);
073: return (SerialVersionUID.implements Serializable(sce));
074:
075: } catch (final ClassNotFoundException ex) {
076: System.err.println("Could not load class: " + super class
077: + ", superclass of " + ce.name());
078: System.exit(1);
079: }
080: return (false);
081: }
082:
083: /**
084: * Returns the serial version UID of the class modeled by the given
085: * <code>ClassEditor</code>.
086: *
087: * @param ce
088: * The class must implement {@link java.io.Serializable
089: * Serializable}
090: */
091: public static long serialVersionUID(final ClassEditor ce) {
092: // Make sure the class implements Serializable
093: if (!SerialVersionUID.implements Serializable(ce)) {
094: final String s = "Class " + ce.name()
095: + " does not implement java.io.Serializable";
096: throw new IllegalArgumentException(s);
097: }
098:
099: // If the class already has a serialVersionUID, return that
100: final FieldInfo[] fields = ce.fields();
101: for (int i = 0; i < fields.length; i++) {
102: final FieldEditor fe = new FieldEditor(ce, fields[i]);
103: if (fe.name().equals("serialVersionUID")) {
104: final Object value = fe.constantValue();
105: if (value != null) {
106: if (value instanceof Long) {
107: return (((Long) value).longValue());
108: }
109: }
110: }
111: }
112:
113: // Now, compute the digest of the bytes using SHA
114: MessageDigest algorithm = null;
115: try {
116: algorithm = MessageDigest.getInstance("SHA");
117:
118: } catch (final NoSuchAlgorithmException ex) {
119: final String s = "Can't use SHA-1 message digest algorith!";
120: throw new IllegalArgumentException(s);
121: }
122:
123: final DataOutputStream dos = new DataOutputStream(
124: new DigestOutputStream(new ByteArrayOutputStream(),
125: algorithm));
126:
127: try {
128: // Write a bunch of information about the class to the output
129: // stream
130: SerialVersionUID.writeClassName(ce, dos);
131: SerialVersionUID.writeClassModifiers(ce, dos);
132: SerialVersionUID.writeInterfaceNames(ce, dos);
133: SerialVersionUID.writeFields(ce, dos);
134: SerialVersionUID.writeStaticInitializer(ce, dos);
135: SerialVersionUID.writeConstructors(ce, dos);
136: SerialVersionUID.writeMethods(ce, dos);
137:
138: dos.flush();
139: dos.close();
140:
141: } catch (final IOException ex) {
142: final String s = ("While computing serial version UID: " + ex);
143: throw new IllegalArgumentException(s);
144: }
145:
146: // Compute the hash value from the first 64 bites of the digest
147: final byte[] digest = algorithm.digest();
148: long uid = 0;
149: for (int i = 0; i < Math.min(8, digest.length); i++) {
150: uid += (long) (digest[i] & 255) << (i * 8);
151: }
152: return (uid);
153:
154: }
155:
156: /**
157: * Writes the name of the class to the data output stream
158: */
159: private static void writeClassName(final ClassEditor ce,
160: final DataOutputStream dos) throws IOException {
161: dos.writeUTF(ce.name().replace('/', '.'));
162: }
163:
164: /**
165: * Returns the Java reflection modifiers for a given class
166: */
167: static int getModifiers(final ClassEditor ce) {
168: // Translate BLOAT's class modifiers into Java's reflection
169: // modifiers
170: int modifiers = 0;
171:
172: if (ce.isPublic()) {
173: modifiers |= Modifier.PUBLIC;
174: }
175:
176: if (ce.isPrivate()) {
177: modifiers |= Modifier.PRIVATE;
178: }
179:
180: if (ce.isProtected()) {
181: modifiers |= Modifier.PROTECTED;
182: }
183:
184: if (ce.isStatic()) {
185: modifiers |= Modifier.STATIC;
186: }
187:
188: if (ce.isFinal()) {
189: modifiers |= Modifier.FINAL;
190: }
191:
192: if (ce.isAbstract()) {
193: modifiers |= Modifier.ABSTRACT;
194: }
195:
196: if (ce.isInterface()) {
197: modifiers |= Modifier.INTERFACE;
198: }
199:
200: return (modifiers);
201: }
202:
203: /**
204: * Writes the class's modifiers to the output stream
205: */
206: private static void writeClassModifiers(final ClassEditor ce,
207: final DataOutputStream dos) throws IOException {
208: dos.writeInt(SerialVersionUID.getModifiers(ce));
209: }
210:
211: /**
212: * Writes the names of the interfaces implemented by the class to the output
213: * stream
214: */
215: private static void writeInterfaceNames(final ClassEditor ce,
216: final DataOutputStream dos) throws IOException {
217:
218: // Sort interfaces by name
219: final SortedSet sorted = new TreeSet();
220:
221: final Type[] interfaces = ce.interfaces();
222: for (int i = 0; i < interfaces.length; i++) {
223: sorted.add(interfaces[i].className().replace('/', '.'));
224: }
225:
226: final Iterator iter = sorted.iterator();
227: while (iter.hasNext()) {
228: final String name = (String) iter.next();
229: dos.writeUTF(name);
230: }
231:
232: }
233:
234: /**
235: * Returns the Java reflection modifiers for a field
236: */
237: static int getModifiers(final FieldEditor fe) {
238: int modifiers = 0;
239:
240: if (fe.isPublic()) {
241: modifiers |= Modifier.PUBLIC;
242: }
243:
244: if (fe.isPrivate()) {
245: modifiers |= Modifier.PRIVATE;
246: }
247:
248: if (fe.isProtected()) {
249: modifiers |= Modifier.PROTECTED;
250: }
251:
252: if (fe.isPackage()) {
253: // Nothing
254: }
255:
256: if (fe.isStatic()) {
257: modifiers |= Modifier.STATIC;
258: }
259:
260: if (fe.isFinal()) {
261: modifiers |= Modifier.FINAL;
262: }
263:
264: if (fe.isVolatile()) {
265: modifiers |= Modifier.VOLATILE;
266: }
267:
268: if (fe.isTransient()) {
269: // Kind of a moot point
270: modifiers |= Modifier.TRANSIENT;
271: }
272:
273: return (modifiers);
274: }
275:
276: /**
277: * Writes information about the class's fields to the output stream
278: */
279: private static void writeFields(final ClassEditor ce,
280: final DataOutputStream dos) throws IOException {
281:
282: // Sort the fields by their names
283: final SortedSet sorted = new TreeSet(new Comparator() {
284: public int compare(Object o1, Object o2) {
285: FieldEditor fe1 = (FieldEditor) o1;
286: FieldEditor fe2 = (FieldEditor) o2;
287: return (fe1.name().compareTo(fe2.name()));
288: }
289: });
290:
291: final FieldInfo[] infos = ce.fields();
292: for (int i = 0; i < infos.length; i++) {
293: final FieldEditor fe = new FieldEditor(ce, infos[i]);
294: // Ignore private static and private transient fields
295: if (fe.isPrivate() && fe.isStatic()) {
296: break;
297:
298: } else if (fe.isPrivate() && fe.isTransient()) {
299: break;
300:
301: } else {
302: sorted.add(fe);
303: }
304: }
305:
306: final Iterator iter = sorted.iterator();
307: while (iter.hasNext()) {
308: final FieldEditor fe = (FieldEditor) iter.next();
309: dos.writeUTF(fe.name());
310: dos.writeInt(SerialVersionUID.getModifiers(fe));
311: dos.writeUTF(fe.type().descriptor());
312: }
313: }
314:
315: /**
316: * Returns the Java reflection descriptors for a method
317: */
318: static int getModifiers(final MethodEditor me) {
319: int modifiers = 0;
320:
321: if (me.isPublic()) {
322: modifiers |= Modifier.PUBLIC;
323: }
324:
325: if (me.isPrivate()) {
326: modifiers |= Modifier.PRIVATE;
327: }
328:
329: if (me.isProtected()) {
330: modifiers |= Modifier.PROTECTED;
331: }
332:
333: if (me.isPackage()) {
334: // Nothing
335: }
336:
337: if (me.isStatic()) {
338: modifiers |= Modifier.STATIC;
339: }
340:
341: if (me.isFinal()) {
342: modifiers |= Modifier.FINAL;
343: }
344:
345: if (me.isSynchronized()) {
346: modifiers |= Modifier.SYNCHRONIZED;
347: }
348:
349: if (me.isNative()) {
350: modifiers |= Modifier.NATIVE;
351: }
352:
353: if (me.isAbstract()) {
354: modifiers |= Modifier.ABSTRACT;
355: }
356:
357: if (me.isInterface()) {
358: modifiers |= Modifier.INTERFACE;
359: }
360:
361: return (modifiers);
362: }
363:
364: /**
365: * Writes information about the classes static initializer if it has one
366: */
367: private static void writeStaticInitializer(final ClassEditor ce,
368: final DataOutputStream dos) throws IOException {
369:
370: MethodEditor clinit = null;
371:
372: final MethodInfo[] methods = ce.methods();
373: for (int i = 0; i < methods.length; i++) {
374: final MethodEditor me = new MethodEditor(ce, methods[i]);
375: if (me.name().equals("<clinit>")) {
376: clinit = me;
377: break;
378: }
379: }
380:
381: if (clinit != null) {
382: dos.writeUTF("<clinit>");
383: dos.writeInt(Modifier.STATIC);
384: dos.writeUTF("()V");
385: }
386: }
387:
388: /**
389: * Writes information about the class's constructors
390: */
391: private static void writeConstructors(final ClassEditor ce,
392: final DataOutputStream dos) throws IOException {
393:
394: // Sort constructors by their signatures
395: final SortedSet sorted = new TreeSet(new Comparator() {
396: public int compare(Object o1, Object o2) {
397: MethodEditor me1 = (MethodEditor) o1;
398: MethodEditor me2 = (MethodEditor) o2;
399: return (me1.type().descriptor().compareTo(me2.type()
400: .descriptor()));
401: }
402: });
403:
404: final MethodInfo[] methods = ce.methods();
405: for (int i = 0; i < methods.length; i++) {
406: final MethodEditor me = new MethodEditor(ce, methods[i]);
407: if (me.name().equals("<init>")) {
408: if (!me.isPrivate()) {
409: // Ignore private constructors
410: sorted.add(me);
411: }
412: }
413: }
414:
415: final Iterator iter = sorted.iterator();
416: while (iter.hasNext()) {
417: final MethodEditor init = (MethodEditor) iter.next();
418: dos.writeUTF("<init>");
419: dos.writeInt(SerialVersionUID.getModifiers(init));
420: dos.writeUTF(init.type().descriptor());
421: }
422: }
423:
424: /**
425: * Write information about the class's methods
426: */
427: private static void writeMethods(final ClassEditor ce,
428: final DataOutputStream dos) throws IOException {
429:
430: // Sort constructors by their names and signatures
431: final SortedSet sorted = new TreeSet(new Comparator() {
432: public int compare(Object o1, Object o2) {
433: MethodEditor me1 = (MethodEditor) o1;
434: MethodEditor me2 = (MethodEditor) o2;
435:
436: String d1 = me1.name() + me1.type().descriptor();
437: String d2 = me2.name() + me2.type().descriptor();
438: return (d1.compareTo(d2));
439: }
440: });
441:
442: final MethodInfo[] methods = ce.methods();
443: for (int i = 0; i < methods.length; i++) {
444: final MethodEditor me = new MethodEditor(ce, methods[i]);
445: if (!me.isPrivate() && !me.isConstructor()
446: && !me.name().equals("<clinit>")) {
447: // Ignore private methods
448: sorted.add(me);
449: }
450: }
451:
452: final Iterator iter = sorted.iterator();
453: while (iter.hasNext()) {
454: final MethodEditor me = (MethodEditor) iter.next();
455: dos.writeUTF(me.name());
456: dos.writeInt(SerialVersionUID.getModifiers(me));
457: dos.writeUTF(me.type().descriptor());
458: }
459:
460: }
461:
462: }
|