org.aspectj.weaver.bcel.UnwovenClassFile.java Source code

Java tutorial

Introduction

Here is the source code for org.aspectj.weaver.bcel.UnwovenClassFile.java

Source

/* *******************************************************************
 * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
 * All rights reserved. 
 * This program and the accompanying materials are made available 
 * under the terms of the Eclipse Public License v1.0 
 * which accompanies this distribution and is available at 
 * http://www.eclipse.org/legal/epl-v10.html 
 *  
 * Contributors: 
 *     PARC     initial implementation 
 * ******************************************************************/

package org.aspectj.weaver.bcel;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;

import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.util.FileUtil;
import org.aspectj.weaver.IUnwovenClassFile;

public class UnwovenClassFile implements IUnwovenClassFile {
    protected String filename;
    protected char[] charfilename;
    protected byte[] bytes;
    // protected JavaClass javaClass = null;
    // protected byte[] writtenBytes = null;
    protected List<ChildClass> writtenChildClasses = Collections.emptyList();
    protected String className = null;
    protected boolean isModule = false;

    public UnwovenClassFile(String filename, byte[] bytes) {
        this.filename = filename;
        this.isModule = filename.toLowerCase().endsWith("module-info.java");
        this.bytes = bytes;
    }

    /** Use if the classname is known, saves a bytecode parse */
    public UnwovenClassFile(String filename, String classname, byte[] bytes) {
        this.filename = filename;
        this.isModule = filename.toLowerCase().endsWith("module-info.class");
        this.className = classname;
        this.bytes = bytes;
    }

    public boolean shouldBeWoven() {
        // Skip module-info files for now, they aren't really types
        return !isModule;
    }

    public String getFilename() {
        return filename;
    }

    public String makeInnerFileName(String innerName) {
        String prefix = filename.substring(0, filename.length() - 6); // strip the .class
        return prefix + "$" + innerName + ".class";
    }

    public byte[] getBytes() {
        // if (bytes == null) bytes = javaClass.getBytes();
        return bytes;
    }

    public JavaClass getJavaClass() {
        // XXX need to know when to make a new class and when not to
        // XXX this is an important optimization
        if (getBytes() == null) {
            System.out.println("no bytes for: " + getFilename());
            // Thread.currentThread().dumpStack();
            Thread.dumpStack();
        }
        return Utility.makeJavaClass(filename, getBytes());
        // if (javaClass == null) javaClass = Utility.makeJavaClass(filename, getBytes());
        // return javaClass;
    }

    public void writeUnchangedBytes() throws IOException {
        writeWovenBytes(getBytes(), Collections.<ChildClass>emptyList());
    }

    public void writeWovenBytes(byte[] bytes, List<ChildClass> childClasses) throws IOException {
        writeChildClasses(childClasses);

        // System.err.println("should write: " + getClassName());

        // System.err.println("about to write: " + this + ", " + writtenBytes + ", ");
        // + writtenBytes != null + " && " + unchanged(bytes, writtenBytes) );

        // if (writtenBytes != null && unchanged(bytes, writtenBytes)) return;

        // System.err.println("    actually wrote it");

        BufferedOutputStream os = FileUtil.makeOutputStream(new File(filename));
        os.write(bytes);
        os.close();

        // writtenBytes = bytes;
    }

    private void writeChildClasses(List<ChildClass> childClasses) throws IOException {
        // ??? we only really need to delete writtenChildClasses whose
        // ??? names aren't in childClasses; however, it's unclear
        // ??? how much that will affect performance
        deleteAllChildClasses();

        childClasses.removeAll(writtenChildClasses); // XXX is this right

        for (ChildClass childClass : childClasses) {
            writeChildClassFile(childClass.name, childClass.bytes);
        }

        writtenChildClasses = childClasses;

    }

    private void writeChildClassFile(String innerName, byte[] bytes) throws IOException {
        BufferedOutputStream os = FileUtil.makeOutputStream(new File(makeInnerFileName(innerName)));
        os.write(bytes);
        os.close();
    }

    protected void deleteAllChildClasses() {
        for (ChildClass childClass : writtenChildClasses) {
            deleteChildClassFile(childClass.name);
        }
    }

    protected void deleteChildClassFile(String innerName) {
        File childClassFile = new File(makeInnerFileName(innerName));
        childClassFile.delete();
    }

    /* private */static boolean unchanged(byte[] b1, byte[] b2) {
        int len = b1.length;
        if (b2.length != len)
            return false;
        for (int i = 0; i < len; i++) {
            if (b1[i] != b2[i])
                return false;
        }
        return true;
    }

    public char[] getClassNameAsChars() {
        if (charfilename == null) {
            charfilename = getClassName().replace('.', '/').toCharArray();
        }
        return charfilename;
    }

    public String getClassName() {
        if (className == null)
            className = getJavaClass().getClassName(); // OPTIMIZE quicker way to determine name??? surely?
        return className;
    }

    @Override
    public String toString() {
        return "UnwovenClassFile(" + filename + ", " + getClassName() + ")";
    }

    // record
    // OPTIMIZE why is the 'short name' used here (the bit after the dollar) - seems we mess about a lot trimming it off only to put
    // it back on!
    public static class ChildClass {
        public final String name;
        public final byte[] bytes;

        ChildClass(String name, byte[] bytes) {
            this.name = name;
            this.bytes = bytes;
        }

        @Override
        public boolean equals(Object other) {
            if (!(other instanceof ChildClass))
                return false;
            ChildClass o = (ChildClass) other;
            return o.name.equals(name) && unchanged(o.bytes, bytes);
        }

        @Override
        public int hashCode() {
            return name.hashCode();
        }

        @Override
        public String toString() {
            return "(ChildClass " + name + ")";
        }
    }

    public void setClassNameAsChars(char[] classNameAsChars) {
        this.charfilename = classNameAsChars;
    }
}