content: /**
   * JReversePro - Java Decompiler / Disassembler.
   * Copyright (C) 2008 Karthik Kumar.
   * 
   * Licensed under the Apache License, Version 2.0 (the "License"); 
   * you may not use this file except in compliance with the License. 
   * You may obtain a copy of the License at 
   *  
   *  	http://www.apache.org/licenses/LICENSE-2.0 
   *  
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS, 
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
   * See the License for the specific language governing permissions and
   * limitations under the License.
   ***/
  package org.jreversepro.parser.common;
  
  import java.io.DataInputStream;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.List;
  
  import org.jreversepro.jls.JLSConstants;
  import org.jreversepro.jvm.JVMConstants;
  import org.jreversepro.parser.ClassParserException;
  import org.jreversepro.reflect.ClassInfo;
  import org.jreversepro.reflect.ConstantPool;
  import org.jreversepro.reflect.Field;
  import org.jreversepro.reflect.Method;
  
  
  /**
   * @author Karthik Kumar
   */
  public class AbstractClassFileParser {
  
    /**
     * Reads the ConstantPool.
     * 
     * @throws ClassParserException
     *           Thrown if class file not in desired format.
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    public static ConstantPool readConstantPool(DataInputStream dis)
        throws IOException, ClassParserException {
      int numCpEntry = dis.readShort();
      ConstantPool cpInfo = new ConstantPool(numCpEntry);
      readCpEntries(dis, cpInfo, numCpEntry);
      return cpInfo;
    }
  
    /**
     * Reads the access specifier of the class.
     * 
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    public static short readAccess(DataInputStream dis) throws IOException {
      return dis.readShort();
    }
  
    /**
     * 
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    public static short readThisClassIndex(DataInputStream dis)
        throws IOException {
      return dis.readShort();
    }
  
    /**
     * 
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    public static short readSuperClassIndex(DataInputStream dis)
        throws IOException {
      return dis.readShort();
    }
  
    /**
     * Reads the fully qualified name of the interfaces <code>implemented</code>
     * by the Current class.
     * 
     *
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    public static List<String> readInterfaces(DataInputStream dis,
        ConstantPool cpInfo) throws IOException {
      short count = dis.readShort();
      List<String> interfaces = new ArrayList<String>();
      for (int i = 0; i < count; i++) {
        String interfaceName = cpInfo.getClassName(dis.readShort());
        interfaces.add(interfaceName);
      }
      return interfaces;
    }
  
    /**
     * Reads the fields <code>defined</code> in the Current class.
     * 
     * 
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    public static List<Field> readFields(DataInputStream dis, ConstantPool cpInfo)
        throws IOException {
      short count = dis.readShort();
  
      List<Field> fields = new ArrayList<Field>();
      for (int i = 0; i < count; i++) {
        Field curField = new Field();
  
        short accessFlags = dis.readShort();
        short nameIndex = dis.readShort();
        short descIndex = dis.readShort();
  
        String name = cpInfo.getUtf8String(nameIndex);
        String descriptor = cpInfo.getUtf8String(descIndex);
  
        curField.setName(name);
        curField.setDatatype(descriptor);
        curField.setQualifier(accessFlags);
  
        short attrCount = dis.readShort();
        for (int j = 0; j < attrCount; j++) {
          readFieldAttributes(curField, dis, cpInfo);
        }
  
        fields.add(curField);
      }
      return fields;
    }
  
    /**
     * Reads the methods <code>defined</code> in the Current class.
     * 
     * 
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    public static List<Method> readMethods(DataInputStream dis,
        ConstantPool cpInfo) throws IOException {
      short count = dis.readShort();
  
      List<Method> methods = new ArrayList<Method>();
  
      for (int i = 0; i < count; i++) {
        Method curMethod = new Method();
  
        short accessFlags = dis.readShort();
        short nameIndex = dis.readShort();
        short descIndex = dis.readShort();
  
        String name = cpInfo.getUtf8String(nameIndex);
        String descriptor = cpInfo.getUtf8String(descIndex);
  
        curMethod.setName(name);
        curMethod.setSignature(descriptor);
        curMethod.setQualifier(accessFlags);
  
        short attrCount = dis.readShort();
        for (int j = 0; j < attrCount; j++) {
          readMethodAttributes(curMethod, dis, cpInfo);
        }
        if (curMethod.getVariableTable() == null) {
          // It was not decompiled with variableTable- Hence setting it
          // using symboltable
          curMethod.initializeSymbolTable();
        }
  
        methods.add(curMethod);
      }
      return methods;
    }
  
    /**
     * Reads the ATTRIBUTES of the fields and methods.
     * <p>
     * The possible attributes here are Code, LineNumberTable, Exception
     * </p>
     * .
     * 
     * @see AttributeParser
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    public static void readClassAttributes(DataInputStream dis,
        ConstantPool cpInfo, ClassInfo clazz) throws IOException {
      short attrCount = dis.readShort();
  
      for (int i = 0; i < attrCount; i++) {
        readClassAttribute(dis, cpInfo, clazz);
      }
    }
  
    /**
     * Reads the ConstantPool Entries.
     * 
     * @param aNumEntry
     *          Number of constant pool entries.
     * @throws ClassParserException
     *           Thrown if class file not in desired format.
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    private static void readCpEntries(DataInputStream dis, ConstantPool cpInfo,
        int aNumEntry) throws IOException, ClassParserException {
      cpInfo.addNullEntry();
  
      for (int i = 1; i < aNumEntry; i++) {
        byte tagByte = dis.readByte();
  
        switch (tagByte) {
        case ConstantPool.TAG_UTF8:
          readTagUtf8(dis, cpInfo, i);
          break;
        case ConstantPool.TAG_INTEGER:
          readTagInteger(dis, cpInfo, i);
          break;
        case ConstantPool.TAG_FLOAT:
          readTagFloat(dis, cpInfo, i);
          break;
        case ConstantPool.TAG_LONG:
          readTagLong(dis, cpInfo, i);
          // Long takes two ConstantPool Entries.
          i++;
          break;
        case ConstantPool.TAG_DOUBLE:
          readTagDouble(dis, cpInfo, i);
          // Double takes two ConstantPool Entries.
          i++;
          break;
        case ConstantPool.TAG_CLASS:
          readTagClass(dis, cpInfo, i);
          break;
        case ConstantPool.TAG_STRING:
          readTagString(dis, cpInfo, i);
          break;
        case ConstantPool.TAG_FIELDREF:
          readTagFieldRef(dis, cpInfo, i);
          break;
        case ConstantPool.TAG_METHODREF:
          readTagMethodRef(dis, cpInfo, i);
          break;
        case ConstantPool.TAG_INTERFACEREF:
          readTagInterfaceRef(dis, cpInfo, i);
          break;
        case ConstantPool.TAG_NAMETYPE:
          readTagNameType(dis, cpInfo, i);
          break;
        default:
          throw new ClassParserException("TagByte " + tagByte
              + " Invalid for ConstantPool Entry #" + i);
        }
      }
    }
  
    /**
     * Reads an UTF8 entry.
     * 
     * @param aIndex
     *          Index of a ConstantPool Entry.
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    private static void readTagUtf8(DataInputStream dis, ConstantPool cpInfo,
        int aIndex) throws IOException {
      String utfString = dis.readUTF();
      cpInfo.addUtf8Entry(utfString);
    }
  
    /**
     * Reads an integer entry.
     * 
     * @param aIndex
     *          Index of a ConstantPool Entry.
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    private static void readTagInteger(DataInputStream dis, ConstantPool cpInfo,
        int aIndex) throws IOException {
      int intValue = dis.readInt();
  
      cpInfo.addIntegerEntry(String.valueOf(intValue));
    }
  
    /**
     * Reads an float entry.
     * 
     * @param aIndex
     *          Index of a ConstantPool Entry.
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    private static void readTagFloat(DataInputStream dis, ConstantPool cpInfo,
        int aIndex) throws IOException {
      float floatValue = dis.readFloat();
  
      cpInfo.addFloatEntry(String.valueOf(floatValue)
          + JLSConstants.JLS_FLOATING_NUMBER_SUFFIX);
    }
  
    /**
     * Reads a long entry.
     * 
     * @param aIndex
     *          Index of a ConstantPool Entry.
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    private static void readTagLong(DataInputStream dis, ConstantPool cpInfo,
        int aIndex) throws IOException {
  
      long longValue = dis.readLong();
  
      cpInfo.addLongEntry(String.valueOf(longValue)
          + JLSConstants.JLS_LONG_NUMBER_SUFFIX);
      cpInfo.addNullEntry();
    }
  
    /**
     * Reads a double entry.
     * 
     * @param aIndex
     *          Index of a ConstantPool Entry.
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    private static void readTagDouble(DataInputStream dis, ConstantPool cpInfo,
        int aIndex) throws IOException {
  
      double doubleValue = dis.readDouble();
  
      cpInfo.addDoubleEntry(String.valueOf(doubleValue));
      cpInfo.addNullEntry();
    }
  
    /**
     * Reads an TAG_CLASS entry.
     * <p>
     * u1 tag; <br>
     * u2 name_index;<br>
     * </p>
     * Reads a class entry.
     * 
     * @param aIndex
     *          Index of a ConstantPool Entry.
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    private static void readTagClass(DataInputStream dis, ConstantPool cpInfo,
        int aIndex) throws IOException {
      int classIndex = dis.readShort();
  
      cpInfo.addClassEntry(classIndex);
    }
  
    /**
     * Reads a string entry.
     * 
     * @param aIndex
     *          Index of a ConstantPool Entry.
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    private static void readTagString(DataInputStream dis, ConstantPool cpInfo,
        int aIndex) throws IOException {
      int stringIndex = dis.readShort();
      cpInfo.addStringEntry(stringIndex);
    }
  
    /**
     * Reads a TAG_FIELDREF entry.
     * <p>
     * u1 tag; <br>
     * u2 class_index; <br>
     * u2 name_and_type_index; <br>
     * </p>
     * 
     * @param aIndex
     *          Index of a ConstantPool Entry.
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    private static void readTagFieldRef(DataInputStream dis, ConstantPool cpInfo,
        int aIndex) throws IOException {
      int classIndex = dis.readShort();
      int nameType = dis.readShort();
  
      cpInfo.addFieldRefEntry(classIndex, nameType);
    }
  
    /**
     * Reads a TAG_METHODREF entry.
     * <p>
     * u1 tag; <br>
     * u2 class_index; <br>
     * u2 name_and_type_index; <br>
     * </p>
     * 
     * @param aIndex
     *          Index of a ConstantPool Entry.
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    private static void readTagMethodRef(DataInputStream dis,
        ConstantPool cpInfo, int aIndex) throws IOException {
      int classIndex = dis.readShort();
      int nameType = dis.readShort();
  
      cpInfo.addMethodRefEntry(classIndex, nameType);
    }
  
    /**
     * Reads a TAG_INTERFACEREF entry.
     * <p>
     * u1 tag;<br>
     * u2 class_index;<br>
     * u2 name_and_type_index;<br>
     * </p>
     * 
     * @param aIndex
     *          Index of a ConstantPool Entry.
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    private static void readTagInterfaceRef(DataInputStream dis,
        ConstantPool cpInfo, int aIndex) throws IOException {
      int classIndex = dis.readShort();
      int nameType = dis.readShort();
  
      cpInfo.addInterfaceRefEntry(classIndex, nameType);
    }
  
    /**
     * Reads a TAG_NAMETYPE entry.
     * <p>
     * u1 tag; <br>
     * u2 name_index;<br>
     * u2 descriptor_index;<br>
     * </p>
     * 
     * @param aIndex
     *          Index of a ConstantPool Entry.
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    private static void readTagNameType(DataInputStream dis, ConstantPool cpInfo,
        int aIndex) throws IOException {
      int nameIndex = dis.readShort();
      int descIndex = dis.readShort();
  
      cpInfo.addNameTypeEntry(nameIndex, descIndex);
    }
  
    /**
     * Reads the ATTRIBUTES of the field defined in the Current class.
     * <p>
     * The possible attributes here are ConstantValue , Deprecated , Synthetic.
     * </p>
     * .
     * 
     * @param aRhsField
     *          Reads attributes into this field.
     * @see AttributeParser
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    private static void readFieldAttributes(Field aRhsField, DataInputStream dis,
        ConstantPool cpInfo) throws IOException {
  
      String attrName = cpInfo.getUtf8String(dis.readShort());
  
      if (attrName.equals(JVMConstants.ATTRIBUTE_CONSTANT_VALUE)) {
        aRhsField.setValue(AttributeParser.readConstantValue(dis, cpInfo));
      } else if (attrName.equals(JVMConstants.ATTRIBUTE_SYNTHETIC)) {
        AttributeParser.readSynthetic(dis);
      } else if (attrName.equals(JVMConstants.ATTRIBUTE_DEPRECATED)) {
        AttributeParser.readDeprecated(dis);
      }
    }
  
    /**
     * Reads the ATTRIBUTES of the method defined in the Current class.
     * <p>
     * The possible attributes here are Deprecated , Synthetic , <b> Code </b> and
     * Exceptions
     * </p>
     * .
     * 
     * @param aRhsMethod
     *          Reads attributes into this method.
     * @see AttributeParser
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    private static void readMethodAttributes(Method method, DataInputStream dis,
        ConstantPool cpInfo) throws IOException {
      String attrName = cpInfo.getUtf8String(dis.readShort());
  
      if (attrName.compareTo(JVMConstants.ATTRIBUTE_CODE) == 0) {
        AttributeParser.readCode(method, dis, cpInfo);
      } else if (attrName.compareTo(JVMConstants.ATTRIBUTE_EXCEPTIONS) == 0) {
        // counterpart to 'throws' clause in the source code.
        method.setThrowsClasses(AttributeParser.readExceptions(dis, cpInfo));
      } else if (attrName.compareTo(JVMConstants.ATTRIBUTE_SYNTHETIC) == 0) {
        AttributeParser.readSynthetic(dis);
      } else if (attrName.compareTo(JVMConstants.ATTRIBUTE_DEPRECATED) == 0) {
        AttributeParser.readDeprecated(dis);
      } else if (attrName.compareTo(JVMConstants.ATTRIBUTE_INNERCLASSES) == 0) {
        // TODO Should discuss with akkumar if he has implemented it
        // already in some other code location.
        AttributeParser.readInnerClasses(dis);
      }
    }
  
    /**
     * Reads the ATTRIBUTES of the whole class itself.
     * <p>
     * The possible attributes here are Deprecated , SourceFile
     * </p>
     * .
     * 
     * @see AttributeParser
     * @throws IOException
     *           Thrown if error in stream of bytes containing the class file.
     */
    private static void readClassAttribute(DataInputStream dis,
        ConstantPool cpInfo, ClassInfo clazz) throws IOException {
      int attrIndex = dis.readShort();
      String attrName = cpInfo.getUtf8String(attrIndex);
  
      if (attrName.compareTo(JVMConstants.ATTRIBUTE_SOURCEFILE) == 0) {
        clazz.setSourceFile(AttributeParser.readSourceFile(dis, cpInfo));
      } else if (attrName.compareTo(JVMConstants.ATTRIBUTE_DEPRECATED) == 0) {
        AttributeParser.readDeprecated(dis);
      } else {
        // System.out.println("Attribute " + AttrName );
      }
    }
  
  }
  