/*
 * Bytecode Analysis Framework
 * 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.ba.constant;

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

/**
 * Abstract dataflow value representing a value which
 * may or may not be a constant.
 * 
 * @see edu.umd.cs.findbugs.ba.constant.ConstantAnalysis
 * @author David Hovemeyer
 */
public class Constant {
	private final Object value;

	/**
	 * Single instance representing all non-constant values.
	 */
	public static final Constant NOT_CONSTANT = new Constant(null);

	/**
	 * Constructor for a constant value.
	 * 
	 * @param value the constant value; must be a String, Integer, etc.
	 */
	public Constant(@Nullable Object value) {
		this.value = value;
	}

	/**
	 * Return whether or not this value is a constant.
	 * 
	 * @return true if the value is a constant, false if not
	 */
	public boolean isConstant() {
		return value != null;
	}

	/**
	 * Return whether or not this value is a constant String.
	 * 
	 * @return true if the value is a constant String, false if not
	 */
	public boolean isConstantString() {
		return isConstant() && (value instanceof String);
	}

	/**
	 * Get the constant String value of this value.
	 * 
	 * @return the constant String value
	 */
	public String getConstantString() {
		return (String) value;
	}

	/**
	 * Return whether or not this value is a constant int/Integer.
	 * 
	 * @return true if the value is a constant int/Integer, false if not
	 */
	public boolean isConstantInteger() {
		return isConstant() && (value instanceof Integer);
	}


	/**
	 * Get the constant int value of this value.
	 * 
	 * @return the constant int value
	 */
	public int getConstantInt() {
		return ((Integer) value).intValue();
	}


	/**
	 * Merge two Constants.
	 * 
	 * @param a a Constant
	 * @param b another Constant
	 * @return the merge (dataflow meet) of the two Constants
	 */
	public static Constant merge(Constant a, Constant b) {
		if (!a.isConstant() || !b.isConstant())
			return NOT_CONSTANT;
		if (a.value.getClass() != b.value.getClass() || !a.value.equals(b.value))
			return NOT_CONSTANT;
		return a;
	}


	@Override
		 public boolean equals(Object obj) {
		if (obj == null || obj.getClass() != this.getClass())
			return false;
		Constant other = (Constant) obj;
		if (other.value == this.value)
			return true;
		else if (other.value == null || this.value == null)
			return false;
		else
			return this.value.equals(other.value);
	}


	@Override
		 public int hashCode() {
		return (value == null) ? 123 : value.hashCode();
	}

	@Override
		 public String toString() {
		if (!isConstant()) {
			return "-";
		} else {
			return "<" + value.toString() + ">";
		}
	}
}
