package org.aksw.jena_sparql_api.restriction; import java.util.EnumSet; import java.util.Iterator; import java.util.Set; import org.aksw.jena_sparql_api.views.PrefixSet; import org.aksw.jena_sparql_api.views.RdfTermType; import org.aksw.jena_sparql_api.views.TernaryLogic; import org.apache.jena.graph.Node; /** * This class represents restrictions to be used on variables. * * * Rules * . Stating a constant value (node) must be consistent with at least one prefix (if there are any), or equivalent to a previous value. * Additionally all prefixes are removed in that case. * * . If a restriction is inconsistent, retrieving fields is meaningless, as their values are not defined. * * * .) Methods return true if a change occurred * * . TODO More details * * * Further statements could be: * * statePattern() * stateRange(min, max) * stateDatatype() * * I really hope I am not ending up with my own Datalog+Constraints engine :/ * * * TODO: Maybe the set of uriPrefixes should be replaced with a single prefix - * so that an instance of restriction really only states a single restriction. * * So my problem is how to deal with dis/conjunctions of restrictions efficiently * * * * @author Claus Stadler <cstadler@informatik.uni-leipzig.de> * */ public class RestrictionImpl implements Restriction { private RdfTermType type = RdfTermType.UNKNOWN; private Node node; private PrefixSet uriPrefixes; // TODO Actually it should be: isInconsistent(); - We are not making consistency guaratees here //private boolean isConsistent = true; private Boolean satisfiability = Boolean.TRUE; /** * Return true if 'this' is equal to or less restrictive than other * * @param other * @return */ public boolean subsumesOrIsEqual(RestrictionImpl other) { if(type != other.type) { return false; } if(this.node != null) { if(other.node == null) { return false; } else { if(!this.node.equals(other.node)) { return false; } } } if(uriPrefixes != null) { // Check of on of the prefixes is a prefix of the constant if(other.node != null) { if(this.uriPrefixes.containsPrefixOf(other.node.toString())) { return true; } } if(other.getUriPrefixes() == null) { return false; } else { // Test whether each of this.prefixes is a prefix of other for(String prefix : other.getUriPrefixes().getSet()) { if(!this.uriPrefixes.containsPrefixOf(prefix)) { return false; } } } } return true; } public RestrictionImpl clone() { return new RestrictionImpl(this); } public RestrictionImpl() { satisfiability = Boolean.TRUE; } public RestrictionImpl(RdfTermType type) { this.stateType(type); } public RestrictionImpl(PrefixSet prefixSet) { this.stateUriPrefixes(prefixSet); } public RestrictionImpl(Node node) { this.stateNode(node); } public RestrictionImpl(RestrictionImpl other) { this.type = other.type; this.node = other.node; if(other.uriPrefixes != null) { this.uriPrefixes = new PrefixSet(other.uriPrefixes); } this.satisfiability = other.satisfiability; } public boolean hasConstant() { return isConsistent() && node != null; } public boolean hasPrefix() { return !hasConstant() && uriPrefixes != null; } /** * Get the rdf term type of this restriction. * Deprecated because this can be a set. * * @return */ @Deprecated public RdfTermType getType() { return type; } public EnumSet<RdfTermType> getRdfTermTypes() { EnumSet<RdfTermType> result = EnumSet.noneOf(RdfTermType.class); result.add(type); return result; } public Node getNode() { return node; } public PrefixSet getUriPrefixes() { return uriPrefixes; } /* (non-Javadoc) * @see org.aksw.sparqlify.restriction.IRestriction#stateRestriction(org.aksw.sparqlify.restriction.Restriction) */ @Override public boolean stateRestriction(Restriction that) { RestrictionImpl other = (RestrictionImpl)that; if(other.satisfiability == Boolean.TRUE) { return false; } satisfiability = TernaryLogic.and(this.satisfiability, other.satisfiability); //isConsistent = this.isConsistent != false && other.isConsistent != false; if(satisfiability == Boolean.FALSE) { return false; } else if(other.node != null) { return stateNode(other.node); } else if(other.uriPrefixes != null) { return stateUriPrefixes(other.uriPrefixes); } else if(other.getType() != RdfTermType.UNKNOWN) { return stateType(other.type); } throw new RuntimeException("Should not happen"); } /* (non-Javadoc) * @see org.aksw.sparqlify.restriction.IRestriction#stateType(org.aksw.sparqlify.restriction.Type) */ @Override public boolean stateType(RdfTermType newType) { if(satisfiability == Boolean.FALSE) { return false; } if(type == RdfTermType.UNKNOWN) { if(newType != RdfTermType.UNKNOWN) { type = newType; satisfiability = null; return true; } return false; } else { if(type.equals(newType)) { return false; } else { satisfiability = Boolean.FALSE; return true; } } } public static RdfTermType getNodeType(Node node) { if(node == null) { return RdfTermType.UNKNOWN; } else if(node.isURI()) { return RdfTermType.URI; } else if(node.isLiteral()) { return RdfTermType.LITERAL; } else if(node.isBlank()) { return RdfTermType.BLANK; } else { throw new RuntimeException("Should not happen"); } } /* (non-Javadoc) * @see org.aksw.sparqlify.restriction.IRestriction#stateNode(org.apache.jena.graph.Node) */ @Override public boolean stateNode(Node newNode) { boolean change = stateType(getNodeType(newNode)); if(satisfiability == Boolean.FALSE) { return change; } if(node == null) { if(uriPrefixes != null) { /* if(!node.isURI()) { satisfiability = Boolean.FALSE; return true; }*/ if(!uriPrefixes.containsPrefixOf(newNode.getURI())) { satisfiability = Boolean.FALSE; return true; } } node = newNode; satisfiability = null; return true; } else { if(!node.equals(newNode)) { satisfiability = Boolean.FALSE; return true; } return false; } } /* (non-Javadoc) * @see org.aksw.sparqlify.restriction.IRestriction#stateUriPrefixes(org.aksw.sparqlify.config.lang.PrefixSet) */ @Override public boolean stateUriPrefixes(PrefixSet prefixes) { if(prefixes.isEmpty()) { throw new RuntimeException("Should not happen"); } boolean change = stateType(RdfTermType.URI); if(satisfiability == Boolean.FALSE) { return change; } if(node != null) { if(!node.isURI() || !prefixes.containsPrefixOf(node.getURI())) { satisfiability = Boolean.FALSE; return true; } // We have a constant, no need to track the prefixes return false; } // If no prefixes have been stated yet, state them if(uriPrefixes == null) { uriPrefixes = new PrefixSet(); for(String s : prefixes.getSet()) { Set<String> ps = uriPrefixes.getPrefixesOf(s); uriPrefixes.removeAll(ps); uriPrefixes.add(s); } satisfiability = uriPrefixes.isEmpty() ? false : null; return true; } else if(prefixes.isEmpty()) { // If we get here, then we were not inconsistent yet // TODO Not sure if the satisfiability computation also works for TRUE if(uriPrefixes.isEmpty()) { satisfiability = Boolean.FALSE; return true; } else { return false; } } // {http:, mailto:addr} {http://foo, mailto:} // Note: If we have prefixes Foo and FooBar, we keep FooBar, which is more restrictive. for(String s : prefixes.getSet()) { Set<String> ps = uriPrefixes.getPrefixesOf(s, false); if(!ps.isEmpty()) { uriPrefixes.removeAll(ps); uriPrefixes.add(s); } } // Remove all entries that do not have a prefix in the other set Iterator<String> it = uriPrefixes.getSet().iterator(); while(it.hasNext()) { String s = it.next(); Set<String> ps = prefixes.getPrefixesOf(s); if(ps.isEmpty()) { it.remove(); } } if(uriPrefixes.isEmpty()) { satisfiability = Boolean.FALSE; return true; } // TODO Could sometimes return false return true; //return change; } // To be done. /* (non-Javadoc) * @see org.aksw.sparqlify.restriction.IRestriction#statePattern(com.karneim.util.collection.regex.PatternPro) */ // @Override // public void statePattern(PatternPro pattern) { // // If there is a pattern already, make it the intersection with the new pattern // // // If there is a node, check if it conforms to the pattern // // // If there are prefixes, check if they conform to the pattern // // throw new NotImplementedException(); // } /** * If the restriction is unconstrained, we return true * If it is inconsistent false, * and null otherwise * @return */ public Boolean getSatisfiability() { return satisfiability; } public boolean isConsistent() { return satisfiability != Boolean.FALSE; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((node == null) ? 0 : node.hashCode()); result = prime * result + ((satisfiability == null) ? 0 : satisfiability.hashCode()); result = prime * result + ((type == null) ? 0 : type.hashCode()); result = prime * result + ((uriPrefixes == null) ? 0 : uriPrefixes.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; RestrictionImpl other = (RestrictionImpl) obj; if (node == null) { if (other.node != null) return false; } else if (!node.equals(other.node)) return false; if (satisfiability == null) { if (other.satisfiability != null) return false; } else if (!satisfiability.equals(other.satisfiability)) return false; if (type != other.type) return false; if (uriPrefixes == null) { if (other.uriPrefixes != null) return false; } else if (!uriPrefixes.equals(other.uriPrefixes)) return false; return true; } @Override public String toString() { if(satisfiability != null) { return satisfiability.toString(); } else if(hasConstant()) { return "" + node; } else if(hasPrefix()){ return "" + uriPrefixes; } else if(type != RdfTermType.UNKNOWN) { return "" + type; } else { return "Invalid state"; } } /* (non-Javadoc) * @see org.aksw.sparqlify.restriction.IRestriction#isUnsatisfiable() */ @Override public boolean isUnsatisfiable() { return satisfiability == Boolean.FALSE; } }