/*
 * FindBugs - Find Bugs in Java programs
 * Copyright (C) 2005, University of Maryland
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs.visitclass;

import org.apache.bcel.Repository;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.CodeException;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.InnerClass;
import org.apache.bcel.classfile.InnerClasses;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.LineNumber;
import org.apache.bcel.classfile.LineNumberTable;
import org.apache.bcel.classfile.Method;

import edu.umd.cs.findbugs.annotations.CheckForNull;

/**
 * @author pugh
 */
public class Util {
	/**
	 * Determine the outer class of obj.
	 * @param obj
	 * @return JavaClass for outer class, or null if obj is not an outer class
	 * @throws ClassNotFoundException
	 */

	@CheckForNull public static JavaClass getOuterClass(JavaClass obj) throws ClassNotFoundException {
		for(Attribute a : obj.getAttributes()) 
			if (a instanceof InnerClasses) {
				for(InnerClass ic :  ((InnerClasses) a).getInnerClasses()) {
					if (obj.getClassNameIndex() == ic.getInnerClassIndex()) {
//						System.out.println("Outer class is " + ic.getOuterClassIndex());
						ConstantClass oc = (ConstantClass) obj.getConstantPool().getConstant(ic.getOuterClassIndex());
						String ocName = oc.getBytes(obj.getConstantPool());
						return Repository.lookupClass(ocName);
					}
				}
			}
		return null;
	}
	 public static int getSizeOfSurroundingTryBlock(Method method, String vmNameOfExceptionClass, int pc) {
		 return getSizeOfSurroundingTryBlock(method.getConstantPool(), method.getCode(), vmNameOfExceptionClass, pc);
	 }
			
		 
	 public static int getSizeOfSurroundingTryBlock(ConstantPool constantPool, Code code, @CheckForNull String vmNameOfExceptionClass, int pc) {
			int size = Integer.MAX_VALUE;
			int tightStartPC = 0;
			int tightEndPC = Integer.MAX_VALUE;
			if (code.getExceptionTable() == null) return size;
			for (CodeException catchBlock : code.getExceptionTable()) {
				if (vmNameOfExceptionClass != null) {
					Constant catchType = constantPool.getConstant(catchBlock.getCatchType());
					if (catchType == null || catchType instanceof ConstantClass 
							&& !((ConstantClass)catchType).getBytes(constantPool).equals(vmNameOfExceptionClass)) continue;
				}
				int startPC = catchBlock.getStartPC();
				int endPC = catchBlock.getEndPC();
				if (pc >= startPC && pc <= endPC) {
					int thisSize = endPC - startPC;
					if (size > thisSize) {
						size = thisSize;
						tightStartPC = startPC;
						tightEndPC = endPC;
					}
				}
			}
			if (size == Integer.MAX_VALUE) return size;

			// try to guestimate number of lines that correspond
			size = (size+7) / 8;
			LineNumberTable lineNumberTable = code.getLineNumberTable();
			if (lineNumberTable == null) return size;

			int count = 0;
			for(LineNumber line : lineNumberTable.getLineNumberTable()) {
				if (line.getStartPC() > tightEndPC) break;
				if (line.getStartPC() >= tightStartPC) count++;
			}
			return count;

	 }


}
