/*
* Copyright 2014 Igor Maznitsa (http://www.igormaznitsa.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.igormaznitsa.prol.data;
import com.igormaznitsa.prol.exceptions.ProlCriticalError;
import java.util.Map;
/**
* The term represents a prolog list
*
* @author Igor Maznitsa (igor.maznitsa@igormaznitsa.com)
* @see com.igormaznitsa.prol.data.TermStruct
*/
public final class TermList extends TermStruct {
/**
* The text which is used for the list functor
*/
public static final String LIST_FUNCTOR = ".";
/**
* The term which is used as the functor for a list
*/
public static final Term LIST_FUNCTOR_AS_TERM = new Term(LIST_FUNCTOR);
/**
* The term which is used as the NULL LIST
*/
public static final TermList NULLLIST = new TermList();
/**
* The index of the list head in the element array
*/
private static final int INDEX_HEAD = 0;
/**
* The index of the list tail in the element array
*/
private static final int INDEX_TAIL = 1;
/**
* a constructor to make NULL_LIST
*/
private TermList() {
super(LIST_FUNCTOR, null);
}
/**
* A constructor allows to make a list with a term as its head
*
* @param term the term which will be used as the head of the created list,
* must not be null
*/
public TermList(final Term term) {
super(LIST_FUNCTOR, new Term[]{term, NULLLIST});
}
/**
* A constructor which allows to make a list contains the head and the tail
*
* @param head the head of the created list, must not be null
* @param tail the tail of the created list, must not be null
*/
public TermList(final Term head, final Term tail) {
super(LIST_FUNCTOR, new Term[]{head, tail});
}
/**
* Get the head of the list
*
* @return the head of the list as a term, must not be null (if it is not the
* NULL LIST)
*/
public Term getHead() {
return terms[INDEX_HEAD];
}
/**
* Get the tail of the list
*
* @return the tail of the list as term because it can be as list as not list
* (for '|' case), must not be null (if it is not the NULL LIST)
*/
public Term getTail() {
final Term tail = this.terms[INDEX_TAIL];
switch (tail.getTermType()) {
case TYPE_LIST:
return tail;
case TYPE_VAR: {
final Var var = (Var) tail;
final Term val = var.getValue();
return val == null ? var : val;
}
default:
return tail;
}
}
/**
* Calculate the depth of the list
*
* @return the depth of the list, 0 if the list is the NULL_LIST
*/
public int calculateLength() {
if (this == NULLLIST) {
return 0;
}
final Term tail = this.terms[INDEX_TAIL];
switch (tail.getTermType()) {
case TYPE_LIST: {
return ((TermList) tail).calculateLength() + 1;
}
default:
return 2;
}
}
/**
* Set the tail for the list
*
* @param newTail the new tail value, must not be null
*/
public void setTail(final Term newTail) {
if (newTail == null) {
throw new NullPointerException("NULL as Tail in list");
}
this.terms[INDEX_TAIL] = newTail;
}
@Override
public void fillVarables(final Map<String, Var> table) {
if (isNullList()) {
return;
}
terms[INDEX_HEAD].fillVarables(table);
terms[INDEX_TAIL].fillVarables(table);
}
/**
* Check the list to be the NULL LIST
*
* @return true if it is the NULL LIST else false
*/
public boolean isNullList() {
return this == NULLLIST;
}
/**
* Append a term to the end of a list
*
* @param list the destination list, must not be null
* @param term the term to be added, must not be null
* @return new generated list for the added term as a TermList object
*/
public static TermList appendItem(final TermList list, final Term term) {
final TermList newList = new TermList(term);
if (!list.isNullList()) {
newList.setTail(list.getTail());
list.setTail(newList);
}
return newList;
}
@Override
public boolean checkVariables() {
if (isNullList()) {
return true;
}
Term lst = this;
while (lst != NULLLIST) {
if (lst.getTermType() == TYPE_LIST) {
final TermList tList = (TermList) lst;
if (!tList.getHead().checkVariables()) {
return false;
}
lst = tList.getTail();
}
else {
if (!lst.checkVariables()) {
return false;
}
}
}
return true;
}
@Override
public int getTermType() {
return TYPE_LIST;
}
@Override
public String toString() {
if (isNullList()) {
return "[]";
}
final StringBuilder builder = new StringBuilder("[");
boolean notfirst = false;
Term list = this;
while (list != NULLLIST) {
if (list.getTermType() == Term.TYPE_LIST) {
if (notfirst) {
builder.append(',');
}
final TermList asList = (TermList) list;
builder.append(asList.getHead().toString());
list = asList.getTail();
}
else {
if (notfirst) {
builder.append('|');
}
builder.append(list.toString());
break;
}
notfirst = true;
}
builder.append(']');
return builder.toString();
}
@Override
public String getSourceLikeRepresentation() {
if (isNullList()) {
return "[]";
}
StringBuilder builder = new StringBuilder("[");
boolean notfirst = false;
Term list = this;
while (list != NULLLIST) {
if (list.getTermType() == Term.TYPE_LIST) {
if (notfirst) {
builder.append(',');
}
final TermList asList = (TermList) list;
builder.append(elementToSourceString(asList.getHead()));
list = asList.getTail();
}
else {
if (notfirst) {
builder.append('|');
}
builder.append(elementToSourceString(list));
break;
}
notfirst = true;
}
builder.append(']');
return builder.toString();
}
/**
* Inside auxulary function to convert a term into ints source representation
*
* @param term term to be converted into its source repesentation, must not be
* null
* @return the source like representation of the term
*/
private static String elementToSourceString(final Term term) {
switch (term.getTermType()) {
case Term.TYPE_ATOM: {
return term.getSourceLikeRepresentation();
}
default:
return term.getSourceLikeRepresentation();
}
}
/**
* Replace the last element of the list
*
* @param newLastElement new the last element of the list, must not be null
*/
public final void replaceLastElement(final Term newLastElement) {
if (isNullList()) {
throw new ProlCriticalError("Attemption to change Null list");
}
TermList curList = this;
while (true) {
Term tail = curList.getTail();
if (tail == NULLLIST || tail.getTermType() != TYPE_LIST) {
curList.setTail(newLastElement);
break;
}
curList = (TermList) tail;
}
}
@Override
public String getSignature() {
return getSourceLikeRepresentation();
}
@Override
public String forWrite() {
if (isNullList()) {
return "[]";
}
StringBuilder builder = new StringBuilder("[");
boolean notfirst = false;
Term list = this;
while (list != NULLLIST) {
if (list.getTermType() == Term.TYPE_LIST) {
if (notfirst) {
builder.append(',');
}
final TermList asList = (TermList) list;
builder.append(asList.getHead().forWrite());
list = asList.getTail();
}
else {
if (notfirst) {
builder.append('|');
}
builder.append(list.forWrite());
break;
}
notfirst = true;
}
builder.append(']');
return builder.toString();
}
@Override
public boolean Equ(final Term atom) {
if (this == atom) {
return true;
}
switch (atom.getTermType()) {
case Term.TYPE_LIST: {
TermList thatList = (TermList) atom;
if (this.isNullList()) {
return thatList.isNullList();
}
else if (thatList.isNullList()) {
return this.isNullList();
}
return this.getHead().Equ(thatList.getHead()) && this.getTail().Equ(thatList.getTail());
}
case Term.TYPE_VAR: {
final Var var = (Var) atom;
final Term value = var.getValue();
if (value == null) {
return ((Var) atom).setValue(this);
}
else {
return Equ(value);
}
}
}
return false;
}
@Override
public boolean equWithoutSet(Term atom) {
if (this == atom) {
return true;
}
if (atom.getTermType() == TYPE_VAR) {
atom = ((Var) atom).getValue();
}
if (atom == null) {
return true;
}
if (atom.getTermType() == Term.TYPE_LIST) {
if (this == atom) {
return true;
}
final TermList thisList = this;
final TermList thatList = (TermList) atom;
if (thisList.isNullList()) {
return thatList.isNullList();
}
else {
if (thatList.isNullList()) {
return false;
}
}
return thisList.getHead().equWithoutSet(thatList.getHead()) && thisList.getTail().equWithoutSet(thatList.getTail());
}
return false;
}
@Override
public int termComparsion(Term atom) {
if (this == atom) {
return 0;
}
if (atom.getTermType() == Term.TYPE_VAR && !((Var) atom).isUndefined()) {
atom = ((Var) atom).getValue();
}
switch (atom.getTermType()) {
case Term.TYPE_LIST: {
final TermList thatList = (TermList) atom;
if (isNullList() && thatList.isNullList()) {
return 0;
}
final TermList thisList = this;
if (thisList.isNullList() && !thatList.isNullList()) {
return -1;
}
if (!thisList.isNullList() && thatList.isNullList()) {
return 1;
}
final Term thisHead = thisList.getHead();
final Term thatHead = thatList.getHead();
int result = thisHead.termComparsion(thatHead);
if (result != 0) {
return result;
}
return thisList.getTail().termComparsion(thatList.getTail());
}
default:
return 1;
}
}
@Override
public boolean hasAnyDifference(final Term atom) {
if (atom.getTermType() != Term.TYPE_LIST) {
return true;
}
TermList thisList = this;
TermList thatList = (TermList) atom;
if (thisList == NULLLIST) {
return thisList != thatList;
}
else if (thatList == NULLLIST) {
return true;
}
if (thisList.getHead().hasAnyDifference(thatList.getHead())) {
return true;
}
return thisList.getTail().hasAnyDifference(thatList.getTail());
}
@Override
public boolean hasVariableWithName(final String name) {
if (this == NULLLIST) {
return false;
}
return getHead().hasVariableWithName(name) || getTail().hasVariableWithName(name);
}
}