/*******************************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation and others.
 * 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
 *     Tim Tromey - update method signature syntax (bug 31507)
 *******************************************************************************/
package org.eclipse.jdi.internal;


import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.eclipse.jdi.internal.jdwp.JdwpCommandPacket;
import org.eclipse.jdi.internal.jdwp.JdwpMethodID;
import org.eclipse.jdi.internal.jdwp.JdwpReplyPacket;

import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.ClassLoaderReference;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.InvalidCodeIndexException;
import com.sun.jdi.InvalidLineNumberException;
import com.sun.jdi.Locatable;
import com.sun.jdi.Location;
import com.sun.jdi.Method;
import com.sun.jdi.Type;


/**
 * Implementation of com.sun.jdi.Method.
 */
public class MethodImpl extends TypeComponentImpl implements Method, Locatable {
	/** InvokeOptions Constants. */
	public static final int INVOKE_SINGLE_THREADED_JDWP = 0x01;
	public static final int INVOKE_NONVIRTUAL_JDWP = 0x02;
	
	/** Map with Strings for flag bits. */
	private static String[] fgInvokeOptions = null;
	
	/** MethodTypeID that corresponds to this reference. */
	private JdwpMethodID fMethodID;
	
	/** The following are the stored results of JDWP calls. */
	private List fVariables = null;
	private long fLowestValidCodeIndex = -1;
	private long fHighestValidCodeIndex = -1;
	private Map fCodeIndexToLine = null;
	private Map fLineToCodeIndexes = null;
	private Map fStratumAllLineLocations = null;
	private int fArgumentSlotsCount = -1;
	private List fArguments = null;
	private List fArgumentTypes = null;
	private List fArgumentTypeNames = null;
	private List fArgumentTypeSignatures = null;
	private byte[] fByteCodes = null;
	private long[] fCodeIndexTable;
	private int[] fJavaStratumLineNumberTable;
	
	private String fReturnTypeName= null;
	
	/**
	 * Creates new MethodImpl.
	 */
	public MethodImpl(VirtualMachineImpl vmImpl, ReferenceTypeImpl declaringType, JdwpMethodID methodID, String name, String signature, String genericSignature, int modifierBits) {
		super("Method", vmImpl, declaringType, name, signature, genericSignature, modifierBits); //$NON-NLS-1$
		fMethodID = methodID;
	}

	/**
	 * Flushes all stored Jdwp results.
	 */
	protected void flushStoredJdwpResults() {
		fVariables = null;
		fLowestValidCodeIndex = -1;
		fHighestValidCodeIndex = -1;
		fCodeIndexToLine = null;
		fLineToCodeIndexes = null;
		fStratumAllLineLocations = null;
		fCodeIndexTable= null;
		fJavaStratumLineNumberTable= null;
		fArgumentSlotsCount = -1;
		fArguments = null;
		fArgumentTypes = null;
		fArgumentTypeNames = null;
		fArgumentTypeSignatures = null;
		fByteCodes = null;
	}
	
	/** 
	 * @return Returns methodID of method.
	 */
	protected JdwpMethodID getMethodID() {
		return fMethodID;
	}
	
	/** 
	 * @return Returns map of location to line number.
	 */
	protected Map javaStratumCodeIndexToLine() throws AbsentInformationException {
		if (isAbstract()) {
			return Collections.EMPTY_MAP;
		}
		getLineTable();
		return fCodeIndexToLine;
	}
	
	/** 
	 * @return Returns map of line number to locations.
	 */
	protected List javaStratumLineToCodeIndexes(int line) throws AbsentInformationException {
		if (isAbstract() || isNative()) {
			return null;
		}
		getLineTable();
		
		return (List)fLineToCodeIndexes.get(new Integer(line));
	}
	
