package org.vertexium.cypher.executor.models.match; import org.vertexium.cypher.ast.model.CypherElementPattern; import org.vertexium.cypher.ast.model.CypherRangeLiteral; import org.vertexium.cypher.ast.model.CypherRelationshipPattern; import java.util.ArrayList; import java.util.List; import java.util.UUID; import static com.google.common.base.Preconditions.checkNotNull; public abstract class MatchConstraint<TCypherElementPattern extends CypherElementPattern, TConnected extends MatchConstraint> { private final String name; private final String hashName; private final List<TConnected> connectedConstraints = new ArrayList<>(); private final List<TCypherElementPattern> patterns; private boolean optional; protected MatchConstraint(String name, List<TCypherElementPattern> patterns, boolean optional) { this.name = name; if (name == null) { hashName = UUID.randomUUID().toString(); } else { hashName = name; } this.patterns = patterns; this.optional = optional; } public String getName() { return name; } public boolean isOptional() { return optional; } public void setOptional(boolean optional) { this.optional = optional; } public List<TConnected> getConnectedConstraints() { return connectedConstraints; } public void addConnectedConstraint(TConnected constraint) { checkNotNull(constraint, "connected constraint cannot be null"); this.connectedConstraints.add(constraint); } public List<TCypherElementPattern> getPatterns() { return patterns; } @SuppressWarnings("unchecked") public static <TCypherElementPattern extends CypherElementPattern, TConnected extends MatchConstraint> void merge( MatchConstraint<TCypherElementPattern, TConnected> src, MatchConstraint<TCypherElementPattern, TConnected> dest ) { dest.connectedConstraints.addAll(src.getConnectedConstraints()); for (TConnected connectedConstraint : dest.connectedConstraints) { if (connectedConstraint.getConnectedConstraints().contains(src)) { connectedConstraint.getConnectedConstraints().remove(src); connectedConstraint.getConnectedConstraints().add(dest); } } dest.patterns.addAll(src.patterns); dest.setOptional(src.isOptional() && dest.isOptional()); } @Override public String toString() { return "MatchConstraint{" + "name='" + name + '\'' + ", hashName='" + hashName + '\'' + ", patterns=" + patterns + ", optional=" + optional + '}'; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MatchConstraint that = (MatchConstraint) o; if (!hashName.equals(that.hashName)) { return false; } return true; } @Override public int hashCode() { return hashName.hashCode(); } public boolean hasZeroRangePattern() { for (CypherElementPattern elementPattern : getPatterns()) { if (elementPattern instanceof CypherRelationshipPattern) { CypherRangeLiteral range = ((CypherRelationshipPattern) elementPattern).getRange(); if (range != null) { Integer from = range.getFrom(); if (from != null && from == 0) { return true; } } } } return false; } public int getConstraintCount() { return getPatterns().stream() .map(CypherElementPattern::getConstraintCount) .reduce(0, (i1, i2) -> i1 + i2); } }