Decompiling JDK 1.5 classes

jad expects class file versions (major.minor) to be 45.3 or 46.0. Sources compiled with 1.5 have the file version 48.0. The following error message is displayed when trying to decompile a 1.5 .class file:

Parsing RequestForEnhancement.class...The class file version is 48.0, expected 45.3 or 46.0
JavaClassFileParseException: Class file version mismatch

To be able to use JAD, we can simply change the class file version of a 1.5 compiled class into 46. The following small application sets the magic number of a .class file or all classes in a jar file to whatever you desire.

ChangeMagicNumber.java:

import java.io.*;
import java.util.*;
import java.util.jar.*;
import java.util.zip.*;
 
public class ChangeMagicNumber
{
   public static void main(String []args) throws Exception {
      if (args.length != 2) {
         System.out.println("Usage: java ChangeMagicNumber [file.class | file.jar] magicNumber (eg. 46)");
         System.exit(1);
      }
       
      String filename = args[0];
      byte desiredMagicNumber = (byte) Integer.parseInt(args[1]);
      if (filename.toLowerCase().endsWith(".jar")) {
         processJar(filename, desiredMagicNumber);
      }
      else {
         processFile(filename, desiredMagicNumber);
      }
   }
    
   public static void processFile(String filename, byte desiredMagicNumber) throws Exception {
      RandomAccessFile raf = new RandomAccessFile(filename, "rw");
      raf.seek(7);
      byte b = raf.readByte();
      if (b != desiredMagicNumber) {
         raf.seek(7);
         raf.writeByte((byte) desiredMagicNumber);
      }
      raf.close();
   }
    
   public static void processJar(String filename, byte desiredMagicNumber) throws Exception {
      JarFile jf = new JarFile(filename);
      JarOutputStream jos = new JarOutputStream(new FileOutputStream(filename + "." + desiredMagicNumber));
      CRC32 crc = new CRC32();
      Enumeration enum = jf.entries();
      while (enum.hasMoreElements()) {
         ZipEntry entry = (ZipEntry) enum.nextElement();
         BufferedInputStream bis = new BufferedInputStream(jf.getInputStream(entry));
         int length = bis.available();
         byte[] buffer = null;
         if (length > 0) {
            buffer = new byte[length];
            bis.read(buffer, 0, length);
            
            buffer[7] = desiredMagicNumber;
            crc.reset();
            crc.update(buffer, 0, length);
            entry.setCrc(crc.getValue());
         }
         
         String e = entry.toString();
         jos.putNextEntry(entry);
         if (length > 0) {
            jos.write(buffer, 0, length);
         }
      }
      jf.close();
      jos.close();
   }
}

For example:

java ChangeMagicNumber rt.jar 46  

produces rt.jar.46, all classes in JAR file can be decompiled with JAD

Now the decompilation of the following 1.5 enum-example source works fine, after changing its file class version to 46.

Main.java:

enum DestinationType
{
   queue,
   topic
}
 
public class Main
{
   public static void main(String []args) {
      DestinationType dt = DestinationType.queue;

      // Outputting an enumerated type is user-friendly
      System.out.println(dt);   // prints out "queue"
      
      // Switch can be used on an enumerated type, no need to qualify the name of the constant
      switch (dt) {
         case queue:  System.out.println("Using a queue");
                      break;
         case topic:  System.out.println("Using a topic");
                      break;
         default:
      }
   }
}

Execute:

   java ChangeMagicNumber DestinationType.class 46
   jad DestinationType.class

results in:

DestinationType.jad:

// Decompiled by Jad v1.5.6d. Copyright 1997-99 Pavel Kouznetsov.
// Jad home page: http://www.geocities.com/SiliconValley/Bridge/8617/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   Main.java
 
native class DestinationType extends Enum
{
    public static final DestinationType[] values()
    {
        return (DestinationType[])_2B_VALUES.clone();
    }
 
    public static DestinationType valueOf(String s)
    {
        DestinationType adestinationtype[] = _2B_VALUES;
        int i = adestinationtype.length;
        for(int j = 0; j < i; j++)
        {
            DestinationType destinationtype = adestinationtype[j];
            if(destinationtype.name().equals(s))
                return destinationtype;
        }
 
        throw new IllegalArgumentException(s);
    }
 
    DestinationType(String s, int i)
    {
        super(s, i);
    }
 
    public volatile int compareTo(Enum enum)
    {
        return super.compareTo((DestinationType)enum);
    }
 
    public static final native DestinationType queue;
    public static final native DestinationType topic;
    private static final DestinationType _2B_VALUES[] = {
        queue, topic
    };
 
    static 
    {
        queue = new DestinationType("queue", 0);
        topic = new DestinationType("topic", 1);
    }
}