	/** 
	 * Gets line table from VM.
	 */
	private void getLineTable() throws AbsentInformationException {
		if (isObsolete()) {
			return;
		}
		if (fCodeIndexToLine != null) {
			if (fCodeIndexToLine.isEmpty()) {
				throw new AbsentInformationException(JDIMessages.MethodImpl_Got_empty_line_number_table_for_this_method_1); //$NON-NLS-1$
			}
			return;
		}

		initJdwpRequest();
		try {
			ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
			DataOutputStream outData = new DataOutputStream(outBytes);
			writeWithReferenceType(this, outData);
	
			JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.M_LINE_TABLE, outBytes);
			switch (replyPacket.errorCode()) {
				case JdwpReplyPacket.ABSENT_INFORMATION:
					throw new AbsentInformationException(JDIMessages.MethodImpl_No_line_number_information_available_2); //$NON-NLS-1$
				case JdwpReplyPacket.NATIVE_METHOD:
					throw new AbsentInformationException(JDIMessages.MethodImpl_No_line_number_information_available_2); //$NON-NLS-1$
			}
			defaultReplyErrorHandler(replyPacket.errorCode());
			
			DataInputStream replyData = replyPacket.dataInStream();
			fLowestValidCodeIndex = readLong("lowest index", replyData); //$NON-NLS-1$
			fHighestValidCodeIndex = readLong("highest index", replyData); //$NON-NLS-1$
			int nrOfElements = readInt("elements", replyData); //$NON-NLS-1$
			fCodeIndexToLine = new HashMap();
			fLineToCodeIndexes = new HashMap();
			if (nrOfElements == 0) {
				throw new AbsentInformationException(JDIMessages.MethodImpl_Got_empty_line_number_table_for_this_method_3); //$NON-NLS-1$
			}
			fCodeIndexTable= new long[nrOfElements];
			fJavaStratumLineNumberTable= new int[nrOfElements];
			for (int i = 0; i < nrOfElements; i++) {
				long lineCodeIndex = readLong("code index", replyData); //$NON-NLS-1$
				Long lineCodeIndexLong = new Long(lineCodeIndex);
				int lineNr = readInt("line nr", replyData); //$NON-NLS-1$
				Integer lineNrInt = new Integer(lineNr);
				
				// Add entry to code-index to line mapping.
				fCodeIndexToLine.put(lineCodeIndexLong, lineNrInt);
				
				fCodeIndexTable[i]= lineCodeIndex;
				fJavaStratumLineNumberTable[i]= lineNr;
				
				List lineNrEntry = (List)fLineToCodeIndexes.get(lineNrInt);
				if (lineNrEntry == null) {
					lineNrEntry= new ArrayList();
					fLineToCodeIndexes.put(lineNrInt, lineNrEntry);
				}
				lineNrEntry.add(lineCodeIndexLong);
			}
		} catch (IOException e) {
			fCodeIndexToLine = null;
			fLineToCodeIndexes = null;
			defaultIOExceptionHandler(e);
		} finally {
			handledJdwpRequest();
		}
	}	
	
	/** 
	 * @return Returns the line number that corresponds to the given lineCodeIndex.
	 */
	protected int javaStratumLineNumber(long lineCodeIndex) throws AbsentInformationException {
		if (isAbstract() || isNative() || isObsolete()) {
			return -1;
		}
		getLineTable();
		if (lineCodeIndex > fHighestValidCodeIndex) {
			throw new InvalidCodeIndexException (JDIMessages.MethodImpl_Invalid_code_index_of_a_location_given_4); //$NON-NLS-1$
		}

		Long lineCodeIndexObj;
		Integer lineNrObj;
		long index= lineCodeIndex;
		// Search for the line where this code index is located.
		do {
			lineCodeIndexObj = new Long(index);
			lineNrObj = (Integer)javaStratumCodeIndexToLine().get(lineCodeIndexObj);
		} while (lineNrObj == null && --index >= fLowestValidCodeIndex);
		if (lineNrObj == null) {
			if (lineCodeIndex >= fLowestValidCodeIndex) {
				index= lineCodeIndex;
				do {
					lineCodeIndexObj = new Long(index);
					lineNrObj = (Integer)javaStratumCodeIndexToLine().get(lineCodeIndexObj);
				} while (lineNrObj == null && ++index <= fHighestValidCodeIndex);
				if (lineNrObj != null) {
					return lineNrObj.intValue();
				}
			}
			throw new InvalidCodeIndexException (JDIMessages.MethodImpl_Invalid_code_index_of_a_location_given_4); //$NON-NLS-1$
		}
		return lineNrObj.intValue();
	}

	
	/**
	 * @see com.sun.jdi.Method#allLineLocations()
	 */
	public List allLineLocations() throws AbsentInformationException {
		return allLineLocations(virtualMachine().getDefaultStratum(), null);
	}
	
	/**
	 * @see com.sun.jdi.Method#arguments()
	 */
	public List arguments() throws AbsentInformationException {
		if (isNative() || isAbstract()) {
			throw new AbsentInformationException(JDIMessages.MethodImpl_No_local_variable_information_available_9); //$NON-NLS-1$
		}
		if (fArguments != null) {
			return fArguments;
		}
		
		List result = new ArrayList();	
		Iterator iter = variables().iterator();
		while (iter.hasNext()) {
			LocalVariableImpl var = (LocalVariableImpl)iter.next();
			if (var.isArgument())
				result.add(var);
		}
		fArguments = result;
		return fArguments;
	}
	
	/** 
	 * @return Returns a text representation of all declared argument types of this method. 
	 */
	public List argumentTypeNames() {
		if (fArgumentTypeNames != null) {
			return fArgumentTypeNames;
		}
		List argumentTypeSignatures= argumentTypeSignatures();
		List result= new ArrayList();
		for (Iterator iter= argumentTypeSignatures.iterator(); iter.hasNext();) {
			result.add(TypeImpl.signatureToName((String) iter.next()));
		}
		
		fArgumentTypeNames= result;
		return fArgumentTypeNames;
	}


	/** 
	 * @return Returns a signatures of all declared argument types of this method. 
	 */
	private List argumentTypeSignatures() {
		if (fArgumentTypeSignatures != null) {
			return fArgumentTypeSignatures;
		}
		
		fArgumentTypeSignatures= GenericSignature.getParameterTypes(signature());
		return fArgumentTypeSignatures;
	}

	/** 
	 * @return Returns the list containing the type of each argument. 
	 */
	public List argumentTypes() throws ClassNotLoadedException {
		if (fArgumentTypes != null) {
			return fArgumentTypes;
		}

		List result = new ArrayList();
		Iterator iter = argumentTypeSignatures().iterator();
		ClassLoaderReference classLoaderRef= declaringType().classLoader();
		VirtualMachineImpl vm= virtualMachineImpl();
		while (iter.hasNext()) {
			String argumentTypeSignature = (String)iter.next();
			result.add(TypeImpl.create(vm, argumentTypeSignature, classLoaderRef));
		}
		fArgumentTypes = result;
		return fArgumentTypes;
	}

	/** 
	 * @return Returns an array containing the bytecodes for this method. 
	 */
	public byte[] bytecodes() {
		if (fByteCodes != null) {
			return fByteCodes;
		}

		initJdwpRequest();
		try {
			ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
			DataOutputStream outData = new DataOutputStream(outBytes);
			writeWithReferenceType(this, outData);
			
			JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.M_BYTECODES, outBytes);
			defaultReplyErrorHandler(replyPacket.errorCode());
			
			DataInputStream replyData = replyPacket.dataInStream();
			int length = readInt("length", replyData); //$NON-NLS-1$
			fByteCodes = readByteArray(length, "bytecodes", replyData); //$NON-NLS-1$
			return fByteCodes;
		} catch (IOException e) {
			fByteCodes = null;
			defaultIOExceptionHandler(e);
			return null;
		} finally {
			handledJdwpRequest();
		}
	}

	/** 
	 * @return Returns the hash code value.
	 */
	public int hashCode() {
		return fMethodID.hashCode();
	}
	
	/**
	 * @return Returns true if two mirrors refer to the same entity in the target VM.
	 * @see java.lang.Object#equals(Object)
	 */
	public boolean equals(Object object) {
		return object != null
			&& object.getClass().equals(this.getClass())
			&& fMethodID.equals(((MethodImpl)object).fMethodID)
			&& referenceTypeImpl().equals(((MethodImpl)object).referenceTypeImpl());
	}

	/**
	 * @return Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
	 */
	public int compareTo(Object object) {
		if (object == null || !object.getClass().equals(this.getClass()))
			throw new ClassCastException(JDIMessages.MethodImpl_Can__t_compare_method_to_given_object_6); //$NON-NLS-1$
		
		// See if declaring types are the same, if not return comparison between declaring types.
		Method type2 = (Method)object;
		if (!declaringType().equals(type2.declaringType()))
			return declaringType().compareTo(type2.declaringType());
		
		// Return comparison of position within declaring type.
		int index1 = declaringType().methods().indexOf(this);
		int index2 = type2.declaringType().methods().indexOf(type2);
		if (index1 < index2) {
			return -1;
		} else if (index1 > index2) {
			return 1;
		} else {
			return 0;
		}
	}
	
	
	/**
	 * @see com.sun.jdi.Method#isAbstract()
	 */
	public boolean isAbstract() {
		return (fModifierBits & MODIFIER_ACC_ABSTRACT) != 0;
	}
	
	
	/**
	 * @see com.sun.jdi.Method#isConstructor()
	 */
	public boolean isConstructor() {
		return name().equals("<init>"); //$NON-NLS-1$
	}
	
	/**
	 * @see com.sun.jdi.Method#isNative()
	 */
	public boolean isNative() {
		return (fModifierBits & MODIFIER_ACC_NATIVE) != 0;
	}
		
	/**
	 * @see com.sun.jdi.Method#isStaticInitializer()
	 */
	public boolean isStaticInitializer() {
		return name().equals("<clinit>"); //$NON-NLS-1$
	}
	
	/**
	 * @see com.sun.jdi.Method#isSynchronized()
	 */
	public boolean isSynchronized() {
		return (fModifierBits & MODIFIER_ACC_SYNCHRONIZED) != 0;
	}
	
	/**
	 * @see com.sun.jdi.Method#locationOfCodeIndex(long)
	 */
	public Location locationOfCodeIndex(long index) {
		if (isAbstract() || isNative()) {
			return null;
		}
		try {
			Integer lineNrInt = (Integer)javaStratumCodeIndexToLine().get(new Long(index));
			if (lineNrInt == null) {
				throw new InvalidCodeIndexException(MessageFormat.format(JDIMessages.MethodImpl_No_valid_location_at_the_specified_code_index__0__2, new Object[]{Long.toString(index)})); //$NON-NLS-1$
			}
		} catch (AbsentInformationException e ) {
		}
		return new LocationImpl(virtualMachineImpl(), this, index);
	}
	
	/**
	 * @see com.sun.jdi.Method#locationsOfLine(int)
	 */
	public List locationsOfLine(int line) throws AbsentInformationException, InvalidLineNumberException {
		return locationsOfLine(virtualMachine().getDefaultStratum(), null, line);
	}
	
	/**
	 * @see com.sun.jdi.Method#returnType()
	 */
	public Type returnType() throws ClassNotLoadedException {
		int startIndex = signature().lastIndexOf(')') + 1;	// Signature position is just after ending brace.
		return TypeImpl.create(virtualMachineImpl(), signature().substring(startIndex), declaringType().classLoader());
	}

	
	/**
	 * @see com.sun.jdi.Method#returnTypeName()
	 */
	public String returnTypeName() {
		if (fReturnTypeName != null) {
			return fReturnTypeName;
		}
		int startIndex = signature().lastIndexOf(')') + 1;	// Signature position is just after ending brace.
		fReturnTypeName= TypeImpl.signatureToName(signature().substring(startIndex));
		return fReturnTypeName;
	}	
	
	
	/**
	 * @see com.sun.jdi.Method#variables()
	 */
	public List variables() throws AbsentInformationException {
		if (isNative() || isAbstract()) {
			throw new AbsentInformationException(JDIMessages.MethodImpl_No_local_variable_information_available_9); //$NON-NLS-1$
		}
		
		if (fVariables != null) {
			return fVariables;
		}

		initJdwpRequest();
		try {
			ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
			DataOutputStream outData = new DataOutputStream(outBytes);
			writeWithReferenceType(this, outData);
			
			boolean withGenericSignature= virtualMachineImpl().isJdwpVersionGreaterOrEqual(1, 5);
			int jdwpCommand= withGenericSignature ? JdwpCommandPacket.M_VARIABLE_TABLE_WITH_GENERIC : JdwpCommandPacket.M_VARIABLE_TABLE;
			JdwpReplyPacket replyPacket = requestVM(jdwpCommand, outBytes);
			switch (replyPacket.errorCode()) {
				case JdwpReplyPacket.ABSENT_INFORMATION:
					return inferArguments();
			}
			
			defaultReplyErrorHandler(replyPacket.errorCode());
			
			DataInputStream replyData = replyPacket.dataInStream();
			fArgumentSlotsCount = readInt("arg count", replyData); //$NON-NLS-1$
			int nrOfElements = readInt("elements", replyData); //$NON-NLS-1$
			List variables = new ArrayList(nrOfElements);
			for (int i = 0; i < nrOfElements; i++) {
				long codeIndex = readLong("code index", replyData); //$NON-NLS-1$
				String name = readString("name", replyData); //$NON-NLS-1$
				String signature = readString("signature", replyData); //$NON-NLS-1$
				String genericSignature= null;
				if (withGenericSignature) {
					genericSignature= readString("generic signature", replyData); //$NON-NLS-1$
					if ("".equals(genericSignature)) { //$NON-NLS-1$
						genericSignature= null;
					}
				}
				int length = readInt("length", replyData); //$NON-NLS-1$
				int slot = readInt("slot", replyData); //$NON-NLS-1$
				boolean isArgument = slot < fArgumentSlotsCount;

				// Note that for instance methods, the first slot contains the this reference.
				if (isStatic() || slot > 0) {
					LocalVariableImpl localVar = new LocalVariableImpl(virtualMachineImpl(), this, codeIndex, name, signature, genericSignature, length, slot, isArgument);
					variables.add(localVar);
				}
			}
			fVariables= variables;
			return fVariables;
		} catch (IOException e) {
			fArgumentSlotsCount = -1;
			fVariables = null;
			defaultIOExceptionHandler(e);
			return null;
		} finally {
			handledJdwpRequest();
		}
	}
	
	
	/**
	 * @throws AbsentInformationException
	 */
	private List inferArguments() throws AbsentInformationException {
		// infer arguments, if possible
		
		// try to generate the right generic signature for each argument
		String genericSignature= genericSignature();
		String[] signatures= (String[]) argumentTypeSignatures().toArray(new String[0]);
		String[] genericSignatures;
		if (genericSignature == null) {
			genericSignatures= new String[signatures.length];
		} else {
			genericSignatures= (String[]) GenericSignature.getParameterTypes(genericSignature).toArray(new String[0]);
			for (int i= 0; i < genericSignatures.length; i++) {
				if (genericSignatures[i].equals(signatures[i])) {
					genericSignatures[i]= null;
				}
			}
		}
		
		int slot = 0;
		if (!isStatic()) {
			slot++;
		}
		if (signatures.length >0) {
			fArgumentSlotsCount = signatures.length;
			fVariables = new ArrayList(fArgumentSlotsCount);
			for (int i = 0; i < signatures.length; i++) {
				String name = "arg" + i; //$NON-NLS-1$
				LocalVariableImpl localVar = new LocalVariableImpl(virtualMachineImpl(), this, 0, name, signatures[i], genericSignatures[i], -1, slot, true);
				fVariables.add(localVar);
				slot++;
			}
			return fVariables;
		}
		throw new AbsentInformationException(JDIMessages.MethodImpl_No_local_variable_information_available_9); //$NON-NLS-1$

	}

	/**
	 * @see com.sun.jdi.Method#variablesByName(String)
	 */
	public List variablesByName(String name) throws AbsentInformationException {
		Iterator iter = variables().iterator();
		List result = new ArrayList();
		while (iter.hasNext()) {
			LocalVariableImpl var = (LocalVariableImpl)iter.next();
			if (var.name().equals(name)) {
				result.add(var);
			}
		}
		return result;
	}

	/**
	 * @see com.sun.jdi.Locatable#location()
	 */
	public Location location() {
		if (isAbstract()) {
			return null;
		}
		if (isNative()) {
			return new LocationImpl(virtualMachineImpl(), this, -1);
		}
		// First retrieve line code table.
   		try {
			getLineTable();
		} catch (AbsentInformationException e) {
			return new LocationImpl(virtualMachineImpl(), this, -1);
		}

		// Return location with Lowest Valid Code Index.
		return new LocationImpl(virtualMachineImpl(), this, fLowestValidCodeIndex);
	}
	
	/**
	 * Writes JDWP representation.
	 */
	public void write(MirrorImpl target, DataOutputStream out) throws IOException {
		fMethodID.write(out);
		if (target.fVerboseWriter != null) {
			target.fVerboseWriter.println("method", fMethodID.value()); //$NON-NLS-1$
		}
	}
	
	/**
	 * Writes JDWP representation, including ReferenceType.
	 */
	protected void writeWithReferenceType(MirrorImpl target, DataOutputStream out) throws IOException {
		referenceTypeImpl().write(target, out);
		write(target, out);
	}

	/**
	 * Writes JDWP representation, including ReferenceType with Tag.
	 */
	protected void writeWithReferenceTypeWithTag(MirrorImpl target, DataOutputStream out) throws IOException {
		referenceTypeImpl().writeWithTag(target, out);
		write(target, out);
	}

	/**
	 * @return Reads JDWP representation and returns new instance.
	 */
	protected static MethodImpl readWithReferenceTypeWithTag(MirrorImpl target, DataInputStream in) throws IOException {
		VirtualMachineImpl vmImpl = target.virtualMachineImpl();
	  	// See Location.
		ReferenceTypeImpl referenceType = ReferenceTypeImpl.readWithTypeTag(target, in);
		if (referenceType == null)
			return null;

		JdwpMethodID ID = new JdwpMethodID(vmImpl);
		if (target.fVerboseWriter != null) {
			target.fVerboseWriter.println("method", ID.value()); //$NON-NLS-1$
		}

		ID.read(in);
		if (ID.isNull()) {
			return null;
		}
			
		// The method must be part of a known reference type.
		MethodImpl method = referenceType.findMethod(ID);
		if (method == null) {
			throw new InternalError(JDIMessages.MethodImpl_Got_MethodID_of_ReferenceType_that_is_not_a_member_of_the_ReferenceType_10); //$NON-NLS-1$
		}
		return method;
	}
	
	/**
	 * @return Reads JDWP representation and returns new instance.
	 */
	protected static MethodImpl readWithNameSignatureModifiers(ReferenceTypeImpl target, ReferenceTypeImpl referenceType, boolean withGenericSignature, DataInputStream in)  throws IOException {
		VirtualMachineImpl vmImpl = target.virtualMachineImpl();
		JdwpMethodID ID = new JdwpMethodID(vmImpl);
		ID.read(in);
		if (target.fVerboseWriter != null) {
			target.fVerboseWriter.println("method", ID.value()); //$NON-NLS-1$
		}

		if (ID.isNull()) {
			return null;
		}
		String name = target.readString("name", in); //$NON-NLS-1$
		String signature = target.readString("signature", in); //$NON-NLS-1$
		String genericSignature= null;
		if (withGenericSignature) {
			genericSignature= target.readString("generic signature", in); //$NON-NLS-1$
			if ("".equals(genericSignature)) { //$NON-NLS-1$
				genericSignature= null;
			}
		}
		int modifierBits= target.readInt("modifiers", AccessibleImpl.getModifierStrings(), in); //$NON-NLS-1$

		MethodImpl mirror = new MethodImpl(vmImpl, referenceType, ID, name, signature, genericSignature, modifierBits);
		return mirror;
	}

	/**
	 * Retrieves constant mappings.
	 */
	public static void getConstantMaps() {
		if (fgInvokeOptions != null) {
			return;
		}
		
		Field[] fields = MethodImpl.class.getDeclaredFields();
		fgInvokeOptions = new String[32];

		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			if ((field.getModifiers() & Modifier.PUBLIC) == 0 || (field.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0 || (field.getModifiers() & Modifier.FINAL) == 0) {
				continue;
			}
				
			try {
				String name = field.getName();

				if (name.startsWith("INVOKE_")) { //$NON-NLS-1$
					int value = field.getInt(null);
					for (int j = 0; j < fgInvokeOptions.length; j++) {
						if ((1 << j & value) != 0) {
							fgInvokeOptions[j]= name;
							break;
						}
					}
				}
			} catch (IllegalAccessException e) {
				// Will not occur for own class.
			} catch (IllegalArgumentException e) {
				// Should not occur.
				// We should take care that all public static final constants
				// in this class are numbers that are convertible to int.
			}
		}
	}
	
	/**
	 * @return Returns a map with string representations of tags.
	 */
	 protected static String[] getInvokeOptions() {
	 	getConstantMaps();
	 	return fgInvokeOptions;
	 }
	/**
	 * @see Method#isObsolete()
	 * 
	 * The JDK 1.4.0 specification states that obsolete methods
	 * are given an ID of zero. It also states that when a method
	 * is redefined, the new method gets the ID of the old method.
	 * Thus, the JDWP query for isObsolete on JDK 1.4 will never return true
	 * for a non-zero method ID. The query is therefore not needed
	 */
	public boolean isObsolete() {
		if (virtualMachineImpl().isJdwpVersionGreaterOrEqual(1, 4)) {
			return fMethodID.value() == 0;
		}
		return false;
	}

	/**
	 * @see Method#allLineLocations(String, String)
	 */
	public List allLineLocations(String stratum, String sourceName) throws AbsentInformationException {
		if (isAbstract() || isNative()) {
			return Collections.EMPTY_LIST;
		}
		if (stratum == null) { // if stratum not defined use the default stratum for the declaring type
			stratum= declaringType().defaultStratum();
		}
		List allLineLocations= null;
		Map sourceNameAllLineLocations= null;
		if (fStratumAllLineLocations == null) { // the stratum map doesn't exist, create it
			fStratumAllLineLocations= new HashMap();
		} else {
			// get the source name map
			sourceNameAllLineLocations= (Map)fStratumAllLineLocations.get(stratum);
		}
		if (sourceNameAllLineLocations == null) { // the source name map doesn't exist, create it
			sourceNameAllLineLocations= new HashMap();
			fStratumAllLineLocations.put(stratum, sourceNameAllLineLocations);
		} else {
			// get the line locations
			allLineLocations= (List)sourceNameAllLineLocations.get(sourceName);
		}
		if (allLineLocations == null) { // the line locations are not know, compute and store them
			getLineTable();
			allLineLocations= referenceTypeImpl().allLineLocations(stratum, sourceName, this, fCodeIndexTable, fJavaStratumLineNumberTable);
			sourceNameAllLineLocations.put(sourceName, allLineLocations);
		}
		return allLineLocations;
	}

	/**
	 * @see Method#locationsOfLine(String, String, int)
	 */
	public List locationsOfLine(String stratum, String sourceName, int lineNumber) throws AbsentInformationException {
		if (isAbstract() || isNative()) {
			return Collections.EMPTY_LIST;
		}
		return referenceTypeImpl().locationsOfLine(stratum, sourceName, lineNumber, this);
	}

	/**
	 * Return a list which contains a location for the each disjoin range of code indice
	 * that have bean assigned to the given lines (by the compiler or/and the VM).
	 * Return an empty list if there is not executable code at the specified lines.
	 */
	protected List javaStratumLocationsOfLines(List javaLines) throws AbsentInformationException {
		Set	tmpLocations= new TreeSet();
		for (Iterator iter = javaLines.iterator(); iter.hasNext();) {
			Integer key = (Integer)iter.next();
			List indexes= javaStratumLineToCodeIndexes(key.intValue());
			if (indexes != null) {
				tmpLocations.addAll(indexes);
			}
		}
		List locations = new ArrayList();
		for (Iterator iter = tmpLocations.iterator(); iter.hasNext();) {
			long index = ((Long)iter.next()).longValue();
			int position= Arrays.binarySearch(fCodeIndexTable, index);
			if (position == 0 || !tmpLocations.contains(new Long(fCodeIndexTable[position - 1]))) {
				locations.add(new LocationImpl(virtualMachineImpl(), this, index));
			}
		}
		return locations;
	}
	
	public boolean isBridge() {
		return (fModifierBits & MODIFIER_ACC_BRIDGE) != 0;
	}

	public boolean isVarArgs() {
		// TODO: remove this test when j9 solve its problem
		// it returns invalid 1.5 flags for 1.4 classes.
		// see bug 53870
		return !virtualMachine().name().equals("j9") && (fModifierBits & MODIFIER_ACC_VARARGS) != 0; //$NON-NLS-1$
	}
}
