package org.aspectj.apache.bcel.classfile;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (https://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *    "Apache BCEL" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    "Apache BCEL", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <https://www.apache.org/>.
 */

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.aspectj.apache.bcel.Constants;

/**
 * Wrapper class that parses a given Java .class file. The method <A
 * href ="#parse">parse</A> returns a <A href ="JavaClass.html">
 * JavaClass</A> object on success. When an I/O error or an
 * inconsistency occurs an appropiate exception is propagated back to
 * the caller.
 *
 * The structure and the names comply, except for a few conveniences,
 * exactly with the <A href="ftp://java.sun.com/docs/specs/vmspec.ps">
 * JVM specification 1.0</a>. See this paper for
 * further details about the structure of a bytecode file.
 *
 * @version $Id: ClassParser.java,v 1.6 2008/05/30 17:29:14 aclement Exp $
 * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
 */
public final class ClassParser {
  private DataInputStream file;
  private String          filename;
  private int             classnameIndex;
  private int             superclassnameIndex;
  private int             major, minor;
  private int             accessflags;
  private int[]           interfaceIndices;
  private ConstantPool    cpool;
  private Field[]         fields;
  private Method[]        methods;
  private Attribute[]     attributes;

  private static final int BUFSIZE = 8192;

  /** Parse class from the given stream */
  public ClassParser(InputStream file, String filename) {
    this.filename = filename;
    if (file instanceof DataInputStream) this.file = (DataInputStream)file;
    else                                 this.file = new DataInputStream(new BufferedInputStream(file,BUFSIZE));
  }

  public ClassParser(ByteArrayInputStream baos, String filename) {
	    this.filename = filename;
	    this.file = new DataInputStream(baos);
  }

  /** Parse class from given .class file */
  public ClassParser(String file_name) throws IOException {
    this.filename = file_name;
    file = new DataInputStream(new BufferedInputStream(new FileInputStream(file_name),BUFSIZE));
  }

  /**
   * Parse the given Java class file and return an object that represents
   * the contained data, i.e., constants, methods, fields and commands.
   * A <em>ClassFormatException</em> is raised, if the file is not a valid
   * .class file. (This does not include verification of the byte code as it
   * is performed by the java interpreter).
   */
  public JavaClass parse() throws IOException, ClassFormatException {
    /****************** Read headers ********************************/
    // Check magic tag of class file
    readID();

    // Get compiler version
    readVersion();

    /****************** Read constant pool and related **************/
    // Read constant pool entries
    readConstantPool();

    // Get class information
    readClassInfo();

    // Get interface information, i.e., implemented interfaces
    readInterfaces();

    /****************** Read class fields and methods ***************/
    // Read class fields, i.e., the variables of the class
    readFields();

    // Read class methods, i.e., the functions in the class
    readMethods();

    // Read class attributes
    readAttributes();

    // Read everything of interest, so close the file
    file.close();

    // Return the information we have gathered in a new object
    JavaClass jc= new JavaClass(classnameIndex, superclassnameIndex,
			 filename, major, minor, accessflags,
			 cpool, interfaceIndices, fields,
			 methods, attributes);
    return jc;
  }

  /** Read information about the attributes of the class */
  private final void readAttributes() {
	  attributes = AttributeUtils.readAttributes(file,cpool);
  }

  /** Read information about the class and its super class */
  private final void readClassInfo() throws IOException {
    accessflags = file.readUnsignedShort();

    /* Interfaces are implicitely abstract, the flag should be set
     * according to the JVM specification */
    if((accessflags & Constants.ACC_INTERFACE) != 0)
      accessflags |= Constants.ACC_ABSTRACT;

    // don't police it like this... leave higher level verification code to check it.
//    if(((access_flags & Constants.ACC_ABSTRACT) != 0) &&
//       ((access_flags & Constants.ACC_FINAL)    != 0 ))
//      throw new ClassFormatException("Class can't be both final and abstract");

    classnameIndex      = file.readUnsignedShort();
    superclassnameIndex = file.readUnsignedShort();
  }

  private final void readConstantPool() throws IOException {
    try {
		cpool = new ConstantPool(file);
	} catch (ClassFormatException cfe) {
		// add some context if we can
		cfe.printStackTrace();
		if (filename!=null) {
			String newmessage = "File: '"+filename+"': "+cfe.getMessage();
			throw new ClassFormatException(newmessage); // this loses the old stack trace but I dont think that matters!
		}
		throw cfe;
	}
  }

  /** Read information about the fields of the class */
  private final void readFields() throws IOException, ClassFormatException {
    int fieldCount = file.readUnsignedShort();
    if (fieldCount == 0) {
    	fields = Field.NoFields;
    } else {
    	fields = new Field[fieldCount];
    	for(int i=0; i < fieldCount; i++)
    		fields[i] = new Field(file, cpool);
    }
  }

  /** Check whether the header of the file is ok. Of course, this has
   *  to be the first action on successive file reads */
  private final void readID() throws IOException {
    int magic = 0xCAFEBABE;
    if (file.readInt() != magic)
      throw new ClassFormatException(filename + " is not a Java .class file");
  }

  private static final int[] NO_INTERFACES = new int[0];

  /** Read information about the interfaces implemented by this class */
  private final void readInterfaces() throws IOException {
    int interfacesCount = file.readUnsignedShort();
    if (interfacesCount==0) {
    	interfaceIndices = NO_INTERFACES;
    } else {
	    interfaceIndices = new int[interfacesCount];
	    for(int i=0; i < interfacesCount; i++)
	      interfaceIndices[i] = file.readUnsignedShort();
    }
  }

  /** Read information about the methods of the class */
  private final void readMethods() throws IOException {
    int methodsCount = file.readUnsignedShort();
    if (methodsCount==0) {
    	methods = Method.NoMethods;
    } else {
	    methods = new Method[methodsCount];
	    for(int i=0; i < methodsCount; i++)
	      methods[i] = new Method(file, cpool);
    }
  }

  /** Read major and minor version of compiler which created the file */
  private final void readVersion() throws IOException {
    minor = file.readUnsignedShort();
    major = file.readUnsignedShort();
  }

}
