/*
 * Bytecode Analysis Framework
 * Copyright (C) 2003,2004 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.ba.vna;

import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.CheckForNull;

/**
 * Factory for ValueNumbers.
 * A single Factory must be used to create all of the ValueNumbers
 * for a method.
 *
 * @author David Hovemeyer
 * @see ValueNumber
 */
public class ValueNumberFactory {
	/**
	 * Store all allocated value numbers.
	 */
	private ArrayList<ValueNumber> allocatedValueList = new ArrayList<ValueNumber>();
	private HashMap<String, ValueNumber> classObjectValueMap = new HashMap<String, ValueNumber>();
	
	/**
	 * Create a fresh (unique) value number.
	 */
	public ValueNumber createFreshValue() {
		ValueNumber result = ValueNumber.createValueNumber(getNumValuesAllocated());
		allocatedValueList.add(result);
		return result;
	}
	public ValueNumber createFreshValue(int flags) {
		ValueNumber result = ValueNumber.createValueNumber(getNumValuesAllocated(), flags);
		allocatedValueList.add(result);
		return result;
	}
	/**
	 * Return a previously allocated value.
	 */
	public ValueNumber forNumber(int number) {
		if (number >= getNumValuesAllocated())
			throw new IllegalArgumentException("Value " + number + " has not been allocated");
		return allocatedValueList.get(number);
	}

	/**
	 * Get the number of values which have been created.
	 */
	public int getNumValuesAllocated() {
		return allocatedValueList.size();
	}

	/**
	 * Compact the value numbers produced by this factory.
	 *
	 * @param map                array mapping old numbers to new numbers
	 * @param numValuesAllocated the number of values allocated in the new numbering
	 */
	 @Deprecated
	public void compact(int[] map, int numValuesAllocated) {
		if (true) throw new UnsupportedOperationException();
		ArrayList<ValueNumber> oldList = this.allocatedValueList;
		ArrayList<ValueNumber> newList = new ArrayList<ValueNumber>(Collections.<ValueNumber>nCopies(numValuesAllocated, null));

		for (ValueNumber value : oldList) {
			int newNumber = map[value.getNumber()];
			if (newNumber >= 0) {
				// Note: because we are simply assigning new numbers to the
				// old ValueNumber objects, their flags remain valid.
				// value.number = newNumber;
				newList.set(newNumber, value);
			}
		}

		this.allocatedValueList = newList;
	}
	 /**
	  * Get the ValueNumber for given class's Class object.
	  *
	  * @param className the class
	  */
	 public ValueNumber getClassObjectValue(@DottedClassName String className) {
		 // assert className.indexOf('.') == -1;
		 // TODO: Check to see if we need to do this
		 className = className.replace('/','.');
		 ValueNumber value = classObjectValueMap.get(className);
		 if (value == null) {
			 value = createFreshValue(ValueNumber.CONSTANT_CLASS_OBJECT);
			 classObjectValueMap.put(className, value);
		 }
		 return value;
	 }
	 
	 public @CheckForNull @DottedClassName String getClassName(ValueNumber v) {
		 if (!v.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) 
			 throw new IllegalArgumentException ("Not a value number for a constant class");
		 for(Map.Entry<String, ValueNumber> e : classObjectValueMap.entrySet()) {
			 if (e.getValue().equals(v)) return e.getKey();
		 }
		 return null;
	 }

}

// vim:ts=4
