/*
* 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.
*/
/*
* Copyright (C) 2014 Igor Maznitsa (http://www.igormaznitsa.com)
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package com.igormaznitsa.prol.data;
import com.igormaznitsa.prol.exceptions.ProlCriticalError;
import com.igormaznitsa.prol.utils.Utils;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* It is the main core class for all objects in the engine which represent
* terms. The object can contain a text string which is its unique content.
*
* @author Igor Maznitsa (igor.maznitsa@igormaznitsa.com)
*/
public class Term {
/**
* The text representation of the term
*/
private final String termText;
/**
* The constant represents term as an atom (it can be numeric or text)
*/
public static final int TYPE_ATOM = 0;
/**
* The constant represents term as a structure
*/
public static final int TYPE_STRUCT = 1;
/**
* The constant represents term as a variable
*/
public static final int TYPE_VAR = 2;
/**
* The constant represents term as an operator
*/
public static final int TYPE_OPERATOR = 3;
/**
* The constant represents term as an operator container (it used only in very
* seldom special cases by the engine)
*/
public static final int TYPE_OPERATORS = 4;
/**
* The constant represents term as a list
*/
public static final int TYPE_LIST = 5;
/**
* The constant represents the 'true' term
*/
public static final Term TERM_TRUE = new Term("true");
/**
* The variable contains a carried object
*/
protected Serializable carriedObject;
/**
* Get the carried object for the term
*
* @return the carried object, it can be null
*/
public synchronized Serializable getCarriedObject() {
return carriedObject;
}
/**
* Set the carried object to the term
*
* @param object the carried object
*/
public synchronized void setCarriedObject(Serializable object) {
carriedObject = object;
}
/**
* The constructor
*
* @param termText the text which will be associated with the term, must not
* be null
*/
public Term(final String termText) {
this.termText = termText;
}
/**
* To get the priority of the term
*
* @return the priority of the term as an integer value
*/
public int getPriority() {
return 0;
}
/**
* To get the text associated with the term
*
* @return the text associated with the term as String object, in very very
* seldom special cases can be null
*/
public String getText() {
return termText;
}
/**
* Get the term type
*
* @return the term type as an integer value
*/
public int getTermType() {
return TYPE_ATOM;
}
/**
* To check the equivalency without set of variables, just to check
*
* @param term the term to be compared, msut not be null
* @return true if the compared term is equaivalent else false
*/
public boolean equWithoutSet(Term term) {
if (this == term) {
return true;
}
if (term.getTermType() == Term.TYPE_VAR) {
term = ((Var) term).getValue();
}
if (term == null) {
return true;
}
if (term.getTermType() == Term.TYPE_ATOM) {
return getText().equals(term.getText());
}
return false;
}
/**
* Chaeck that all variables at the term are instantiated
*
* @return true if all variables at the term have been instantiated
*/
public boolean checkVariables() {
return true;
}
/**
* Put all variables from the term into the table
*
* @param table the table to save variables from the term, must not be null
*/
public void fillVarables(final Map<String, Var> table) {
// the function should be empty because there is not any variable
}
/**
* To encode the term context into source like form
*
* @return source like form of the term as String, must not be null
*/
public String getSourceLikeRepresentation() {
return '\'' + Utils.encodeTextSourceLike(getText()) + '\'';
}
@Override
public String toString() {
return getSourceLikeRepresentation();
}
@Override
public int hashCode() {
if (termText == null) {
return super.hashCode();
}
else {
return termText.hashCode();
}
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
final Class<?> objclass = obj.getClass();
if (objclass == TermInteger.class || obj.getClass() == Term.class) {
final Term other = (Term) obj;
if (hashCode() != other.hashCode()) {
return false;
}
return this.termText.equals(other.termText);
}
else {
return false;
}
}
/**
* Get signature of the term
*
* @return the signature of the term as String, must not be null
*/
public String getSignature() {
return getText();
}
/**
* Get the text representation of the term for write/1 predicate
*
* @return the string for of the term which can be used for write/1 predicate,
* must not return null
*/
public String forWrite() {
return getText();
}
/**
* Check that the term is equal to an other term. Variables in the terms will
* be changed in the operation.
*
* @param otherTerm the other term to be compared, must not be null
* @return true if both term are equal, else false (but terms can be changed
* after the operation)
*/
public boolean Equ(final Term otherTerm) {
if (this == otherTerm) {
return true;
}
boolean result = false;
switch (otherTerm.getTermType()) {
case Term.TYPE_ATOM: {
result = getText().equals(otherTerm.getText());
}
break;
case Term.TYPE_STRUCT: {
final TermStruct struct = (TermStruct) otherTerm;
if (struct.getArity() == 0) {
result = struct.getFunctor().getText().equals(termText);
}
}
break;
case Term.TYPE_VAR: {
final Var var = (Var) otherTerm;
final Term value = var.getValue();
if (value == null) {
result = ((Var) otherTerm).setValue(this);
}
else {
result = Equ(value);
}
}
break;
}
return result;
}
/**
* Encode the text representation of the term into TermList which contains
* chars of the text separately
*
* @return the generated char list as TermList object, must not be null
*/
public TermList asCharList() {
final String text = getText();
final int len = text.length();
if (len == 0) {
return TermList.NULLLIST;
}
final StringBuilder buff = new StringBuilder(1);
TermList resultList = null;
TermList curList = null;
for (int li = 0; li < len; li++) {
buff.append(text.charAt(li));
final Term newAtom = new Term(buff.toString());
buff.setLength(0);
if (li == 0) {
resultList = new TermList(newAtom);
curList = resultList;
}
else {
curList = TermList.appendItem(curList, newAtom);
}
}
return resultList;
}
/**
* Encode the text representation of the term into TermList which contains
* char codes of the text separately
*
* @return the generated char code list as TermList object, must not be null
*/
public TermList asCharCodeList() {
if (termText == null) {
return TermList.NULLLIST;
}
final int len = termText.length();
if (len == 0) {
return TermList.NULLLIST;
}
TermList resultList = null;
TermList curList = null;
for (int li = 0; li < len; li++) {
final Term newAtom = new TermInteger(termText.charAt(li));
if (li == 0) {
resultList = new TermList(newAtom);
curList = resultList;
}
else {
curList = TermList.appendItem(curList, newAtom);
}
}
return resultList;
}
/**
* To compare two terms, it is like prolog predicate '==' or '=\='
*
* @return 0 if both term are equal, -1 if compared term has bigger order and
* 1 if compared term has lower order
*/
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_ATOM: {
if (atom instanceof NumericTerm) {
return 1;
}
return termText.compareTo(atom.termText);
}
case Term.TYPE_VAR: {
return 1;
}
case Term.TYPE_OPERATOR: {
return termText.compareTo(atom.termText);
}
default:
return -1;
}
}
/**
* To find any difference with other term
*
* @return true if compared term is not equal or false if equal
*/
public boolean hasAnyDifference(final Term comparedTerm) {
if (comparedTerm.getTermType() != Term.TYPE_ATOM || comparedTerm instanceof NumericTerm) {
return true;
}
return !termText.equals(comparedTerm.termText);
}
/**
* Get the length of the text associated with the term
*
* @return the text length as integer
*/
public int getTextLength() {
int result = 0;
if (termText != null) {
result = termText.length();
}
return result;
}
/**
* Check whether it has such named variable
*
* @param name the variable name, must not be null
* @return true if the term contains such named variable or false if it hasn't
*/
public boolean hasVariableWithName(final String name) {
return false;
}
/**
* One from most important functions. The function clones a term
*
* @return cloned term and its content (not null)
*/
public Term makeClone() {
final Term termToBeClone = this;
Term result = null;
switch (termToBeClone.getTermType()) {
case Term.TYPE_ATOM:
case Term.TYPE_OPERATOR: {
result = termToBeClone;
}
break;
case Term.TYPE_STRUCT: {
if (((TermStruct) termToBeClone).getArity() == 0) {
result = termToBeClone;
}
}
break;
case Term.TYPE_LIST: {
if (termToBeClone == TermList.NULLLIST) {
result = termToBeClone;
}
else {
final Map<Integer, Var> varHashMap = new HashMap<Integer, Var>();
result = termToBeClone.makeClone(varHashMap);
}
}
break;
case Term.TYPE_VAR: {
final Var termAsVar = (Var) termToBeClone;
final Term value = ((Var) termToBeClone).getThisValue();
result = termAsVar.isAnonymous() ? new Var() : new Var(termAsVar.getText());
if (value != null) {
boolean makeClone = true;
switch (value.getTermType()) {
case Term.TYPE_ATOM:
case Term.TYPE_OPERATOR: {
((Var) result).setThisValue(value);
makeClone = false;
}
break;
case Term.TYPE_STRUCT: {
if (((TermStruct) value).getArity() == 0) {
((Var) result).setThisValue(value);
makeClone = false;
}
}
break;
case Term.TYPE_LIST: {
if (((TermList) value).isNullList()) {
((Var) result).setThisValue(TermList.NULLLIST);
makeClone = false;
}
}
break;
}
if (makeClone) {
final Map<Integer, Var> varHashMap = new HashMap<Integer, Var>();
varHashMap.put(((Var) termToBeClone).getVarUID(), (Var) result);
((Var) result).setThisValue(value.makeClone(varHashMap));
}
}
}
break;
default:
throw new ProlCriticalError("Attempt to clone a system non-clonable term");
}
if (result == null) {
final Map<Integer, Var> varHashMap = new HashMap<Integer, Var>();
result = termToBeClone.makeClone(varHashMap);
}
return result;
}
/**
* The private inside function to make instance of a term, it is called from
* the public makeInstance function
*
* @param variableSet the table is used as the variable storage during the
* operation
* @return the cloned term (not null)
*/
private Term makeClone(final Map<Integer, Var> variableSet) {
final Term term = this;
Term result = null;
switch (term.getTermType()) {
case Term.TYPE_OPERATOR:
case Term.TYPE_ATOM: {
result = term;
}
break;
case Term.TYPE_LIST: {
final TermList source = (TermList) term;
if (source == TermList.NULLLIST) {
result = source;
}
else {
final Term head = source.getHead().makeClone(variableSet);
final Term tail = source.getTail().makeClone(variableSet);
result = new TermList(head, tail);
}
}
break;
case Term.TYPE_VAR: {
final Var var = (Var) term;
final Term val = var.getThisValue();
if (val == null) {
final String varName = var.getText();
final int varId = var.getVarUID();
Var newVar = variableSet.get(varId);
if (newVar == null) {
newVar = var.isAnonymous() ? new Var() : new Var(varName);
variableSet.put(varId, newVar);
final Term thisVal = var.getThisValue();
if (thisVal != null) {
newVar.setThisValue(thisVal.makeClone(variableSet));
}
}
result = newVar;
}
else {
result = val.makeClone(variableSet);
}
}
break;
case Term.TYPE_STRUCT: {
final TermStruct source = (TermStruct) term;
if (source.getArity() == 0) {
result = term;
}
else {
final Term[] elements = source.getElementsAsArray();
final int arity = elements.length;
final Term[] destElements = new Term[arity];
for (int li = 0; li < arity; li++) {
final Term element = elements[li];
destElements[li] = element.makeClone(variableSet);
}
result = new TermStruct(source.getFunctor(), destElements, source.getPredicateProcessor());
}
}
break;
default:
throw new ProlCriticalError("Attemption to clone a system non-clonable term");
}
result.carriedObject = this.carriedObject;
return result;
}
}