/*
 * Copyright (c) 2002,2006 IBM Corporation.
 * 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:
 *     IBM Corporation - initial API and implementation
 */
package com.ibm.wala.shrikeBT.shrikeCT;

import com.ibm.wala.shrikeBT.ConstantPoolReader;
import com.ibm.wala.shrikeBT.Decoder;
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
import com.ibm.wala.shrikeCT.ClassReader;
import com.ibm.wala.shrikeCT.CodeReader;
import com.ibm.wala.shrikeCT.ConstantPoolParser;
import com.ibm.wala.shrikeCT.InvalidClassFileException;

/**
 * This class decodes Java bytecodes into ShrikeBT code using a ShrikeCT class reader.
 */
final public class CTDecoder extends Decoder {

  /**
   * Decode the code resource 'r'.
   * 
   * @throws NullPointerException if r is null
   */
  public CTDecoder(CodeReader r) throws NullPointerException {
    this(r, makeConstantPoolReader(r.getClassReader()));
  }

  /**
   * Decode the code resource 'r' using the predeclared constant pool reader 'cpr' (obtained by makeConstantPoolReader below).
   * 
   * @throws NullPointerException if r is null
   */
  public CTDecoder(CodeReader r, ConstantPoolReader cpr) throws NullPointerException {
    super(r.getBytecode(), r.getRawHandlers(), cpr);
  }

  /**
   * Convert the internal JVM class name to a JVM type name (e.g., java/lang/Object to Ljava/lang/Object;).
   * 
   * @throws IllegalArgumentException if s is null
   */
  public static String convertClassToType(String s) {
    if (s == null) {
      throw new IllegalArgumentException("s is null");
    }
    if (s.length() > 0 && s.charAt(0) != '[') {
      return 'L' + s + ';';
    } else {
      return s;
    }
  }

  /**
   * Build a ConstantPoolReader implementation to read the constant pool from 'cr'.
   */
  public static ConstantPoolReader makeConstantPoolReader(ClassReader cr) throws IllegalArgumentException {
    if (cr == null) {
      throw new IllegalArgumentException("illegal null cr");
    }
    return new CPReader(cr.getCP());
  }

  final static class CPReader extends ConstantPoolReader {
    final private ConstantPoolParser cp;

    CPReader(ConstantPoolParser cp) {
      this.cp = cp;
    }

    @Override
    public int getConstantPoolItemType(int index) {
      return cp.getItemType(index);
    }

    private static Error convertToError(InvalidClassFileException e) {
      e.printStackTrace();
      return new Error("Invalid class file: " + e.getMessage());
    }

    @Override
    public int getConstantPoolInteger(int index) {
      return cp.getCPInt(index);
    }

    @Override
    public float getConstantPoolFloat(int index) {
      return cp.getCPFloat(index);
    }

    @Override
    public long getConstantPoolLong(int index) {
      return cp.getCPLong(index);
    }

    @Override
    public double getConstantPoolDouble(int index) {
      return cp.getCPDouble(index);
    }

    @Override
    public String getConstantPoolMethodType(int index) {
      try {
        return cp.getCPMethodType(index);
      } catch (InvalidClassFileException e) {
        throw convertToError(e);
      }
    }

    @Override
    public String getConstantPoolString(int index) {
      try {
        return cp.getCPString(index);
      } catch (InvalidClassFileException e) {
        throw convertToError(e);
      }
    }

    @Override
    public String getConstantPoolClassType(int index) {
      try {
        return convertClassToType(cp.getCPClass(index));
      } catch (InvalidClassFileException e) {
        throw convertToError(e);
      }
    }

    @Override
    public String getConstantPoolMemberClassType(int index) {
      try {
        return convertClassToType(cp.getCPRefClass(index));
      } catch (InvalidClassFileException e) {
        throw convertToError(e);
      }
    }

    @Override
    public String getConstantPoolMemberName(int index) {
      try {
        return cp.getCPRefName(index);
      } catch (InvalidClassFileException e) {
        throw convertToError(e);
      }
    }

    @Override
    public String getConstantPoolMemberType(int index) {
      try {
        return cp.getCPRefType(index);
      } catch (InvalidClassFileException e) {
        throw convertToError(e);
      }
    }

    @Override
    public String getConstantPoolHandleClassType(int index) {
      try {
        return cp.getCPHandleClass(index);
      } catch (InvalidClassFileException e) {
        throw convertToError(e);
      }
    }

    @Override
    public String getConstantPoolHandleName(int index) {
      try {
        return cp.getCPHandleName(index);
      } catch (InvalidClassFileException e) {
        throw convertToError(e);
      }
    }

    @Override
    public String getConstantPoolHandleType(int index) {
      try {
        return cp.getCPHandleType(index);
      } catch (InvalidClassFileException e) {
        throw convertToError(e);
      }
    }

    @Override
    public byte getConstantPoolHandleKind(int index) {
      try {
        return cp.getCPHandleKind(index);
      } catch (InvalidClassFileException e) {
        throw convertToError(e);
      }
    }

    @Override
    public BootstrapMethod getConstantPoolDynamicBootstrap(int index) {
      try {
        return cp.getCPDynBootstrap(index);
      } catch (InvalidClassFileException e) {
        throw convertToError(e);
      }
    }

    @Override
    public String getConstantPoolDynamicName(int index) {
      try {
        return cp.getCPDynName(index);
      } catch (InvalidClassFileException e) {
        throw convertToError(e);
      }
    }

    @Override
    public String getConstantPoolDynamicType(int index) {
      try {
        return cp.getCPDynType(index);
      } catch (InvalidClassFileException e) {
        throw convertToError(e);
      }
    }
  }
}
