package org.aksw.sparqlify.restriction.experiment; import java.util.HashSet; import java.util.Set; import org.aksw.sparqlify.restriction.Polarity; import org.aksw.sparqlify.restriction.Prefix; /** * A class which denotes a set of strings with common prefix. * Exceptions may pertain to the set, with following rules: * - The exception must be part of the set * - The polarity of the exception is opposite of that of the base * * * * * @author raven * */ public class PrefixSet { private Polarity polarity; private Prefix value; private Set<PrefixSet> exceptions; // OPTIMIZE We could use tries for speeding up prefix lookups // but we are not doing that many lookups anyway //private PatriciaTrie<String, String> exceptions; public PrefixSet(Prefix value) { this(value, Polarity.POSITIVE); } public PrefixSet(Prefix value, Polarity polarity) { this(value, polarity, new HashSet<PrefixSet>()); } public PrefixSet(Prefix value, Polarity polarity, Set<PrefixSet> exceptions) { this.value = value; this.polarity = polarity; this.exceptions = exceptions; } public Polarity getPolarity() { return polarity; } public Prefix getValue() { return value; } public Set<PrefixSet> getExceptions() { return exceptions; } public boolean isPrefixOf(String str) { boolean result; if(value.isConstant()) { result = str.equals(value.getPrefix()); } else { result = str.startsWith(value.getPrefix()); } return result; } public boolean contains(String str) { boolean result = contains(new Prefix(str)); return result; } public boolean contains(Prefix that) { boolean result = this.value.isPrefixOf(that); if(polarity.equals(Polarity.NEGATIVE)) { result = !result; } // Check if exceptions negate this for(PrefixSet exception : exceptions) { if(exception.contains(that)) { result = !result; break; } } return result; } public PrefixSet getExceptionFor(Prefix prefix) { for(PrefixSet ex : exceptions) { if(ex.value.isPrefixOf(prefix)) { return ex; } } return null; } public Set<PrefixSet> getSuffixExceptions(Prefix prefix) { Set<PrefixSet> result = new HashSet<PrefixSet>(); for(PrefixSet ex : exceptions) { if(prefix.isPrefixOf(ex.value)) { result.add(ex); } } return result; } /** * Adds an exception to the current set. * * Note that if {owla} is added, but {owl} is already an exception, * then the exception is subsumed (i.e. not added) * * On the other hand, if {owl} is added, but {owla, ..., owlz} exists, then * all their exceptions are added to owl * * * @param exception * @return */ public PrefixSet addException(Prefix exception) { boolean isValid = this.contains(exception); if(!isValid) { throw new RuntimeException("Cannot add exception: " + exception + ", current state: " + this); } PrefixSet subsumed = getExceptionFor(exception); if(subsumed == null) { subsumed = new PrefixSet(exception); exceptions.add(subsumed); } Set<PrefixSet> suffixes = getSuffixExceptions(exception); this.exceptions.removeAll(suffixes); for(PrefixSet suffix : suffixes) { subsumed.addException(suffix); } return subsumed; } /** * Adds an exception: * Example: * prefix = {owl} * addException(owla) * addException(owlab) * * @param exception * @return */ public void addException(PrefixSet that) { for(PrefixSet exs : that.exceptions) { Prefix p = exs.value; PrefixSet added = this.addException(p); for(PrefixSet exs2 : exs.exceptions) { added.addException(exs2); } } } /** * Returns the set of exceptions with the given prefix * @param prefix */ public Set<PrefixSet> getExceptions(String prefix) { Set<PrefixSet> result = new HashSet<PrefixSet>(); for(PrefixSet exception : exceptions) { if(exception.contains(prefix)) { result.add(exception); } } return result; } /* public static Set<PrefixSet2> intersect(Set<PrefixSet2> as, Set<PrefixSet2> bs) { for(PrefixSet2 a : as) { for(PrefixSet2 b : bs) { } } } */ public static Prefix intersect(Prefix a, Prefix b) { Prefix result; if(a.isConstant() && b.isConstant()) { if(a.getPrefix().equals(b.getPrefix())) { result = a; } else { result = null; } } else { String intersection = intersect(a.getPrefix(), b.getPrefix()); if(a.isConstant()) { result = a.getPrefix().equals(intersection) ? a : null; } else if(b.isConstant()) { result = b.getPrefix().equals(intersection) ? b : null; } else { result = new Prefix(intersection); } } return result; } public static String intersect(String a, String b) { String shorter; String longer; // Determine the shorter and loger prefix if(a.length() < b.length()) { shorter = a; longer = b; } else { shorter = b; longer = a; } String result; if(longer.startsWith(shorter)) { result = shorter; } else { result = null; } return result; } /** * How to deal with exceptions: * - This is not owl, except for owl:Class * - This is rdf, except for rdf:type * * * case: positive - positive * Create the intersection: * {owl} and {rdf} -> {} * {owl} and {owla} -> {owla} (owla is more specific) * * case: positive - negative * {owl} and not{owla, rdf} -> {owl} except {owla} (positive prevails) * {owl} and not{owla} -> {owl[except owla]} (exception)} * not{owla} and {owl} -> same as above * not{owl} and {owla} -> {} (owl namespace was excluded) * * case: negative - negative * Create the union: * not{owl} and not{rdf} -> not{rdf, owl} * not(owl} and not{owla} -> not{owl} (owla subsumed by the shorter prefix) * */ public PrefixSet intersects(PrefixSet that) { PrefixSet result; if(this.polarity.isPositive()) { if(that.polarity.isPositive()) { Prefix intersection = intersect(this.value, that.value); if(intersection == null) { Prefix p = new Prefix(""); result = new PrefixSet(p, Polarity.NEGATIVE); } else { result = new PrefixSet(intersection, Polarity.POSITIVE); } // Add the combined exceptions for(PrefixSet ps : this.exceptions) { result.addException(ps); } for(PrefixSet ps : that.exceptions) { result.addException(ps); } } } return that; } /** * Rule of thumb: Shorter prefixes subsume longer ones * {owl} union {owla} -> {owl} * * The exceptions are then also unioned: * for each exception it is checked, whether there is a shorter exception * * case: positive - positive * {owl} union {rdf} -> {} except {owl, rdf} * {owl} union {owla} -> {owl} * * case: positive - negative * {owl} union not{owla, rdf} -> {} except {owl, rdf} * {owl} union not{owla} -> {owl[except owla]} (exception)} * not{owla} and {owl} -> same as above * not{owl} and {owla} -> {} (owl namespace was excluded) * * case: negative - negative * Create the union: * not{owl} union not{rdf} -> not{rdf, owl} * not(owl} and not{owla} -> not{owl} (owla subsumed by the shorter prefix) * * * @param that * @return */ public PrefixSet union(PrefixSet that) { return that; } public PrefixSet negate() { Set<PrefixSet> newExceptions = new HashSet<PrefixSet>(); for(PrefixSet exception : exceptions) { PrefixSet newException = exception.negate(); newExceptions.add(newException); } Polarity newPolarity = polarity.equals(Polarity.POSITIVE) ? Polarity.POSITIVE : Polarity.NEGATIVE ; PrefixSet result = new PrefixSet(this.value, newPolarity, newExceptions); return result; } }