How to correct a mess java .class file set or generate a proper .jar archive from a mess .class set?

前端 未结 1 936
终归单人心
终归单人心 2021-01-17 01:43

Background

I have to contact different kind of Java project with various of build system. Sometimes the directory structure is different from the package hierarchy.

1条回答
  •  走了就别回头了
    2021-01-17 02:21

    Generally, it is better to fix the build system issues, to generate the correct directory structure in the first place, rather than trying to fix it after the fact. One problem I see, is that classes from different packages may have the same simple name, so if their class files are written to the same flat directory, one of them will overwrite the other and this data loss can not be fixed afterwards.

    Generally, the constant pool at the beginning of the class file contains the qualified class name, so it is possible to extract it, but you need to understand the class file structure to pick the right string. The following method will parse a class file and extract the name (in its internal form):

    static String getClassName(ByteBuffer buf) {
        if(buf.order(ByteOrder.BIG_ENDIAN).getInt()!=0xCAFEBABE) {
            throw new IllegalArgumentException("not a valid class file");
        }
        int minor=buf.getChar(), ver=buf.getChar(), poolSize=buf.getChar();
        int[] pool = new int[poolSize];
        //System.out.println("version "+ver+'.'+minor);
        for(int ix=1; ix>1));
        while(buf.hasRemaining()) {
            byte b=buf.get();
            if(b>0) sb.append((char)b);
            else {
                int b2 = buf.get();
                if((b&0xf0)!=0xe0)
                    sb.append((char)((b&0x1F)<<6 | b2&0x3F));
                else {
                    int b3 = buf.get();
                    sb.append((char)((b&0x0F)<<12 | (b2&0x3F)<<6 | b3&0x3F));
                }
            }
        }
        buf.limit(oldLimit);
        return sb.toString();
    }
    private static final byte CONSTANT_Utf8 = 1, CONSTANT_Integer = 3,
        CONSTANT_Float = 4, CONSTANT_Long = 5, CONSTANT_Double = 6,
        CONSTANT_Class = 7, CONSTANT_String = 8, CONSTANT_FieldRef = 9,
        CONSTANT_MethodRef = 10, CONSTANT_InterfaceMethodRef = 11,
        CONSTANT_NameAndType = 12, CONSTANT_MethodHandle = 15,
        CONSTANT_MethodType = 16, CONSTANT_Dynamic = 17, CONSTANT_InvokeDynamic = 18,
        CONSTANT_Module = 19, CONSTANT_Package = 20;
    

    This can be used to fix a wrong file location like this:

    static void checkAndMoveClassFile(Path path) throws IOException {
        ByteBuffer bb;
        try(FileChannel ch=FileChannel.open(path, StandardOpenOption.READ)) {
            bb=ByteBuffer.allocate((int)ch.size());
            while(bb.hasRemaining()) ch.read(bb);
            bb.flip();
        }
        String name = getClassName(bb);
        Path newPath = path.resolveSibling(name+".class");
        if(!path.equals(newPath)) {
            System.out.println("moving "+path+" to "+newPath);
            Files.createDirectories(newPath.getParent());
            Files.move(path, newPath);
        }
    }
    

    which you can run over a directory easily

    Files.list(dirPath)
         .filter(p -> p.getFileName().toString().endsWith(".class"))
         .forEach(p -> {
             try { checkAndMoveClassFile(p); }
             catch (IOException ex) { throw new UncheckedIOException(ex); }
         });
    

    0 讨论(0)
提交回复
热议问题