/*
 * LpLiteral.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):
 * - members added, documented
 * - constructor added, tested, documented
 * v0.2.1 (2007-01-03):
 * - equals and hashCode added, tested, documented
 * v0.2.2 (2007-01-05):
 * - 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 methods isPositive(), getPredicate() and getArguments() added
 * - accept(LpStructureUnit) added
 * - documentation updated
 * v0.2.5 (2007-01-28):
 * - documentation updated
 * v0.2.6 (2007-02-11):
 * - changed quite a lot -- a part of it extracted to the new LpAtom class
 * - instances should not be directly created, they should be accessed through
 *   LpAtom instances
 * v1.0.0 (2007-05-04):
 * - promoted to version 1.0.0 :o)
 */

package lp.struct;

import java.util.List;

/**
 * This class represents a literal in logic programming. Such a
 * literal can be positive or negative (default) and relates to exactly one atom
 * represented by a {@link LpAtom instance}.
 *
 * @author Martin Slota
 * @version 1.0.0
 * @see LpAtom
 * @see LpRule
 */
public class LpLiteral extends LpAbstractStructureUnit {
	/**
	 * The polarity of this literal. {@code true} if this is a positive literal,
	 * {@code false} if this is a default literal.
	 */
	private final boolean positive;
	
	/**
	 * The atom part of this literal.
	 */
	private final LpAtom atom;
	
	/**
	 * Creates a new instance with the given values as members. Should only be
	 * directly used by methods of {@link LpAtom}.
	 *
	 * @param positive {@code true} if it's supposed to be a positive
	 * literal, {@code false} if it's supposed to be a default literal
	 * @param atom the atom part of this literal
	 * @throws IllegalArgumentException if {@code atom} is {@code null}
	 */
	LpLiteral(boolean positive, LpAtom atom) {
		this.positive = positive;
		if (atom == null) {
			throw new IllegalArgumentException("Atom is null!");
		}
		this.atom = atom;
	}
	
	/**
	 * Returns {@code true} if this literal is positive and {@code false}
	 * otherwise.
	 *
	 * @return {@code true} iff this literal is positive
	 */
	public boolean isPositive() {
		return positive;
	}
	
	/**
	 * Returns the atom used to create this literal, the same that was
	 * given to the constructor.
	 *
	 * @return the {@code LpAtom} instance given in the constructor
	 */
	public LpAtom getAtom() {
		return atom;
	}
	
	/**
	 * Returns the predicate symbol of this literal.
	 *
	 * @return the {@code LpPredicate} instance representing the predicate
	 * symbol used in this literal
	 * @see LpAtom#getPredicate()
	 */
	public LpPredicate getPredicate() {
		return getAtom().getPredicate();
	}
	
	/**
	 * Returns the list of arguments of this literal.
	 *
	 * @return an unmodifiable view of the argument list of this literal
	 * @see LpAtom#getArguments()
	 */
	public List<LpTerm> getArguments() {
		return getAtom().getArguments();
	}
	
	/**
	 * Accepts {@code LpStructureUnitVisitor} instance, i.e. calls its
	 * {@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 LpLiteral} instance,</li>
	 * <li>it has the same polarity as this literal,</li>
	 * <li>its underlying {@link LpAtom} instance is equal to this literal's
	 * underlying {@link LpAtom} instance</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 LpAtom#equals(Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof LpLiteral))
			return false;
		
		LpLiteral l = (LpLiteral) obj;
		
		if (isPositive() != l.isPositive())
			return false;
		
		return getAtom().equals(l.getAtom());
	}
	
	/**
	 * Overriden in order to maintain the general contract of
	 * {@link Object#hashCode()}.
	 *
	 * @return the hash of this object
	 * @see LpAtom#hashCode()
	 */
	@Override
	public int hashCode() {
		int result = 0;
		result += isPositive() ? 1 : 0;
		result += getAtom().hashCode();
		return result;
	}
}