/*
* 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 com.igormaznitsa.prol.libraries.PredicateProcessor;
import com.igormaznitsa.prol.utils.Utils;
import java.util.Map;
/**
* The class describing the prolog structure
*
* @author Igor Maznitsa (igor.maznitsa@igormaznitsa.com)
*/
public class TermStruct extends Term {
/**
* It will be used to init zero-arity structures
*/
private static final Term[] EMPTY_ARRAY = new Term[0];
/**
* The array contains all items of the structure, can't be null, if it has
* zero arity then empty array will be saved into the variable
*/
protected final Term[] terms;
/**
* The variable contains the functor of the structure, must not be null
*/
protected final Term functor;
/**
* The variable contains the link to the processor which can be used to
* process data of the structure, can be null
*/
protected PredicateProcessor predicateProcessor;
/**
* The variable contains the signature of the structure in the format
* <functor>/<arity>
*/
private String structureSignature;
/**
* This flag shows that the rule definition functor is presented as the struct
* functor
*/
private boolean rulefunctor;
/**
* The flag shows that the rulefunctor flag has been inited, for lazy initing
*/
private boolean rulefunctorset;
/**
* A constructor allows to make structure with zero arity
*
* @param functor the functor for the structure, must not be null
*/
public TermStruct(final Term functor) {
this(functor, EMPTY_ARRAY);
}
/**
* A constructor allows to create structure with a functor and predeficned
* elements
*
* @param functor the string describes the functor, must not be null
* @param elements the array which contains elements of the structure, it can
* be null and in the case the structure will have zero arity
*/
public TermStruct(final String functor, final Term[] elements) {
this(new Term(functor), elements);
if (functor.length() != 2) {
rulefunctor = false;
rulefunctorset = true;
}
}
/**
* A constructor allows to create structure with a functor and predeficned
* elements
*
* @param functor the functor for the dreated structure, must not be null
* @param elements the array which contains elements of the structure, it can
* be null and in the case the structure will have zero arity
*/
public TermStruct(final Term functor, final Term[] elements) {
super(functor.getText());
final String functorText = functor.getText();
if (functorText.length() != 2) {
rulefunctor = false;
rulefunctorset = true;
}
this.functor = functor;
this.terms = elements == null ? EMPTY_ARRAY : elements;
}
/**
* Check that the functor is the term represended by ':-' text p,s. the
* function makes a lazy initing of inside flag
*
* @return true if the runle functor, else false
*/
public final boolean isFunctorLikeRuleDefinition() {
if (!rulefunctorset) {
rulefunctor = ":-".equals(functor.getText());
rulefunctorset = true;
}
return rulefunctor;
}
/**
* A constructor allows to create structure with a functor and predeficned
* elements and set the predicate processor
*
* @param functor the functor for the dreated structure, must not be null
* @param elements the array which contains elements of the structure, it can
* be null and in the case the structure will have zero arity
* @param processor the predicate processor which will be used to process
* elements of the structure, can be null
*/
public TermStruct(final Term functor, final Term[] elements, final PredicateProcessor processor) {
this(functor, elements);
predicateProcessor = processor;
}
/**
* Get the functor of the structure
*
* @return the functor as a Term object
*/
public final Term getFunctor() {
return functor;
}
/**
* Get the structure elements as a Term array
*
* @return the Term array contains structure elements, it is empty array if
* the structure has zero arity
*/
public final Term[] getElementsAsArray() {
return terms;
}
/**
* Set the structure element for its index (the first element index is zero)
*
* @param index the index of an element (the first element index is zero)
* @param element the element which will be placed in the position
*/
public final void setElement(final int index, final Term element) {
terms[index] = element;
}
@Override
public void fillVarables(final Map<String, Var> table) {
final Term[] arr = terms;
final int len = arr.length;
for (int li = 0; li < len; li++) {
arr[li].fillVarables(table);
}
}
/**
* Check that the structure doesn't have null as one from its elements
*
* @return true if there is not any null element, else false
*/
public final boolean hasNullElement() {
final Term[] arr = terms;
final int len = arr.length;
for (int li = 0; li < len; li++) {
if (arr[li] == null) {
return false;
}
}
return true;
}
/**
* Get the element for its index (the first element has the zero index)
*
* @param index the element index (the first element has the zero index)
* @return the element has been found at the position
*/
public final Term getElement(final int index) {
return terms[index];
}
/**
* Get the arity of the structure
*
* @return the arity of the structure as integer
*/
public final int getArity() {
return terms.length;
}
@Override
public int getPriority() {
if (functor.getTermType() == TYPE_OPERATOR) {
return functor.getPriority();
}
else {
return 0;
}
}
@Override
public String toString() {
return getStringRepresentation(false);
}
@Override
public String getSourceLikeRepresentation() {
return getStringRepresentation(true);
}
/**
* Inside auxulary function to make source like representation of the
* structure
*
* @param sourceLike if true then the result will be optimized for output as
* prolog sources else for write
* @return the representation of the structure as String object, must not be
* null
*/
private String getStringRepresentation(final boolean sourceLike) {
if (functor.getTermType() != Term.TYPE_OPERATOR) {
// just struct
final StringBuilder buffer = new StringBuilder(Utils.encodeTextSourceLike(getText()));
if (getArity() != 0) {
buffer.append('(');
for (int li = 0; li < getArity(); li++) {
if (li > 0) {
buffer.append(',');
}
buffer.append(sourceLike ? getElement(li).getSourceLikeRepresentation() : getElement(li).toString());
}
buffer.append(')');
}
return buffer.toString();
}
else {
// it's an operator
final String opName = sourceLike ? functor.getSourceLikeRepresentation() : functor.toString();
final StringBuilder builder = new StringBuilder();
final Operator OperatorFunctor = (Operator) functor;
final int priority = OperatorFunctor.getPriority();
switch (OperatorFunctor.getOperatorType()) {
case Operator.OPTYPE_FX: {
builder.append(opName);
builder.append(' ');
final String text = sourceLike ? getElement(0).getSourceLikeRepresentation() : getElement(0).toString();
if (getElement(0).getPriority() >= priority) {
builder.append('(').append(text).append(')');
}
else {
builder.append(text);
}
}
break;
case Operator.OPTYPE_FY: {
builder.append(opName);
builder.append(' ');
final String text = sourceLike ? getElement(0).getSourceLikeRepresentation() : getElement(0).toString();
if (getElement(0).getPriority() > priority) {
builder.append('(').append(text).append(')');
}
else {
builder.append(text);
}
}
break;
case Operator.OPTYPE_XF: {
final String text = sourceLike ? getElement(0).getSourceLikeRepresentation() : getElement(0).toString();
if (getElement(0).getPriority() >= priority) {
builder.append('(').append(text).append(')');
}
else {
builder.append(text);
}
builder.append(' ');
builder.append(opName);
}
break;
case Operator.OPTYPE_YF: {
final String text = sourceLike ? getElement(0).getSourceLikeRepresentation() : getElement(0).toString();
if (getElement(0).getPriority() > priority) {
builder.append('(').append(text).append(')');
}
else {
builder.append(text);
}
builder.append(' ');
builder.append(opName);
}
break;
case Operator.OPTYPE_XFX: {
final String text = sourceLike ? getElement(0).getSourceLikeRepresentation() : getElement(0).toString();
final String text2 = sourceLike ? getElement(1).getSourceLikeRepresentation() : getElement(1).toString();
if (getElement(0).getPriority() >= priority) {
builder.append('(').append(text).append(')');
}
else {
builder.append(text);
}
builder.append(' ');
builder.append(opName);
builder.append(' ');
if (getElement(1).getPriority() >= priority) {
builder.append('(').append(text2).append(')');
}
else {
builder.append(text2);
}
}
break;
case Operator.OPTYPE_YFX: {
final String text = sourceLike ? getElement(0).getSourceLikeRepresentation() : getElement(0).toString();
final String text2 = sourceLike ? getElement(1).getSourceLikeRepresentation() : getElement(1).toString();
if (getElement(0).getPriority() > priority) {
builder.append('(').append(text).append(')');
}
else {
builder.append(text);
}
builder.append(' ');
builder.append(opName);
builder.append(' ');
if (getElement(1).getPriority() >= priority) {
builder.append('(').append(text2).append(')');
}
else {
builder.append(text2);
}
}
break;
case Operator.OPTYPE_XFY: {
final String text = sourceLike ? getElement(0).getSourceLikeRepresentation() : getElement(0).toString();
final String text2 = sourceLike ? getElement(1).getSourceLikeRepresentation() : getElement(1).toString();
if (getElement(0).getPriority() >= priority) {
builder.append('(').append(text).append(')');
}
else {
builder.append(text);
}
builder.append(' ');
builder.append(opName);
builder.append(' ');
if (getElement(1).getPriority() > priority) {
builder.append('(').append(text2).append(')');
}
else {
builder.append(text2);
}
}
break;
default:
throw new ProlCriticalError("Unsupported type");
}
return builder.toString();
}
}
@Override
public String forWrite() {
if (functor.getTermType() != Term.TYPE_OPERATOR) {
// just struct
final StringBuilder buffer = new StringBuilder(functor.forWrite());
if (getArity() > 0) {
buffer.append('(');
for (int li = 0; li < getArity(); li++) {
if (li > 0) {
buffer.append(',');
}
buffer.append(getElement(li).forWrite());
}
buffer.append(')');
}
return buffer.toString();
}
else {
// it's an operator
final String opName = functor.forWrite();
final StringBuilder builder = new StringBuilder();
final Operator OperatorFunctor = (Operator) functor;
switch (OperatorFunctor.getOperatorType()) {
case Operator.OPTYPE_FX:
case Operator.OPTYPE_FY: {
builder.append(opName);
builder.append(' ');
builder.append(getElement(0).forWrite());
}
break;
case Operator.OPTYPE_XF:
case Operator.OPTYPE_YF: {
builder.append(getElement(0).forWrite());
builder.append(' ');
builder.append(opName);
}
break;
case Operator.OPTYPE_XFX:
case Operator.OPTYPE_YFX:
case Operator.OPTYPE_XFY: {
builder.append(getElement(0).forWrite());
builder.append(' ');
builder.append(opName);
builder.append(' ');
builder.append(getElement(1).forWrite());
}
break;
default:
throw new ProlCriticalError("Unsupported type");
}
return builder.toString();
}
}
/**
* Get the predicate processor for the structure
*
* @return the predicate processor for the structure or null if it is not
* defined
*/
public final PredicateProcessor getPredicateProcessor() {
return predicateProcessor;
}
/**
* Set the predicate processor for the structure
*
* @param processor the processor which will be called if we need to process
* the structure data (as an example it the functor is an Operator), it can be
* null
*/
public void setPredicateProcessor(final PredicateProcessor processor) {
predicateProcessor = processor;
}
@Override
public boolean checkVariables() {
final int arity = terms.length;
for (int li = 0; li < arity; li++) {
if (!terms[li].checkVariables()) {
return false;
}
}
return true;
}
@Override
public int getTermType() {
return TYPE_STRUCT;
}
@Override
public String getSignature() {
if (structureSignature == null) {
structureSignature = new StringBuilder(functor.getText()).append('/').append(getArity()).toString();
}
return structureSignature;
}
@Override
public boolean Equ(final Term atom) {
if (this == atom) {
return true;
}
switch (atom.getTermType()) {
case Term.TYPE_STRUCT: {
final TermStruct thisStruct = this;
final TermStruct thatStruct = (TermStruct) atom;
final int arity = thisStruct.getArity();
if (arity == thatStruct.getArity() && thisStruct.getFunctor().Equ(thatStruct.getFunctor())) {
for (int li = 0; li < arity; li++) {
final Term thiselement = thisStruct.getElement(li);
final Term thatelement = thatStruct.getElement(li);
if (thiselement != thatelement && !thiselement.Equ(thatelement)) {
return false;
}
}
return true;
}
}
break;
case Term.TYPE_VAR: {
final Var var = (Var) atom;
final Term value = var.getValue();
if (value == null) {
return var.setValue(this);
}
else {
if (value == this) {
return true;
}
return value.Equ(this);
}
}
}
return false;
}
@Override
public boolean equWithoutSet(Term atom) {
if (this == atom) {
return true;
}
if (atom.getTermType() == Term.TYPE_VAR) {
atom = ((Var) atom).getValue();
}
if (atom == null) {
return true;
}
else {
if (atom == this) {
return true;
}
}
if (atom.getTermType() == Term.TYPE_STRUCT) {
final TermStruct thisStruct = this;
final TermStruct thatStruct = (TermStruct) atom;
final int arity = thisStruct.getArity();
if (arity == thatStruct.getArity() && thisStruct.getFunctor().equWithoutSet(thatStruct.getFunctor())) {
for (int li = 0; li < arity; li++) {
if (!thisStruct.getElement(li).equWithoutSet(thatStruct.getElement(li))) {
return false;
}
}
return true;
}
}
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:
return -1;
case Term.TYPE_STRUCT: {
final TermStruct thatStruct = (TermStruct) atom;
final int thisArity = getArity();
final int thatArity = getArity();
if (thisArity == thatArity) {
int result = getFunctor().termComparsion(thatStruct.getFunctor());
if (result == 0) {
for (int li = 0; li < thisArity; li++) {
final Term thisAtom = getElement(li);
final Term thatAtom = thatStruct.getElement(li);
result = thisAtom.termComparsion(thatAtom);
if (result != 0) {
return result;
}
}
return 0;
}
else {
return result;
}
}
else {
if (thisArity < thatArity) {
return -1;
}
else {
return 1;
}
}
}
default:
return 1;
}
}
@Override
public boolean hasAnyDifference(final Term atom) {
if (atom.getTermType() != Term.TYPE_STRUCT) {
return true;
}
final TermStruct thatStruct = (TermStruct) atom;
if (functor.hasAnyDifference(thatStruct.functor)) {
return true;
}
final int thisarity = getArity();
final int thatarity = thatStruct.getArity();
if (thatarity == thisarity) {
for (int li = 0; li < thisarity; li++) {
if (terms[li].hasAnyDifference(thatStruct.terms[li])) {
return true;
}
}
}
else {
return true;
}
return false;
}
@Override
public boolean hasVariableWithName(final String name) {
final int arity = getArity();
for (int li = 0; li < arity; li++) {
if (getElement(li).hasVariableWithName(name)) {
return true;
}
}
return false;
}
@Override
public boolean equals(final Object obj) {
return this == obj;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
}