/* * SubPropertyOfQueries.java * * Created on Jan 7, 2011, 1:46:43 PM * * Description: Provides subPropertyOf inference queries into the the knowledge base specified by a given RDF entity manager. * * Copyright (C) Jan 7, 2011, Stephen L. Reed. * * This program is free software; you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.texai.subsumptionReasoner; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import net.jcip.annotations.NotThreadSafe; import org.apache.log4j.Logger; import org.openrdf.OpenRDFException; import org.openrdf.model.URI; import org.openrdf.query.BindingSet; import org.openrdf.query.MalformedQueryException; import org.openrdf.query.QueryLanguage; import org.openrdf.query.TupleQuery; import org.openrdf.query.TupleQueryResult; import org.openrdf.repository.RepositoryConnection; import org.texai.kb.Constants; import org.texai.kb.persistence.RDFEntityManager; import org.texai.util.ArraySet; import org.texai.util.LRUMap; import org.texai.util.LRUSet; import org.texai.util.TexaiException; /** Provides subPropertyOf inference queries into the the knowledge base specified by a given RDF entity manager. * * @author reed */ @NotThreadSafe public class SubPropertyOfQueries { /** the logger */ private static final Logger LOGGER = Logger.getLogger(SubPropertyOfQueries.class); /** the query */ private static final String SUBCLASSOF_QUERY_STRING = "SELECT s, o FROM {s} <" + Constants.RDFS_NAMESPACE + "subPropertyOf> {o}"; /** the RDF entity manager */ private final RDFEntityManager rdfEntityManager; /** the isSubPropertyOf cache */ private final IsSubPropertyOfCache isSubPropertyOfCache = new IsSubPropertyOfCache(); /** the super property cache */ private final SuperPropertyCache superPropertyCache = new SuperPropertyCache(); /** Constructs a new SubPropertyOfQueries instance. * * @param rdfEntityManager the RDF entity manager */ public SubPropertyOfQueries(final RDFEntityManager rdfEntityManager) { //Preconditions assert rdfEntityManager != null : "rdfEntityManager must not be null"; this.rdfEntityManager = rdfEntityManager; } /** Returns whether the given term is directly a subproperty of of the given property (predicate) term. * * @param repositoryName the repository name * @param property the given property * @param superPropertyTerm the given super property * @return whether the given term is directly a subproperty of of the given property (predicate) term */ public boolean isDirectSubPropertyOf( final String repositoryName, final URI property, final URI superPropertyTerm) { //Preconditions assert repositoryName != null : "repositoryName must not be null"; assert !repositoryName.isEmpty() : "repositoryName must not be empty"; assert property != null : "property must not be null"; assert superPropertyTerm != null : "superPropertyTerm must not be null"; final RepositoryConnection repositoryConnection = rdfEntityManager.getConnectionToNamedRepository(repositoryName); boolean isDirectSubPropertyOf; try { final TupleQuery subPropertyOfTupleQuery = repositoryConnection.prepareTupleQuery( QueryLanguage.SERQL, SUBCLASSOF_QUERY_STRING); subPropertyOfTupleQuery.setBinding("s", property); subPropertyOfTupleQuery.setBinding("o", superPropertyTerm); final TupleQueryResult tupleQueryResult = subPropertyOfTupleQuery.evaluate(); isDirectSubPropertyOf = tupleQueryResult.hasNext(); tupleQueryResult.close(); } catch (final MalformedQueryException ex) { throw new TexaiException(ex); } catch (final OpenRDFException ex) { throw new TexaiException(ex); } return isDirectSubPropertyOf; } /** Returns whether the first property is directly or indirectly a subproperty of of the second property. * * @param repositoryName the repository name * @param property1 the first property term * @param property2 the second property term * @return whether the first property term is directly or indirectly a subproperty of of the second property term */ public boolean isSubPropertyOf( final String repositoryName, final URI property1, final URI property2) { //Preconditions assert repositoryName != null : "repositoryName must not be null"; assert !repositoryName.isEmpty() : "repositoryName must not be empty"; assert property1 != null : "term must not be null"; assert property2 != null : "propertyTerm must not be null"; final List<URI> key = new ArrayList<>(2); if (repositoryName.equals("OpenCyc")) { key.add(property1); key.add(property2); if (isSubPropertyOfCache.get().contains(key)) { return true; } } final boolean isSubPropertyOf = isSubPropertyOf( repositoryName, property1, property2, new HashSet<>()); if (isSubPropertyOf && repositoryName.equals("OpenCyc")) { isSubPropertyOfCache.get().add(key); } return isSubPropertyOf; } /** Returns whether the first given property is directly or indirectly a subproperty of the second given property. * * @param repositoryName the repository name * @param property1 the given term * @param property2 the given property term * @param visitedPropertyTerms the visited property terms * @return whether the first given property is directly or indirectly a subproperty of the second given property */ private boolean isSubPropertyOf( final String repositoryName, final URI property1, final URI property2, final Set<URI> visitedPropertyTerms) { //Preconditions assert repositoryName != null : "repositoryName must not be null"; assert !repositoryName.isEmpty() : "repositoryName must not be empty"; assert property1 != null : "property1 must not be null"; assert property2 != null : "property2 must not be null"; final RepositoryConnection repositoryConnection = rdfEntityManager.getConnectionToNamedRepository(repositoryName); try { final TupleQuery subPropertyOfTupleQuery = repositoryConnection.prepareTupleQuery( QueryLanguage.SERQL, SUBCLASSOF_QUERY_STRING); subPropertyOfTupleQuery.setBinding("s", property1); final TupleQueryResult tupleQueryResult = subPropertyOfTupleQuery.evaluate(); final List<URI> superPropertyTerms = new ArrayList<>(); boolean isSubPropertyOf = false; while (tupleQueryResult.hasNext()) { final BindingSet bindingSet = tupleQueryResult.next(); final URI superPropertyTerm = (URI) bindingSet.getBinding("o").getValue(); if (property2.equals(superPropertyTerm)) { isSubPropertyOf = true; break; } else if (!visitedPropertyTerms.contains(superPropertyTerm)) { visitedPropertyTerms.add(superPropertyTerm); superPropertyTerms.add(superPropertyTerm); } } tupleQueryResult.close(); if (isSubPropertyOf) { return true; } for (final URI superPropertyTerm : superPropertyTerms) { if (isSubPropertyOf( repositoryName, superPropertyTerm, property2, visitedPropertyTerms)) { return true; } } } catch (final MalformedQueryException ex) { throw new TexaiException(ex); } catch (final OpenRDFException ex) { throw new TexaiException(ex); } return false; } /** Returns the direct superclasses of the given term. * * @param repositoryName the repository name * @param term the given term * @return the direct superclasses of the given term */ public Set<URI> getDirectSuperProperties( final String repositoryName, final URI term) { //Preconditions assert repositoryName != null : "repositoryName must not be null"; assert !repositoryName.isEmpty() : "repositoryName must not be empty"; assert term != null : "term must not be null"; final RepositoryConnection repositoryConnection = rdfEntityManager.getConnectionToNamedRepository(repositoryName); final Set<URI> directSuperPropertyTerms = new ArraySet<>(); try { final TupleQuery subPropertyOfTupleQuery = repositoryConnection.prepareTupleQuery( QueryLanguage.SERQL, SUBCLASSOF_QUERY_STRING); subPropertyOfTupleQuery.setBinding("s", term); final TupleQueryResult tupleQueryResult = subPropertyOfTupleQuery.evaluate(); while (tupleQueryResult.hasNext()) { final BindingSet bindingSet = tupleQueryResult.next(); directSuperPropertyTerms.add((URI) bindingSet.getBinding("o").getValue()); } tupleQueryResult.close(); } catch (final MalformedQueryException ex) { throw new TexaiException(ex); } catch (final OpenRDFException ex) { throw new TexaiException(ex); } // LOGGER.info("getDirectSuperProperties for " + RDFUtility.formatResource(term) // + ", directSuperPropertyTerms: " + RDFUtility.formatResources(directSuperPropertyTerms)); return directSuperPropertyTerms; } /** Returns the direct and indirect superclasses of the given term. * * @param repositoryName the repository name * @param term the given term * @return the direct and indirect superclasses of the given term */ public Set<URI> getSuperProperties( final String repositoryName, final URI term) { //Preconditions assert repositoryName != null : "repositoryName must not be null"; assert !repositoryName.isEmpty() : "repositoryName must not be empty"; assert term != null : "term must not be null"; Set<URI> superPropertyTerms; if (repositoryName.equals("OpenCyc")) { superPropertyTerms = superPropertyCache.get().get(term); if (superPropertyTerms != null && !superPropertyTerms.isEmpty()) { return superPropertyTerms; } } superPropertyTerms = new HashSet<>(); getSuperProperties(repositoryName, term, superPropertyTerms); if (repositoryName.equals("OpenCyc")) { superPropertyCache.get().put(term, superPropertyTerms); } return superPropertyTerms; } /** Recursively determines the direct and indirect superclasses of the given term. * * @param repositoryName the repository name * @param term the given term * @param superPropertyTerms the current set of superclass terms */ public void getSuperProperties( final String repositoryName, final URI term, final Set<URI> superPropertyTerms) { //Preconditions assert repositoryName != null : "repositoryName must not be null"; assert !repositoryName.isEmpty() : "repositoryName must not be empty"; assert term != null : "term must not be null"; assert superPropertyTerms != null : "superPropertyTerms must not be null"; // LOGGER.info("getSuperProperties for " + RDFUtility.formatResource(term) + ", superPropertyTerms: " + RDFUtility.formatResources(superPropertyTerms)); for (final URI directSuperPropertyTerm : getDirectSuperProperties(repositoryName, term)) { if (!superPropertyTerms.contains(directSuperPropertyTerm)) { superPropertyTerms.add(directSuperPropertyTerm); getSuperProperties(repositoryName, directSuperPropertyTerm, superPropertyTerms); } } } /** Returns the direct subproperties of the given term. * * @param repositoryName the repository name * @param term the given term * @return the direct subproperties of the given term */ public Set<URI> getDirectSubProperties( final String repositoryName, final URI term) { //Preconditions assert repositoryName != null : "repositoryName must not be null"; assert !repositoryName.isEmpty() : "repositoryName must not be empty"; assert term != null : "term must not be null"; final RepositoryConnection repositoryConnection = rdfEntityManager.getConnectionToNamedRepository(repositoryName); final Set<URI> directSubPropertyTerms = new ArraySet<>(); try { final TupleQuery subPropertyOfTupleQuery = repositoryConnection.prepareTupleQuery( QueryLanguage.SERQL, SUBCLASSOF_QUERY_STRING); subPropertyOfTupleQuery.setBinding("o", term); final TupleQueryResult tupleQueryResult = subPropertyOfTupleQuery.evaluate(); while (tupleQueryResult.hasNext()) { final BindingSet bindingSet = tupleQueryResult.next(); directSubPropertyTerms.add((URI) bindingSet.getBinding("s").getValue()); } tupleQueryResult.close(); } catch (final MalformedQueryException ex) { throw new TexaiException(ex); } catch (final OpenRDFException ex) { throw new TexaiException(ex); } return directSubPropertyTerms; } /** Clears the caches. */ public void clearCaches() { isSubPropertyOfCache.get().clear(); superPropertyCache.get().clear(); } /** Provides the isSubPropertyOf cache. */ class IsSubPropertyOfCache extends ThreadLocal<Set<List<URI>>> { /** Returns the current thread's "initial value" for this thread-local variable. * * @return the current thread's "initial value */ @Override protected Set<List<URI>> initialValue() { return new LRUSet<>( 10, // initialCapacity 10000); // maxCapacity } } /** Provides the super property cache. */ class SuperPropertyCache extends ThreadLocal<Map<URI, Set<URI>>> { /** Returns the current thread's "initial value" for this thread-local variable. * * @return the current thread's "initial value */ @Override protected Map<URI, Set<URI>> initialValue() { return new LRUMap<>( 10, // initialCapacity 10000); // maxCapacity } } }