diff src/clojure/asm/commons/SerialVersionUIDAdder.java @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
line wrap: on
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/clojure/asm/commons/SerialVersionUIDAdder.java	Sat Aug 21 06:25:44 2010 -0400
     1.3 @@ -0,0 +1,508 @@
     1.4 +/***
     1.5 + * ASM: a very small and fast Java bytecode manipulation framework
     1.6 + * Copyright (c) 2000-2005 INRIA, France Telecom
     1.7 + * All rights reserved.
     1.8 + *
     1.9 + * Redistribution and use in source and binary forms, with or without
    1.10 + * modification, are permitted provided that the following conditions
    1.11 + * are met:
    1.12 + * 1. Redistributions of source code must retain the above copyright
    1.13 + *    notice, this list of conditions and the following disclaimer.
    1.14 + * 2. Redistributions in binary form must reproduce the above copyright
    1.15 + *    notice, this list of conditions and the following disclaimer in the
    1.16 + *    documentation and/or other materials provided with the distribution.
    1.17 + * 3. Neither the name of the copyright holders nor the names of its
    1.18 + *    contributors may be used to endorse or promote products derived from
    1.19 + *    this software without specific prior written permission.
    1.20 + *
    1.21 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    1.22 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    1.23 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    1.24 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    1.25 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    1.26 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    1.27 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    1.28 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    1.29 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    1.30 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
    1.31 + * THE POSSIBILITY OF SUCH DAMAGE.
    1.32 + */
    1.33 +package clojure.asm.commons;
    1.34 +
    1.35 +import java.io.ByteArrayOutputStream;
    1.36 +import java.io.DataOutputStream;
    1.37 +import java.io.IOException;
    1.38 +import java.security.MessageDigest;
    1.39 +import java.util.ArrayList;
    1.40 +import java.util.Arrays;
    1.41 +import java.util.Collection;
    1.42 +
    1.43 +import clojure.asm.ClassAdapter;
    1.44 +import clojure.asm.ClassVisitor;
    1.45 +import clojure.asm.FieldVisitor;
    1.46 +import clojure.asm.MethodVisitor;
    1.47 +import clojure.asm.Opcodes;
    1.48 +
    1.49 +/**
    1.50 + * A {@link ClassAdapter} that adds a serial version unique identifier to a
    1.51 + * class if missing. Here is typical usage of this class:
    1.52 + * <p/>
    1.53 + * <pre>
    1.54 + *   ClassWriter cw = new ClassWriter(...);
    1.55 + *   ClassVisitor sv = new SerialVersionUIDAdder(cw);
    1.56 + *   ClassVisitor ca = new MyClassAdapter(sv);
    1.57 + *   new ClassReader(orginalClass).accept(ca, false);
    1.58 + * </pre>
    1.59 + * <p/>
    1.60 + * The SVUID algorithm can be found <a href=
    1.61 + * "http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html"
    1.62 + * >http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html</a>:
    1.63 + * <p/>
    1.64 + * <pre>
    1.65 + * The serialVersionUID is computed using the signature of a stream of bytes
    1.66 + * that reflect the class definition. The National Institute of Standards and
    1.67 + * Technology (NIST) Secure Hash Algorithm (SHA-1) is used to compute a
    1.68 + * signature for the stream. The first two 32-bit quantities are used to form a
    1.69 + * 64-bit hash. A java.lang.DataOutputStream is used to convert primitive data
    1.70 + * types to a sequence of bytes. The values input to the stream are defined by
    1.71 + * the Java Virtual Machine (VM) specification for classes.
    1.72 + * <p/>
    1.73 + * The sequence of items in the stream is as follows:
    1.74 + * <p/>
    1.75 + * 1. The class name written using UTF encoding.
    1.76 + * 2. The class modifiers written as a 32-bit integer.
    1.77 + * 3. The name of each interface sorted by name written using UTF encoding.
    1.78 + * 4. For each field of the class sorted by field name (except private static
    1.79 + * and private transient fields):
    1.80 + * 1. The name of the field in UTF encoding.
    1.81 + * 2. The modifiers of the field written as a 32-bit integer.
    1.82 + * 3. The descriptor of the field in UTF encoding
    1.83 + * 5. If a class initializer exists, write out the following:
    1.84 + * 1. The name of the method, &lt;clinit&gt;, in UTF encoding.
    1.85 + * 2. The modifier of the method, java.lang.reflect.Modifier.STATIC,
    1.86 + * written as a 32-bit integer.
    1.87 + * 3. The descriptor of the method, ()V, in UTF encoding.
    1.88 + * 6. For each non-private constructor sorted by method name and signature:
    1.89 + * 1. The name of the method, &lt;init&gt;, in UTF encoding.
    1.90 + * 2. The modifiers of the method written as a 32-bit integer.
    1.91 + * 3. The descriptor of the method in UTF encoding.
    1.92 + * 7. For each non-private method sorted by method name and signature:
    1.93 + * 1. The name of the method in UTF encoding.
    1.94 + * 2. The modifiers of the method written as a 32-bit integer.
    1.95 + * 3. The descriptor of the method in UTF encoding.
    1.96 + * 8. The SHA-1 algorithm is executed on the stream of bytes produced by
    1.97 + * DataOutputStream and produces five 32-bit values sha[0..4].
    1.98 + * <p/>
    1.99 + * 9. The hash value is assembled from the first and second 32-bit values of
   1.100 + * the SHA-1 message digest. If the result of the message digest, the five
   1.101 + * 32-bit words H0 H1 H2 H3 H4, is in an array of five int values named
   1.102 + * sha, the hash value would be computed as follows:
   1.103 + * <p/>
   1.104 + * long hash = ((sha[0] &gt;&gt;&gt; 24) &amp; 0xFF) |
   1.105 + * ((sha[0] &gt;&gt;&gt; 16) &amp; 0xFF) &lt;&lt; 8 |
   1.106 + * ((sha[0] &gt;&gt;&gt; 8) &amp; 0xFF) &lt;&lt; 16 |
   1.107 + * ((sha[0] &gt;&gt;&gt; 0) &amp; 0xFF) &lt;&lt; 24 |
   1.108 + * ((sha[1] &gt;&gt;&gt; 24) &amp; 0xFF) &lt;&lt; 32 |
   1.109 + * ((sha[1] &gt;&gt;&gt; 16) &amp; 0xFF) &lt;&lt; 40 |
   1.110 + * ((sha[1] &gt;&gt;&gt; 8) &amp; 0xFF) &lt;&lt; 48 |
   1.111 + * ((sha[1] &gt;&gt;&gt; 0) &amp; 0xFF) &lt;&lt; 56;
   1.112 + * </pre>
   1.113 + *
   1.114 + * @author Rajendra Inamdar, Vishal Vishnoi
   1.115 + */
   1.116 +public class SerialVersionUIDAdder extends ClassAdapter{
   1.117 +
   1.118 +/**
   1.119 + * Flag that indicates if we need to compute SVUID.
   1.120 + */
   1.121 +protected boolean computeSVUID;
   1.122 +
   1.123 +/**
   1.124 + * Set to true if the class already has SVUID.
   1.125 + */
   1.126 +protected boolean hasSVUID;
   1.127 +
   1.128 +/**
   1.129 + * Classes access flags.
   1.130 + */
   1.131 +protected int access;
   1.132 +
   1.133 +/**
   1.134 + * Internal name of the class
   1.135 + */
   1.136 +protected String name;
   1.137 +
   1.138 +/**
   1.139 + * Interfaces implemented by the class.
   1.140 + */
   1.141 +protected String[] interfaces;
   1.142 +
   1.143 +/**
   1.144 + * Collection of fields. (except private static and private transient
   1.145 + * fields)
   1.146 + */
   1.147 +protected Collection svuidFields;
   1.148 +
   1.149 +/**
   1.150 + * Set to true if the class has static initializer.
   1.151 + */
   1.152 +protected boolean hasStaticInitializer;
   1.153 +
   1.154 +/**
   1.155 + * Collection of non-private constructors.
   1.156 + */
   1.157 +protected Collection svuidConstructors;
   1.158 +
   1.159 +/**
   1.160 + * Collection of non-private methods.
   1.161 + */
   1.162 +protected Collection svuidMethods;
   1.163 +
   1.164 +/**
   1.165 + * Creates a new {@link SerialVersionUIDAdder}.
   1.166 + *
   1.167 + * @param cv a {@link ClassVisitor} to which this visitor will delegate
   1.168 + *           calls.
   1.169 + */
   1.170 +public SerialVersionUIDAdder(final ClassVisitor cv){
   1.171 +	super(cv);
   1.172 +	svuidFields = new ArrayList();
   1.173 +	svuidConstructors = new ArrayList();
   1.174 +	svuidMethods = new ArrayList();
   1.175 +}
   1.176 +
   1.177 +// ------------------------------------------------------------------------
   1.178 +// Overriden methods
   1.179 +// ------------------------------------------------------------------------
   1.180 +
   1.181 +/*
   1.182 +	 * Visit class header and get class name, access , and intefraces
   1.183 +	 * informatoin (step 1,2, and 3) for SVUID computation.
   1.184 +	 */
   1.185 +
   1.186 +public void visit(
   1.187 +		final int version,
   1.188 +		final int access,
   1.189 +		final String name,
   1.190 +		final String signature,
   1.191 +		final String superName,
   1.192 +		final String[] interfaces){
   1.193 +	computeSVUID = (access & Opcodes.ACC_INTERFACE) == 0;
   1.194 +
   1.195 +	if(computeSVUID)
   1.196 +		{
   1.197 +		this.name = name;
   1.198 +		this.access = access;
   1.199 +		this.interfaces = interfaces;
   1.200 +		}
   1.201 +
   1.202 +	super.visit(version, access, name, signature, superName, interfaces);
   1.203 +}
   1.204 +
   1.205 +/*
   1.206 +	 * Visit the methods and get constructor and method information (step 5 and
   1.207 +	 * 7). Also determince if there is a class initializer (step 6).
   1.208 +	 */
   1.209 +public MethodVisitor visitMethod(
   1.210 +		final int access,
   1.211 +		final String name,
   1.212 +		final String desc,
   1.213 +		final String signature,
   1.214 +		final String[] exceptions){
   1.215 +	if(computeSVUID)
   1.216 +		{
   1.217 +		if(name.equals("<clinit>"))
   1.218 +			{
   1.219 +			hasStaticInitializer = true;
   1.220 +			}
   1.221 +		/*
   1.222 +					 * Remembers non private constructors and methods for SVUID
   1.223 +					 * computation For constructor and method modifiers, only the
   1.224 +					 * ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL,
   1.225 +					 * ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT flags
   1.226 +					 * are used.
   1.227 +					 */
   1.228 +		int mods = access
   1.229 +		           & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE
   1.230 +		              | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC
   1.231 +		              | Opcodes.ACC_FINAL | Opcodes.ACC_SYNCHRONIZED
   1.232 +		              | Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_STRICT);
   1.233 +
   1.234 +		// all non private methods
   1.235 +		if((access & Opcodes.ACC_PRIVATE) == 0)
   1.236 +			{
   1.237 +			if(name.equals("<init>"))
   1.238 +				{
   1.239 +				svuidConstructors.add(new Item(name, mods, desc));
   1.240 +				}
   1.241 +			else if(!name.equals("<clinit>"))
   1.242 +				{
   1.243 +				svuidMethods.add(new Item(name, mods, desc));
   1.244 +				}
   1.245 +			}
   1.246 +		}
   1.247 +
   1.248 +	return cv.visitMethod(access, name, desc, signature, exceptions);
   1.249 +}
   1.250 +
   1.251 +/*
   1.252 +	 * Gets class field information for step 4 of the alogrithm. Also determines
   1.253 +	 * if the class already has a SVUID.
   1.254 +	 */
   1.255 +public FieldVisitor visitField(
   1.256 +		final int access,
   1.257 +		final String name,
   1.258 +		final String desc,
   1.259 +		final String signature,
   1.260 +		final Object value){
   1.261 +	if(computeSVUID)
   1.262 +		{
   1.263 +		if(name.equals("serialVersionUID"))
   1.264 +			{
   1.265 +			// since the class already has SVUID, we won't be computing it.
   1.266 +			computeSVUID = false;
   1.267 +			hasSVUID = true;
   1.268 +			}
   1.269 +		/*
   1.270 +					 * Remember field for SVUID computation For field modifiers, only
   1.271 +					 * the ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC,
   1.272 +					 * ACC_FINAL, ACC_VOLATILE, and ACC_TRANSIENT flags are used when
   1.273 +					 * computing serialVersionUID values.
   1.274 +					 */
   1.275 +		int mods = access
   1.276 +		           & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE
   1.277 +		              | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC
   1.278 +		              | Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE | Opcodes.ACC_TRANSIENT);
   1.279 +
   1.280 +		if((access & Opcodes.ACC_PRIVATE) == 0
   1.281 +		   || (access & (Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT)) == 0)
   1.282 +			{
   1.283 +			svuidFields.add(new Item(name, mods, desc));
   1.284 +			}
   1.285 +		}
   1.286 +
   1.287 +	return super.visitField(access, name, desc, signature, value);
   1.288 +}
   1.289 +
   1.290 +/*
   1.291 +	 * Add the SVUID if class doesn't have one
   1.292 +	 */
   1.293 +public void visitEnd(){
   1.294 +	// compute SVUID and add it to the class
   1.295 +	if(computeSVUID && !hasSVUID)
   1.296 +		{
   1.297 +		try
   1.298 +			{
   1.299 +			cv.visitField(Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,
   1.300 +			              "serialVersionUID",
   1.301 +			              "J",
   1.302 +			              null,
   1.303 +			              new Long(computeSVUID()));
   1.304 +			}
   1.305 +		catch(Throwable e)
   1.306 +			{
   1.307 +			throw new RuntimeException("Error while computing SVUID for "
   1.308 +			                           + name, e);
   1.309 +			}
   1.310 +		}
   1.311 +
   1.312 +	super.visitEnd();
   1.313 +}
   1.314 +
   1.315 +// ------------------------------------------------------------------------
   1.316 +// Utility methods
   1.317 +// ------------------------------------------------------------------------
   1.318 +
   1.319 +/**
   1.320 + * Returns the value of SVUID if the class doesn't have one already. Please
   1.321 + * note that 0 is returned if the class already has SVUID, thus use
   1.322 + * <code>isHasSVUID</code> to determine if the class already had an SVUID.
   1.323 + *
   1.324 + * @return Returns the serial version UID
   1.325 + * @throws IOException
   1.326 + */
   1.327 +protected long computeSVUID() throws IOException{
   1.328 +	ByteArrayOutputStream bos = null;
   1.329 +	DataOutputStream dos = null;
   1.330 +	long svuid = 0;
   1.331 +
   1.332 +	try
   1.333 +		{
   1.334 +		bos = new ByteArrayOutputStream();
   1.335 +		dos = new DataOutputStream(bos);
   1.336 +
   1.337 +		/*
   1.338 +					 * 1. The class name written using UTF encoding.
   1.339 +					 */
   1.340 +		dos.writeUTF(name.replace('/', '.'));
   1.341 +
   1.342 +		/*
   1.343 +					 * 2. The class modifiers written as a 32-bit integer.
   1.344 +					 */
   1.345 +		dos.writeInt(access
   1.346 +		             & (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL
   1.347 +		                | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT));
   1.348 +
   1.349 +		/*
   1.350 +					 * 3. The name of each interface sorted by name written using UTF
   1.351 +					 * encoding.
   1.352 +					 */
   1.353 +		Arrays.sort(interfaces);
   1.354 +		for(int i = 0; i < interfaces.length; i++)
   1.355 +			{
   1.356 +			dos.writeUTF(interfaces[i].replace('/', '.'));
   1.357 +			}
   1.358 +
   1.359 +		/*
   1.360 +					 * 4. For each field of the class sorted by field name (except
   1.361 +					 * private static and private transient fields):
   1.362 +					 *
   1.363 +					 * 1. The name of the field in UTF encoding. 2. The modifiers of the
   1.364 +					 * field written as a 32-bit integer. 3. The descriptor of the field
   1.365 +					 * in UTF encoding
   1.366 +					 *
   1.367 +					 * Note that field signatutes are not dot separated. Method and
   1.368 +					 * constructor signatures are dot separated. Go figure...
   1.369 +					 */
   1.370 +		writeItems(svuidFields, dos, false);
   1.371 +
   1.372 +		/*
   1.373 +					 * 5. If a class initializer exists, write out the following: 1. The
   1.374 +					 * name of the method, <clinit>, in UTF encoding. 2. The modifier of
   1.375 +					 * the method, java.lang.reflect.Modifier.STATIC, written as a
   1.376 +					 * 32-bit integer. 3. The descriptor of the method, ()V, in UTF
   1.377 +					 * encoding.
   1.378 +					 */
   1.379 +		if(hasStaticInitializer)
   1.380 +			{
   1.381 +			dos.writeUTF("<clinit>");
   1.382 +			dos.writeInt(Opcodes.ACC_STATIC);
   1.383 +			dos.writeUTF("()V");
   1.384 +			} // if..
   1.385 +
   1.386 +		/*
   1.387 +					 * 6. For each non-private constructor sorted by method name and
   1.388 +					 * signature: 1. The name of the method, <init>, in UTF encoding. 2.
   1.389 +					 * The modifiers of the method written as a 32-bit integer. 3. The
   1.390 +					 * descriptor of the method in UTF encoding.
   1.391 +					 */
   1.392 +		writeItems(svuidConstructors, dos, true);
   1.393 +
   1.394 +		/*
   1.395 +					 * 7. For each non-private method sorted by method name and
   1.396 +					 * signature: 1. The name of the method in UTF encoding. 2. The
   1.397 +					 * modifiers of the method written as a 32-bit integer. 3. The
   1.398 +					 * descriptor of the method in UTF encoding.
   1.399 +					 */
   1.400 +		writeItems(svuidMethods, dos, true);
   1.401 +
   1.402 +		dos.flush();
   1.403 +
   1.404 +		/*
   1.405 +					 * 8. The SHA-1 algorithm is executed on the stream of bytes
   1.406 +					 * produced by DataOutputStream and produces five 32-bit values
   1.407 +					 * sha[0..4].
   1.408 +					 */
   1.409 +		byte[] hashBytes = computeSHAdigest(bos.toByteArray());
   1.410 +
   1.411 +		/*
   1.412 +					 * 9. The hash value is assembled from the first and second 32-bit
   1.413 +					 * values of the SHA-1 message digest. If the result of the message
   1.414 +					 * digest, the five 32-bit words H0 H1 H2 H3 H4, is in an array of
   1.415 +					 * five int values named sha, the hash value would be computed as
   1.416 +					 * follows:
   1.417 +					 *
   1.418 +					 * long hash = ((sha[0] >>> 24) & 0xFF) | ((sha[0] >>> 16) & 0xFF) <<
   1.419 +					 * 8 | ((sha[0] >>> 8) & 0xFF) << 16 | ((sha[0] >>> 0) & 0xFF) <<
   1.420 +					 * 24 | ((sha[1] >>> 24) & 0xFF) << 32 | ((sha[1] >>> 16) & 0xFF) <<
   1.421 +					 * 40 | ((sha[1] >>> 8) & 0xFF) << 48 | ((sha[1] >>> 0) & 0xFF) <<
   1.422 +					 * 56;
   1.423 +					 */
   1.424 +		for(int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--)
   1.425 +			{
   1.426 +			svuid = (svuid << 8) | (hashBytes[i] & 0xFF);
   1.427 +			}
   1.428 +		}
   1.429 +	finally
   1.430 +		{
   1.431 +		// close the stream (if open)
   1.432 +		if(dos != null)
   1.433 +			{
   1.434 +			dos.close();
   1.435 +			}
   1.436 +		}
   1.437 +
   1.438 +	return svuid;
   1.439 +}
   1.440 +
   1.441 +/**
   1.442 + * Returns the SHA-1 message digest of the given value.
   1.443 + *
   1.444 + * @param value the value whose SHA message digest must be computed.
   1.445 + * @return the SHA-1 message digest of the given value.
   1.446 + */
   1.447 +protected byte[] computeSHAdigest(final byte[] value){
   1.448 +	try
   1.449 +		{
   1.450 +		return MessageDigest.getInstance("SHA").digest(value);
   1.451 +		}
   1.452 +	catch(Exception e)
   1.453 +		{
   1.454 +		throw new UnsupportedOperationException(e);
   1.455 +		}
   1.456 +}
   1.457 +
   1.458 +/**
   1.459 + * Sorts the items in the collection and writes it to the data output stream
   1.460 + *
   1.461 + * @param itemCollection collection of items
   1.462 + * @param dos            a <code>DataOutputStream</code> value
   1.463 + * @param dotted         a <code>boolean</code> value
   1.464 + * @throws IOException if an error occurs
   1.465 + */
   1.466 +private void writeItems(
   1.467 +		final Collection itemCollection,
   1.468 +		final DataOutputStream dos,
   1.469 +		final boolean dotted) throws IOException{
   1.470 +	int size = itemCollection.size();
   1.471 +	Item items[] = (Item[]) itemCollection.toArray(new Item[size]);
   1.472 +	Arrays.sort(items);
   1.473 +	for(int i = 0; i < size; i++)
   1.474 +		{
   1.475 +		dos.writeUTF(items[i].name);
   1.476 +		dos.writeInt(items[i].access);
   1.477 +		dos.writeUTF(dotted
   1.478 +		             ? items[i].desc.replace('/', '.')
   1.479 +		             : items[i].desc);
   1.480 +		}
   1.481 +}
   1.482 +
   1.483 +// ------------------------------------------------------------------------
   1.484 +// Inner classes
   1.485 +// ------------------------------------------------------------------------
   1.486 +
   1.487 +static class Item implements Comparable{
   1.488 +
   1.489 +	String name;
   1.490 +
   1.491 +	int access;
   1.492 +
   1.493 +	String desc;
   1.494 +
   1.495 +	Item(final String name, final int access, final String desc){
   1.496 +		this.name = name;
   1.497 +		this.access = access;
   1.498 +		this.desc = desc;
   1.499 +	}
   1.500 +
   1.501 +	public int compareTo(final Object o){
   1.502 +		Item other = (Item) o;
   1.503 +		int retVal = name.compareTo(other.name);
   1.504 +		if(retVal == 0)
   1.505 +			{
   1.506 +			retVal = desc.compareTo(other.desc);
   1.507 +			}
   1.508 +		return retVal;
   1.509 +	}
   1.510 +}
   1.511 +}