de.enough.polish.postcompile.java5.Java5ClassVisitor.java Source code

Java tutorial

Introduction

Here is the source code for de.enough.polish.postcompile.java5.Java5ClassVisitor.java

Source

/*
 * Created on Sep 13, 2006 at 12:10:06 AM.
 * 
 * Copyright (c) 2006 Michael Koch / Enough Software
 *
 * This file is part of J2ME Polish.
 *
 * J2ME Polish is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * J2ME Polish is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with J2ME Polish; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * Commercial licenses are also available, please
 * refer to the accompanying LICENSE.txt or visit
 * http://www.j2mepolish.org for details.
 */
package de.enough.polish.postcompile.java5;

import org.apache.tools.ant.BuildException;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;

public class Java5ClassVisitor extends ClassAdapter implements Opcodes {
    private static final String METHOD_VALUES = "values";

    private boolean isEnumClass;
    private String className;
    private String classDesc;
    private String signature_values;
    private String name_values;

    public Java5ClassVisitor(ClassVisitor cv) {
        super(cv);
    }

    /* (non-Javadoc)
     * @see org.objectweb.asm.ClassAdapter#visit(int, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
     */
    public void visit(int version, int access, String name, String signature, String superName,
            String[] interfaces) {
        super.visit(version, access, name, signature, superName, interfaces);

        this.isEnumClass = EnumManager.getInstance().isEnumClass(name);
        this.className = name;
        this.classDesc = "L" + this.className + ";";
        this.signature_values = "()[L" + this.className + ";";
    }

    /* (non-Javadoc)
     * @see org.objectweb.asm.ClassAdapter#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
     */
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        // We keep only the values() method in enum classes.
        if (this.isEnumClass && !METHOD_VALUES.equals(name)) {
            return null;
        }

        MethodVisitor mv = super.visitMethod(access, name, EnumManager.transform(desc), signature, exceptions);
        mv = new EnumMethodVisitor(mv, access, name, desc, signature, exceptions, Type.getType(this.classDesc));
        mv = new ThrowableMethodVisitor(mv, access, name, desc);
        mv = new IteratorMethodVisitor(mv, access, name, desc);

        if (this.isEnumClass && METHOD_VALUES.equals(name) && this.signature_values.equals(desc)) {
            mv = new CloneMethodVisitor(mv, access, name, desc, Type.getType("L" + this.className + ";"));
        }

        return mv;
    }

    /* (non-Javadoc)
     * @see org.objectweb.asm.ClassAdapter#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)
     */
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        EnumManager enumManager = EnumManager.getInstance();

        if (this.isEnumClass && this.classDesc.equals(desc)) {
            String fieldName = this.classDesc + "." + name;
            desc = "I";
            value = enumManager.getEnumValue(fieldName);

            if (value == null) {
                throw new BuildException("Value for enum is null");
            }
        }

        // Workaround for bug 64.
        if (this.isEnumClass && desc.startsWith("["))
        //    if (this.isEnumClass
        //        && desc.equals(this.classArrayDesc))
        {
            this.name_values = name;
            desc = "[I";
        }

        if (desc.charAt(0) == 'L' && enumManager.isEnumClass(desc.substring(1, desc.length() - 1))) {
            desc = "I";
        }

        return super.visitField(access, name, desc, signature, value);
    }

    public void visitEnd() {
        if (this.isEnumClass) {
            if (this.name_values == null) {
                throw new BuildException("This is not an enum class: " + this.classDesc);
            }

            // Generate new <clinit> method.
            int numValues = EnumManager.getInstance().getNumEnumValues(this.classDesc);
            Method m = Method.getMethod("void <clinit> ()");
            MethodVisitor mv = super.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
            GeneratorAdapter mg = new GeneratorAdapter(ACC_STATIC, m, mv);
            mg.push(numValues);
            mg.newArray(Type.INT_TYPE);

            if (numValues <= 3) {
                for (int i = 1; i < numValues; i++) {
                    mg.dup();
                    mg.push(i);
                    mg.push(i);
                    mg.arrayStore(Type.INT_TYPE);
                }
            } else {
                Label labelInitializeField = new Label();
                Label labelCheck = new Label();
                Label labelDone = new Label();

                mg.push(1);
                mg.storeLocal(0, Type.INT_TYPE);
                mg.goTo(labelCheck);

                mg.mark(labelInitializeField);
                mg.dup();
                mg.loadLocal(0, Type.INT_TYPE);
                mg.dup();
                mg.arrayStore(Type.INT_TYPE);
                mg.iinc(0, 1);

                mg.mark(labelCheck);
                mg.loadLocal(0, Type.INT_TYPE);
                mg.push(numValues);
                mg.ifICmp(GeneratorAdapter.LT, labelInitializeField);

                mg.mark(labelDone);
            }

            mg.putStatic(Type.getType(this.classDesc), this.name_values, Type.getType(int[].class));
            mg.returnValue();
            mg.endMethod();
        }

        // Called super implementation of this method to really close this class.
        super.visitEnd();
    }
}