/* * 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 java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** * The class contains the prolog var implementation, very important class for * engine work. A variable can contain another variable as a value and in the * case we will have a variable chain. There is a protection mechanism to avoid * the link to itself but a user can to set values directly and in the case it * should be careful. * * @author Igor Maznitsa (igor.maznitsa@igormaznitsa.com) * @see com.igormaznitsa.prol.data.Term */ public final class Var extends Term { /** * The counter is used to get unique id for new created variable, it is very * important */ private static final AtomicInteger VAR_COUNTER_ANONYM = new AtomicInteger(0); /** * The counter is used to generate UID for every new generated Variable */ private static final AtomicInteger VAR_COUNTER_UID = new AtomicInteger(0); /** * The value of the variable, if the variable is not instantiated, it contains * null or other variable */ private Term value; /** * The flag shows that the variable is an anonymous variable and its name has * been automatic generated */ private boolean anonymous; /** * The variable contains generated UIDof the variable in the creation time, * there must not be any other variable with such UID */ private final int variableUID; /** * A constructor allows to create a variable with predefined name, it will be * not an anonymous variable * * @param name the name of the created variable, must not be null */ public Var(final String name) { super(name); variableUID = VAR_COUNTER_UID.incrementAndGet(); } /** * A constructor to create an anonymous variable */ public Var() { this("_$" + Long.toHexString(VAR_COUNTER_ANONYM.incrementAndGet())); anonymous = true; } /** * Get the variable UID * * @return the variable UID as integer */ public final int getVarUID() { return variableUID; } @Override public int getTermType() { return TYPE_VAR; } /** * Get the value of the variable, if the variable has an other variable as its * value, the function will find walue recursively * * @return the value of the variable if it is instantiated and null if it is * not instantiated */ public final Term getValue() { Term result = value; if (result != null && result.getTermType() == TYPE_VAR) { result = ((Var) result).getValue(); } return result; } /** * Set the value for the variable, if the value is a variable then the term * will be tried to be set to the child variable. * * @param value the value for the variable as a Term object * @return true if the variable can be instantiated by the value or false if * it can't */ public final boolean setValue(final Term value) { boolean result = true; if (value != this) { if (value.getTermType() == Term.TYPE_VAR) { // check for loop Var curVar = ((Var) value); while (true) { if (curVar == this) { // loop detected, just return return true; } else { final Term nextval = curVar.getThisValue(); if (nextval != null && nextval.getTermType() == Term.TYPE_VAR) { curVar = (Var) nextval; } else { break; } } } } if (this.value == null) { this.value = value; } else { final Term curValue = getValue(); if (curValue == null) { ((Var) this.value).setValue(value); } else { result = curValue.Equ(value); } } } return result; } @Override public void fillVarables(final Map<String, Var> table) { table.put(getText(), this); } public boolean isAnonymous() { return anonymous; } @Override public String getSourceLikeRepresentation() { String result = "_"; if (!isAnonymous()) { result = getText(); } return result; } /** * Check that the variable is instantiated * * @return true if the variable instantiated else false */ public final boolean isUndefined() { boolean result = false; if (value == null) { result = true; } else { if (value.getTermType() == TYPE_VAR) { result = ((Var) value).isUndefined(); } } return result; } @Override public boolean checkVariables() { if (isAnonymous()) { return true; } return !isUndefined(); } @Override public String toString() { final StringBuilder builder = new StringBuilder(); final Term val = getValue(); if (val == null) { builder.append(isAnonymous() ? '_' : getText());//.append("{uid=").append(variableUID).append('}'); } else { builder.append(val.toString()); } return builder.toString(); } /** * Get the value of the variable without any recursion into child variables if * they are presented. * * @return the term which is the direct value of the variable, without any * inside recursion. */ public Term getThisValue() { return value; } public Var getDeepestVar() { Var curVar = this; while (true) { final Term term = curVar.getThisValue(); if (term == null || term.getTermType() != Term.TYPE_VAR) { return curVar; } else { curVar = (Var) term; } } } /** * Set the value of the variable without any inside recursion, just directly. * * @param value it is new direct value of the variable, it will be set to the * variable even it has another variable as a value already */ public void setThisValue(final Term value) { this.value = value; } /** * To change current value for the variable or the variable chain if it is * presented * * @param value the new value for the variable or the variable chain, can be * null (then the variable will be not instantiated) */ public void changeValue(final Term value) { Var deepestVar = getDeepestVar(); deepestVar.setThisValue(value); // if (this.value == null) { // this.value = value; // } else { // if (this.value.getTermType() == Term.TYPE_VAR) { // ((Var) this.value).changeValue(value); // } else { // this.value = value; // } // } } @Override public int hashCode() { return variableUID; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (this == obj) { return true; } if (obj.getClass() == Var.class) { final Var that = (Var) obj; return (variableUID == that.variableUID && that.getText().hashCode() == getText().hashCode()); } return false; } @Override public String getSignature() { return ".Var." + getText(); } @Override public String forWrite() { final Term val = getValue(); if (val == null) { if (isAnonymous()) { return "_"; } else { return getText(); } } else { return val.forWrite(); } } @Override public boolean Equ(final Term atom) { boolean result = true; if (this != atom) { final Term val = getValue(); if (val == null) { result = setValue(atom); } else { result = val.Equ(atom); } } return result; } @Override public boolean equWithoutSet(final Term atom) { boolean result = true; if (this != atom) { final Term val = getValue(); if (val != null) { result = val.equWithoutSet(atom); } } return result; } @Override public int termComparsion(Term atom) { if (this == atom) { return 0; } Term thisAtom = getValue(); if (thisAtom == null) { thisAtom = this; } if (atom.getTermType() == Term.TYPE_VAR && !((Var) atom).isUndefined()) { atom = ((Var) atom).getValue(); } int result = -1; if (thisAtom == this) { if (atom.getTermType() == TYPE_VAR) { result = getText().compareTo(atom.getText()); } } else { result = thisAtom.termComparsion(atom); } return result; } @Override public boolean hasAnyDifference(final Term atom) { if (atom.getTermType() != Term.TYPE_VAR) { return true; } final Var thatVar = (Var) atom; if (!getText().equals(thatVar.getText())) { return true; } final Term thisVal = getValue(); final Term thatVal = thatVar.getValue(); if (thisVal == null && thatVal == null) { return false; } if (thisVal != null && thatVal != null) { return thisVal.hasAnyDifference(thatVal); } return true; } @Override public boolean hasVariableWithName(final String name) { return getText().equals(name); } }