/* * 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; } }