/*
 * Copyright (c) 2008 Jython Developers
 * Licensed to PSF under a Contributor Agreement.
 */
package org.python.core;

import java.io.IOException;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.commons.EmptyVisitor;

/**
 * This class reads a classfile from a byte array and pulls out the value of the class annotation
 * for APIVersion, which can then be retrieved by a call to getVersion().
 *
 * Hopefully the use of ClassReader in this implementation is not too expensive. I suspect it is not
 * since EmptyVisitor is just a bag of empty methods so shouldn't cost too much. If it turns out to
 * cost too much, we will want to implement a special purpose ClassReader that only reads out the
 * APIVersion annotation I think.
 */
public class AnnotationReader extends EmptyVisitor {

    private boolean nextVisitIsVersion = false;
    private boolean nextVisitIsMTime = false;

    private int version = -1;
    private long mtime = -1;

    /**
     * Reads the classfile bytecode in data and to extract the version.
     * @throws IOException - if the classfile is malformed.
     */
    public AnnotationReader(byte[] data) throws IOException {
        ClassReader r;
        try {
            r = new ClassReader(data);
        } catch (ArrayIndexOutOfBoundsException e) {
            IOException ioe = new IOException("Malformed bytecode: not enough data");
            ioe.initCause(e);// IOException didn't grow a constructor that could take a cause till
                             // 1.6, so do it the old fashioned way
            throw ioe;
        }
        r.accept(this, 0);
    }

    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        nextVisitIsVersion = desc.equals("Lorg/python/compiler/APIVersion;");
        nextVisitIsMTime = desc.equals("Lorg/python/compiler/MTime;");
        return this;
    }

    public void visit(String name, Object value) {
        if (nextVisitIsVersion) {
            version = (Integer)value;
            nextVisitIsVersion = false;
        } else if (nextVisitIsMTime) {
            mtime = (Long)value;
            nextVisitIsVersion = false;
        }
    }

    public int getVersion() {
        return version;
    }

    public long getMTime() {
        return mtime;
    }
}
