/*
 * LpVariable.java
 *
 * Copyright (C) 2006 - 2007 Martin Slota
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/*
 * History:
 * v0.1 (2007-01-02): initial version
 * v0.2 (2007-01-02):
 * - name member added, documented
 * - constructor added, tested, documented
 * v0.2.1 (2007-01-04):
 * - equals and hashCode added, tested, documented
 * v0.2.2 (2007-01-04):
 * - added "extends LpAbstractStructureUnit" declaration
 * - appendNormalForm(Appendable) added, tested, documented
 * v0.2.3 (2007-01-12):
 * - appendNormalForm modified to throw IllegalArgumentException
 * v0.2.4 (2007-01-18):
 * - appendNormalForm() deleted, the same functionality now provided by
 *   LpPrettyPrinter
 * - getter method getName() added
 * - accept(LpStructureUnit) added
 * - documentation updated
 * v0.2.5 (2007-02-11):
 * - instances are now cached in the static member pool and must be requested
 *   through the getInstance() method
 * v1.0.0 (2007-05-04):
 * - promoted to version 1.0.0 :o)
 */

package lp.struct;

import java.util.HashMap;
import java.util.Map;

/**
 * Represents a variable in logic programming. Variables are, together with
 * function symbols and constants (see {@link LpFunction} and
 * {@link LpConstant}) building units of terms (see {@link LpTerm}). Each
 * variable has a unique non-empty name.
 *
 * @author Martin Slota
 * @version 1.0.0
 * @see LpTerm
 */
public class LpVariable extends LpAbstractStructureUnit implements LpTerm {
	/**
	 * A pool of instances of this immutable class. The
	 * {@link #getInstance(String)} method first looks here for the requested
	 * instances and if it is forced to create a new one, it is added to this
	 * pool.
	 */
	private static final Map<String, LpVariable> pool =
			new HashMap<String, LpVariable>(100);
	
	/**
	 * Returns an instance of {@code LpVariable} with the given name.
	 *
	 * @param name the requested variable's name
	 * @throws IllegalArgumentException if {@code name} is {@code null} or an
	 * empty string
	 */
	public static synchronized LpVariable getInstance(String name) {
		LpVariable result = pool.get(name);
		if (result == null) {
			result = new LpVariable(name);
			pool.put(name, result);
		}
		return result;
	}
	
	/**
	 * This variable's name.
	 */
	private final String name;
	
	/**
	 * Creates a new instance of {@code LpVariable} with the given name.
	 *
	 * @param name the new variable's name
	 * @throws IllegalArgumentException if {@code name} is {@code null} or an
	 * empty string
	 */
	protected LpVariable(String name) {
		if (name == null)
			throw new IllegalArgumentException(
					"The variable's name must not be null!");
		if ("".equals(name))
			throw new IllegalArgumentException(
					"The variable's name must not be empty!");
		this.name = name;
	}
	
	/**
	 * Returns this variable's name, the same that was given to the constructor.
	 *
	 * @return the name of this variable
	 */
	public String getName() {
		return name;
	}
	
	/**
	 * Accepts {@code LpStructureUnitVisitor} instance, i.e. calls
	 * {@code visitor.visit(this)}.
	 *
	 *
	 * @param visitor the visitor to accept
	 */
	public void accept(LpStructureUnitVisitor visitor) {
		visitor.visit(this);
	}
	
	/**
	 * Returns {@code true} if and only if
	 * <ol>
	 * <li>{@code obj} is a {@link LpVariable} instance,</li>
	 * <li>its name is equal to this variable's name as
	 * defined by {@link String#equals(Object)},</li>
	 * </ol>
	 *
	 * @param obj the object to compare with
	 * @return {@code true} if this object is equal to {@code obj} according to
	 * the description above, and {@code false} otherwise
	 * @see String#equals(Object)
	 * @see LpTerm#equals(Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof LpVariable))
			return false;
		LpVariable c = (LpVariable) obj;
		return getName().equals(c.getName());
	}
	
	/**
	 * Overriden in order to maintain the general contract of
	 * {@link Object#hashCode()}.
	 *
	 * @return the hash of this object
	 * @see String#hashCode()
	 * @see LpTerm#equals(Object)
	 */
	@Override
	public int hashCode() {
		return getName().hashCode();
	}
}