package models.terms.meta;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import exceptions.SubstituteFailedException;
import exceptions.SyntaxException;
import models.algebra.Symbol;
import models.algebra.Variable;
import models.terms.Dependency;
import models.terms.EvaluatableTerm;
import models.terms.RDLTerm;
import utils.Permutation;
public class MetaDependency extends MetaRDLTerm{
protected RDLTerm dependingTerm;
protected List<? extends RDLTerm> dependedTerms;
protected MetaDependency() {
super(new Symbol(":", -1), TermType.META_DEPENDENCY, -1);
}
public MetaDependency(RDLTerm dependingTerm, List<? extends RDLTerm> dependedTerms) {
super(new Symbol(":", -1), TermType.META_DEPENDENCY, -1);
int size = dependingTerm.getSize();
addChild(dependingTerm);
for(RDLTerm term : dependedTerms.stream().sorted().toList()) {
addChild(term);
size += term.getSize();
}
this.size = size;
this.dependingTerm = dependingTerm;
this.dependedTerms = dependedTerms;
}
public MetaDependency(RDLTerm dependingTerm, RDLTerm ...dependedTerms) {
this(dependingTerm, Arrays.asList(dependedTerms));
}
@Override
protected Set<MatchConstraint> isMatchedBy(RDLTerm another, MatchConstraint constraint, int depth) {
Set<MatchConstraint> result = new HashSet<>();
if (! another.getClass().isAssignableFrom(this.termType.getBaseTermClass())) {
return result;
}
if (isDependencyTerm() && (! islinearRightNormalizedMatchedBy(another))) {
return result;
}
result = dependingTermMatch(another, constraint, depth);
return dependedTermsMatch(another, result, depth);
}
private Set<MatchConstraint> dependingTermMatch(RDLTerm another, MatchConstraint constraint, int depth) {
RDLTerm dependingChild = (RDLTerm) this.getChild(0);
RDLTerm anotherDependingChild = (RDLTerm) another.getChild(0);
if (dependingChild instanceof MetaRDLTerm) {
MetaRDLTerm metaChild = (MetaRDLTerm) dependingChild;
return metaChild.isMatchedBy(anotherDependingChild, constraint, depth + 1);
} else {
if (!(dependingChild.equals(anotherDependingChild))) {
return new HashSet<>();
}
}
return new HashSet<>();
}
private Set<MatchConstraint> dependedTermsMatch(RDLTerm another, Set<MatchConstraint> constraint, int depth) {
Set<MatchConstraint> result = new HashSet<>();
for (List<Integer> perm : Permutation.permutation(another.getChildren().size() - 1)) {
Set<MatchConstraint> localResult = new HashSet<>(constraint);
boolean flg = true;
for (int metaTermIndex = 0; metaTermIndex < perm.size(); metaTermIndex++) {
int anotherTermIndex = perm.get(metaTermIndex);
RDLTerm metaDependedTerm = (RDLTerm) getChild(metaTermIndex + 1);
RDLTerm anotherDependedTerm = (RDLTerm) another.getChild(anotherTermIndex + 1);
if (metaDependedTerm instanceof MetaRDLTerm metaTerm) {
localResult = metaTerm.isMatchedBy(anotherDependedTerm, localResult, depth + 1);
if (localResult.isEmpty()) {
flg = false;
break;
}
} else {
if (!(metaDependedTerm.equals(anotherDependedTerm))) {
flg = false;
break;
}
}
}
if (flg) {
result.addAll(localResult);
}
}
return result;
}
@Override
protected RDLTerm substitute(Map<Variable, RDLTerm> binding, int depth) {
RDLTerm dependingTerm = (RDLTerm) getChild(0);
if (dependingTerm instanceof MetaRDLTerm) {
dependingTerm = ((MetaRDLTerm) dependingTerm).substitute(binding, depth + 1);
}
List<EvaluatableTerm> dependedTerms = new ArrayList<>();
for (int i = 1; i < getChildren().size(); i++) {
RDLTerm dependedTerm = (RDLTerm) getChild(i);
if (dependedTerm instanceof MetaRDLTerm metaTerm) {
dependedTerm = metaTerm.substitute(binding, depth + 1);
}
if (dependedTerm instanceof EvaluatableTerm te) {
dependedTerms.add(te);
} else {
throw new SubstituteFailedException("Syntax error");
}
}
try {
return new Dependency(dependingTerm, dependedTerms);
} catch (SyntaxException e) {
throw new SubstituteFailedException(e.getMessage());
}
}
}