From 01ff2b7072df06e5cfe3a2b707ce38a0fa4c9f1e Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Mon, 7 Oct 2024 16:40:23 +0200 Subject: [PATCH] fixed checkstyle violations --- .../extensions/VCardsAcrossContextNodes.java | 840 +++++++++--------- 1 file changed, 412 insertions(+), 428 deletions(-) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/extensions/VCardsAcrossContextNodes.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/extensions/VCardsAcrossContextNodes.java index 5cfbd17cc..7a6d5ece7 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/extensions/VCardsAcrossContextNodes.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/extensions/VCardsAcrossContextNodes.java @@ -2,6 +2,17 @@ package edu.cornell.mannlib.vitro.webapp.searchindex.extensions; +import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.ALLTEXT; +import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.ALLTEXTUNSTEMMED; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createSelectQueryContext; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import edu.cornell.mannlib.vitro.webapp.beans.Individual; import edu.cornell.mannlib.vitro.webapp.beans.VClass; import edu.cornell.mannlib.vitro.webapp.modelaccess.ContextModelAccess; @@ -13,7 +24,6 @@ import edu.cornell.mannlib.vitro.webapp.utils.configuration.ContextModelsUser; import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property; import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation; -import edu.cornell.mannlib.vitro.webapp.visualization.constants.QueryConstants; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -24,36 +34,20 @@ import org.apache.jena.rdf.model.StmtIterator; import org.apache.jena.vocabulary.RDFS; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.ALLTEXT; -import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.ALLTEXTUNSTEMMED; -import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createSelectQueryContext; - /** - * If an individual has context nodes then the search document for that - * individual should include the labels of the partners across those nodes. The - * labels will be added to the ALLTEXT and ALLTEXTUNSTEMMED fields. + * If an individual has context nodes then the search document for that individual should include the labels of the + * partners across those nodes. The labels will be added to the ALLTEXT and ALLTEXTUNSTEMMED fields. * - * We must specify what property leads to a context node (incoming), and what - * property leads from a context node (outgoing). We may add restrictions to say - * that this only applies to individuals of certain types. We may also restrict - * the type of the applicable context nodes. + * We must specify what property leads to a context node (incoming), and what property leads from a context node + * (outgoing). We may add restrictions to say that this only applies to individuals of certain types. We may also + * restrict the type of the applicable context nodes. * - * An instance of this class acts as both a DocumentModifier and an - * IndexingUriFinder: + * An instance of this class acts as both a DocumentModifier and an IndexingUriFinder: * - * As a DocumentModifier, it looks across approved context nodes to fetch the - * labels of the partners. + * As a DocumentModifier, it looks across approved context nodes to fetch the labels of the partners. * - * As an IndexingUriFinder, it recognizes that this relationship can be changed - * by a change to a "label" statement, or to a "relates" property, and finds all - * partners as candidates for reindexing. + * As an IndexingUriFinder, it recognizes that this relationship can be changed by a change to a "label" statement, or + * to a "relates" property, and finds all partners as candidates for reindexing. * *
  * Configuration:
@@ -64,408 +58,398 @@
  *     :appliesToContextNodeType -- Optional. Match any. If none, then no restriction.
  * 
*/ -public class VCardsAcrossContextNodes implements IndexingUriFinder, - DocumentModifier, ContextModelsUser { - private static final Log log = LogFactory - .getLog(VCardsAcrossContextNodes.class); - - private RDFService rdfService; - - /** - * A name to be used in logging, to identify this instance. If not provided, - * then a descriptive label will be created. - */ - private String label; - - /** - * The URI of the property that leads into the context node. Required. - */ - private String incomingPropertyUri; - - /** - * The URI of the property that leads from the context node. Required. - */ - private String outgoingPropertyUri; - - /** - * URIs of the types of individuals to whom this instance applies. - * - * If this is not empty and an individual does not have any of these types, - * then skip that individual. - */ - private Set typeRestrictions = new HashSet<>(); - - /** - * URIs of the types of acceptable context nodes. - * - * If this is not empty and a context node does not have any of these types, - * then skip that context node's label. - */ - private Set contextNodeClasses = new HashSet<>(); - - @Override - public void setContextModels(ContextModelAccess models) { - this.rdfService = models.getRDFService(); - } - - @Property(uri = "http://www.w3.org/2000/01/rdf-schema#label", maxOccurs = 1) - public void setLabel(String l) { - label = l; - } - - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasIncomingProperty", minOccurs = 1, maxOccurs = 1) - public void setIncomingProperty(String incomingUri) { - incomingPropertyUri = incomingUri; - } - - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasOutgoingProperty", minOccurs = 1, maxOccurs = 1) - public void setOutgoingProperty(String outgoingUri) { - outgoingPropertyUri = outgoingUri; - } - - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTypeRestriction") - public void addTypeRestriction(String typeUri) { - typeRestrictions.add(typeUri); - } - - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#appliesToContextNodeType") - public void addContextNodeClass(String cnc) { - contextNodeClasses.add(cnc); - } - - @Validation - public void validate() { - if (label == null) { - label = String.format("%s[types=%s, contextNodeTypes=%s]", this - .getClass().getSimpleName(), - formatRestrictions(typeRestrictions), - formatRestrictions(contextNodeClasses)); - } - } - - private String formatRestrictions(Set uris) { - if (uris.isEmpty()) { - return "ALL"; - } else { - return localNames(uris).toString(); - } - } - - private Set localNames(Set uris) { - Set names = new HashSet<>(); - for (String uri : uris) { - try { - names.add(ResourceFactory.createResource(uri).getLocalName()); - } catch (Exception e) { - log.warn("Failed to parse URI: " + uri, e); - names.add(uri); - } - } - return names; - } - - @Override - public String toString() { - return (label == null) ? super.toString() : label; - } - - // ---------------------------------------------------------------------- - // DocumentModifier - // ---------------------------------------------------------------------- - - private static final String LABELS_WITHOUT_RESTRICTION = "" - + "PREFIX vcard: \n" - + "PREFIX rdfs: \n" - + "SELECT ?vName \n" // - + "WHERE { \n" // - + " ?uri ?incoming ?contextNode . \n" // - + " ?contextNode ?outgoing ?partner . \n" // - + " ?partner vcard:hasName ?vName . \n" // - + " FILTER( ?uri != ?partner ) \n" // - + "} \n"; - private static final String LABELS_FOR_SPECIFIC_CONTEXT_NODE_TYPE = "" +public class VCardsAcrossContextNodes implements IndexingUriFinder, DocumentModifier, ContextModelsUser { + private static final Log log = LogFactory.getLog(VCardsAcrossContextNodes.class); + + private RDFService rdfService; + + /** + * A name to be used in logging, to identify this instance. If not provided, then a descriptive label will be + * created. + */ + private String label; + + /** + * The URI of the property that leads into the context node. Required. + */ + private String incomingPropertyUri; + + /** + * The URI of the property that leads from the context node. Required. + */ + private String outgoingPropertyUri; + + /** + * URIs of the types of individuals to whom this instance applies. + * + * If this is not empty and an individual does not have any of these types, then skip that individual. + */ + private Set typeRestrictions = new HashSet<>(); + + /** + * URIs of the types of acceptable context nodes. + * + * If this is not empty and a context node does not have any of these types, then skip that context node's label. + */ + private Set contextNodeClasses = new HashSet<>(); + + @Override + public void setContextModels(ContextModelAccess models) { + this.rdfService = models.getRDFService(); + } + + @Property(uri = "http://www.w3.org/2000/01/rdf-schema#label", maxOccurs = 1) + public void setLabel(String l) { + label = l; + } + + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasIncomingProperty", minOccurs = 1, + maxOccurs = 1) + public void setIncomingProperty(String incomingUri) { + incomingPropertyUri = incomingUri; + } + + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasOutgoingProperty", minOccurs = 1, + maxOccurs = 1) + public void setOutgoingProperty(String outgoingUri) { + outgoingPropertyUri = outgoingUri; + } + + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTypeRestriction") + public void addTypeRestriction(String typeUri) { + typeRestrictions.add(typeUri); + } + + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#appliesToContextNodeType") + public void addContextNodeClass(String cnc) { + contextNodeClasses.add(cnc); + } + + @Validation + public void validate() { + if (label == null) { + label = String.format("%s[types=%s, contextNodeTypes=%s]", this + .getClass().getSimpleName(), + formatRestrictions(typeRestrictions), + formatRestrictions(contextNodeClasses)); + } + } + + private String formatRestrictions(Set uris) { + if (uris.isEmpty()) { + return "ALL"; + } else { + return localNames(uris).toString(); + } + } + + private Set localNames(Set uris) { + Set names = new HashSet<>(); + for (String uri : uris) { + try { + names.add(ResourceFactory.createResource(uri).getLocalName()); + } catch (Exception e) { + log.warn("Failed to parse URI: " + uri, e); + names.add(uri); + } + } + return names; + } + + @Override + public String toString() { + return (label == null) ? super.toString() : label; + } + + // ---------------------------------------------------------------------- + // DocumentModifier + // ---------------------------------------------------------------------- + + private static final String LABELS_WITHOUT_RESTRICTION = "" + + "PREFIX vcard: \n" + + "PREFIX rdfs: \n" + + "SELECT ?vName \n" + + "WHERE { \n" + + " ?uri ?incoming ?contextNode . \n" + + " ?contextNode ?outgoing ?partner . \n" + + " ?partner vcard:hasName ?vName . \n" + + " FILTER( ?uri != ?partner ) \n" + + "} \n"; + private static final String LABELS_FOR_SPECIFIC_CONTEXT_NODE_TYPE = "" + "PREFIX vcard: \n" - + "PREFIX rdfs: \n" - + "SELECT ?vName \n" // - + "WHERE { \n" // - + " ?uri ?incoming ?contextNode . \n" // - + " ?contextNode a ?nodeType . \n" // - + " ?contextNode ?outgoing ?partner . \n" // - + " ?partner vcard:hasName ?vName . \n" // - + " FILTER( ?uri != ?partner ) \n" // - + "} \n"; - - /** - * If this individual is acceptable, locate the labels of any context - * partners across acceptable context nodes. Add those labels to the text - * fields of the search document. - */ - @Override - public void modifyDocument(Individual ind, SearchInputDocument doc) { - if (passesTypeRestriction(ind)) { - if (contextNodeClasses.isEmpty()) { - addVCardsFromAllContextNodeClasses(ind, doc); - } else { - for (String contextNodeClass : contextNodeClasses) { - addVCardsFromContextNodeClass(ind, doc, contextNodeClass); - } - } - } - } - - private boolean passesTypeRestriction(Individual ind) { - if (typeRestrictions.isEmpty()) { - return true; - } else { - for (VClass type : ind.getVClasses()) { - if (typeRestrictions.contains(type.getURI())) { - return true; - } - } - } - return false; - } - - private void addVCardsFromAllContextNodeClasses(Individual ind, - SearchInputDocument doc) { - addVCardsToTextFields( - doc, - createSelectQueryContext(rdfService, LABELS_WITHOUT_RESTRICTION) - .bindVariableToUri("uri", ind.getURI()) - .bindVariableToUri("incoming", incomingPropertyUri) - .bindVariableToUri("outgoing", outgoingPropertyUri) - .execute().toStringFields("vName").flatten()); - } - - private void addVCardsFromContextNodeClass(Individual ind, - SearchInputDocument doc, String contextNodeClass) { - addVCardsToTextFields( - doc, - createSelectQueryContext(rdfService, - LABELS_FOR_SPECIFIC_CONTEXT_NODE_TYPE) - .bindVariableToUri("uri", ind.getURI()) - .bindVariableToUri("nodeType", contextNodeClass) - .bindVariableToUri("incoming", incomingPropertyUri) - .bindVariableToUri("outgoing", outgoingPropertyUri) - .execute().toStringFields("vName").flatten()); - } - - private void addVCardsToTextFields(SearchInputDocument doc, - List vcardUris) { - for (String vcardUri : vcardUris) { - String value = vcardToString(vcardUri); - if (!StringUtils.isEmpty(value)) { - doc.addField(ALLTEXT, value); - doc.addField(ALLTEXTUNSTEMMED, value); - } - } - } - - private String vcardToString(String vcardUri) { - String givenName = null; - String familyName = null; - - String sparqlConstruct = "CONSTRUCT\n" - + "{\n" - + " <" + vcardUri + "> ?p ?o .\n" - + "}\n" - + "WHERE\n" - + "{\n" - + " <" + vcardUri + "> ?p ?o .\n" - + "}\n"; - - try { - Model model = ModelFactory.createDefaultModel(); - rdfService.sparqlConstructQuery(sparqlConstruct, model); - - StmtIterator iter = model.listStatements(); - try { - while (iter.hasNext()) { - Statement stmt = iter.nextStatement(); - - if (stmt.getObject().isLiteral()) { - switch (stmt.getPredicate().getURI()) { - case "http://www.w3.org/2006/vcard/ns#familyName": - familyName = stmt.getObject().asLiteral().getString(); - break; - - case "http://www.w3.org/2006/vcard/ns#givenName": - givenName = stmt.getObject().asLiteral().getString(); - break; - } - } - } - } finally { - iter.close(); - } - } catch (RDFServiceException e) { - } - - if (!StringUtils.isEmpty(familyName)) { - if (!StringUtils.isEmpty(givenName)) { - return familyName + ", " + givenName; - } else { - return familyName; - } - } else if (!StringUtils.isEmpty(givenName)) { - return givenName; - } - - return null; - } - - @Override - public void shutdown() { - // Nothing to shut down. - } - - // ---------------------------------------------------------------------- - // IndexingUriFinder - // ---------------------------------------------------------------------- - - private static final String LOCATE_PARTNERS_WITHOUT_RESTRICTION = "" - + "SELECT ?partner \n" // - + "WHERE { \n" // - + " ?partner ?incoming ?contextNode . \n" // - + " ?contextNode ?outgoing ?uri . \n" // - + " FILTER( ?uri != ?partner ) \n" // - + "} \n"; - private static final String LOCATE_PARTNERS_ON_CONTEXT_NODE_TYPE = "" - + "SELECT ?partner \n" // - + "WHERE { \n" // - + " ?partner ?incoming ?contextNode . \n" // - + " ?contextNode ?outgoing ?uri . \n" // - + " ?contextNode a ?nodeType . \n" // - + " FILTER( ?uri != ?partner ) \n" // - + "} \n"; - private static final String LOCATE_OTHER_PARTNERS_ON_THIS_NODE = "" - + "SELECT ?partner \n" // - + "WHERE { \n" // - + " ?contextNode ?outgoing ?partner . \n" // - + " FILTER( ?uri != ?partner ) \n" // - + "} \n"; - private static final String GET_TYPES = "" // - + "SELECT ?type \n" // - + "WHERE { \n" // - + " ?uri a ?type . \n" // - + "} \n"; - - @Override - public void startIndexing() { - // Nothing to do. - } - - /** - * If this is a "label" statement, check to see if the subject has any - * acceptable partners across acceptable context nodes. - * - * If this is a statement that involves the specified incoming property on - * an acceptable context node, check to see if there are any acceptable - * partners on this node. - */ - @Override - public List findAdditionalURIsToIndex(Statement stmt) { - if (isLabelStatement(stmt)) { - return filterByType(locatePartners(stmt)); - } else if (isIncomingStatementOnAcceptableContextNode(stmt)) { - return filterByType(locateOtherPartners(stmt)); - } - return Collections.emptyList(); - } - - private boolean isLabelStatement(Statement stmt) { - return RDFS.label.getURI().equals(stmt.getPredicate().getURI()); - } - - private Set locatePartners(Statement stmt) { - String uri = stmt.getSubject().getURI(); - if (contextNodeClasses.isEmpty()) { - return locatePartnersWithoutRestriction(uri); - } else { - Set uris = new HashSet<>(); - for (String contextNodeClass : contextNodeClasses) { - uris.addAll(locatePartnersAcrossContextNodeClass(uri, - contextNodeClass)); - } - return uris; - } - } - - private Set locatePartnersWithoutRestriction(String uri) { - return createSelectQueryContext(rdfService, - LOCATE_PARTNERS_WITHOUT_RESTRICTION) - .bindVariableToUri("uri", uri) - .bindVariableToUri("incoming", incomingPropertyUri) - .bindVariableToUri("outgoing", outgoingPropertyUri).execute() - .toStringFields("partner").flattenToSet(); - } - - private Collection locatePartnersAcrossContextNodeClass( - String uri, String contextNodeClass) { - return createSelectQueryContext(rdfService, - LOCATE_PARTNERS_ON_CONTEXT_NODE_TYPE) - .bindVariableToUri("uri", uri) - .bindVariableToUri("nodeType", contextNodeClass) - .bindVariableToUri("incoming", incomingPropertyUri) - .bindVariableToUri("outgoing", outgoingPropertyUri).execute() - .toStringFields("partner").flattenToSet(); - } - - private boolean isIncomingStatementOnAcceptableContextNode(Statement stmt) { - String subjectUri = stmt.getSubject().getURI(); - String predicateUri = stmt.getPredicate().getURI(); - - return incomingPropertyUri.equals(predicateUri) - && (contextNodeClasses.isEmpty() || isAnyMatch( - contextNodeClasses, getTypes(subjectUri))); - } - - private boolean isAnyMatch(Set set1, Set set2) { - Set matches = new HashSet<>(set1); - matches.retainAll(set2); - return !matches.isEmpty(); - } - - private Set getTypes(String uri) { - return createSelectQueryContext(rdfService, GET_TYPES) - .bindVariableToUri("uri", uri).execute().toStringFields("type") - .flattenToSet(); - } - - private Set locateOtherPartners(Statement stmt) { - if (!stmt.getSubject().isURIResource()) { - return Collections.emptySet(); - } - - String nodeUri = stmt.getSubject().getURI(); - String objectUri = (stmt.getObject().isURIResource()) ? stmt - .getObject().asResource().getURI() : "NO_MATCH"; - - return createSelectQueryContext(rdfService, - LOCATE_OTHER_PARTNERS_ON_THIS_NODE) - .bindVariableToUri("contextNode", nodeUri) - .bindVariableToUri("uri", objectUri) - .bindVariableToUri("outgoing", outgoingPropertyUri).execute() - .toStringFields("partner").flattenToSet(); - } - - private List filterByType(Collection uris) { - if (typeRestrictions.isEmpty()) { - return new ArrayList<>(uris); - } else { - List filtered = new ArrayList<>(); - for (String uri : uris) { - if (isAnyMatch(typeRestrictions, getTypes(uri))) { - filtered.add(uri); - } - } - return filtered; - } - } - - @Override - public void endIndexing() { - // Nothing to do. - } + + "PREFIX rdfs: \n" + + "SELECT ?vName \n" + + "WHERE { \n" + + " ?uri ?incoming ?contextNode .\n" + + " ?contextNode a ?nodeType .\n" + + " ?contextNode ?outgoing ?partner .\n" + + " ?partner vcard:hasName ?vName .\n" + + " FILTER( ?uri != ?partner ) \n" + + "} \n"; + + /** + * If this individual is acceptable, locate the labels of any context partners across acceptable context nodes. Add + * those labels to the text fields of the search document. + */ + @Override + public void modifyDocument(Individual ind, SearchInputDocument doc) { + if (passesTypeRestriction(ind)) { + if (contextNodeClasses.isEmpty()) { + addVCardsFromAllContextNodeClasses(ind, doc); + } else { + for (String contextNodeClass : contextNodeClasses) { + addVCardsFromContextNodeClass(ind, doc, contextNodeClass); + } + } + } + } + + private boolean passesTypeRestriction(Individual ind) { + if (typeRestrictions.isEmpty()) { + return true; + } else { + for (VClass type : ind.getVClasses()) { + if (typeRestrictions.contains(type.getURI())) { + return true; + } + } + } + return false; + } + + private void addVCardsFromAllContextNodeClasses(Individual ind, SearchInputDocument doc) { + addVCardsToTextFields( + doc, + createSelectQueryContext(rdfService, LABELS_WITHOUT_RESTRICTION) + .bindVariableToUri("uri", ind.getURI()) + .bindVariableToUri("incoming", incomingPropertyUri) + .bindVariableToUri("outgoing", outgoingPropertyUri) + .execute().toStringFields("vName").flatten()); + } + + private void addVCardsFromContextNodeClass(Individual ind, SearchInputDocument doc, String contextNodeClass) { + addVCardsToTextFields( + doc, + createSelectQueryContext(rdfService, + LABELS_FOR_SPECIFIC_CONTEXT_NODE_TYPE) + .bindVariableToUri("uri", ind.getURI()) + .bindVariableToUri("nodeType", contextNodeClass) + .bindVariableToUri("incoming", incomingPropertyUri) + .bindVariableToUri("outgoing", outgoingPropertyUri) + .execute().toStringFields("vName").flatten()); + } + + private void addVCardsToTextFields(SearchInputDocument doc, List vcardUris) { + for (String vcardUri : vcardUris) { + String value = vcardToString(vcardUri); + if (!StringUtils.isEmpty(value)) { + doc.addField(ALLTEXT, value); + doc.addField(ALLTEXTUNSTEMMED, value); + } + } + } + + private String vcardToString(String vcardUri) { + String givenName = null; + String familyName = null; + + String sparqlConstruct = "CONSTRUCT\n" + + "{\n" + + " <" + vcardUri + "> ?p ?o .\n" + + "}\n" + + "WHERE\n" + + "{\n" + + " <" + vcardUri + "> ?p ?o .\n" + + "}\n"; + + try { + Model model = ModelFactory.createDefaultModel(); + rdfService.sparqlConstructQuery(sparqlConstruct, model); + + StmtIterator iter = model.listStatements(); + try { + while (iter.hasNext()) { + Statement stmt = iter.nextStatement(); + + if (stmt.getObject().isLiteral()) { + switch (stmt.getPredicate().getURI()) { + case "http://www.w3.org/2006/vcard/ns#familyName": + familyName = stmt.getObject().asLiteral().getString(); + break; + + case "http://www.w3.org/2006/vcard/ns#givenName": + givenName = stmt.getObject().asLiteral().getString(); + break; + } + } + } + } finally { + iter.close(); + } + } catch (RDFServiceException e) { + log.error(e, e); + } + + if (!StringUtils.isEmpty(familyName)) { + if (!StringUtils.isEmpty(givenName)) { + return familyName + ", " + givenName; + } else { + return familyName; + } + } else if (!StringUtils.isEmpty(givenName)) { + return givenName; + } + + return null; + } + + @Override + public void shutdown() { + // Nothing to shut down. + } + + // ---------------------------------------------------------------------- + // IndexingUriFinder + // ---------------------------------------------------------------------- + + private static final String LOCATE_PARTNERS_WITHOUT_RESTRICTION = "" + + "SELECT ?partner \n" // + + "WHERE { \n" // + + " ?partner ?incoming ?contextNode . \n" // + + " ?contextNode ?outgoing ?uri . \n" // + + " FILTER( ?uri != ?partner ) \n" // + + "} \n"; + private static final String LOCATE_PARTNERS_ON_CONTEXT_NODE_TYPE = "" + + "SELECT ?partner \n" // + + "WHERE { \n" // + + " ?partner ?incoming ?contextNode . \n" // + + " ?contextNode ?outgoing ?uri . \n" // + + " ?contextNode a ?nodeType . \n" // + + " FILTER( ?uri != ?partner ) \n" // + + "} \n"; + private static final String LOCATE_OTHER_PARTNERS_ON_THIS_NODE = "" + + "SELECT ?partner \n" // + + "WHERE { \n" // + + " ?contextNode ?outgoing ?partner . \n" // + + " FILTER( ?uri != ?partner ) \n" // + + "} \n"; + private static final String GET_TYPES = "" // + + "SELECT ?type \n" // + + "WHERE { \n" // + + " ?uri a ?type . \n" // + + "} \n"; + + @Override + public void startIndexing() { + // Nothing to do. + } + + /** + * If this is a "label" statement, check to see if the subject has any acceptable partners across acceptable context + * nodes. + * + * If this is a statement that involves the specified incoming property on an acceptable context node, check to see + * if there are any acceptable partners on this node. + */ + @Override + public List findAdditionalURIsToIndex(Statement stmt) { + if (isLabelStatement(stmt)) { + return filterByType(locatePartners(stmt)); + } else if (isIncomingStatementOnAcceptableContextNode(stmt)) { + return filterByType(locateOtherPartners(stmt)); + } + return Collections.emptyList(); + } + + private boolean isLabelStatement(Statement stmt) { + return RDFS.label.getURI().equals(stmt.getPredicate().getURI()); + } + + private Set locatePartners(Statement stmt) { + String uri = stmt.getSubject().getURI(); + if (contextNodeClasses.isEmpty()) { + return locatePartnersWithoutRestriction(uri); + } else { + Set uris = new HashSet<>(); + for (String contextNodeClass : contextNodeClasses) { + uris.addAll(locatePartnersAcrossContextNodeClass(uri, contextNodeClass)); + } + return uris; + } + } + + private Set locatePartnersWithoutRestriction(String uri) { + return createSelectQueryContext(rdfService, + LOCATE_PARTNERS_WITHOUT_RESTRICTION) + .bindVariableToUri("uri", uri) + .bindVariableToUri("incoming", incomingPropertyUri) + .bindVariableToUri("outgoing", outgoingPropertyUri).execute() + .toStringFields("partner").flattenToSet(); + } + + private Collection locatePartnersAcrossContextNodeClass( + String uri, String contextNodeClass) { + return createSelectQueryContext(rdfService, + LOCATE_PARTNERS_ON_CONTEXT_NODE_TYPE) + .bindVariableToUri("uri", uri) + .bindVariableToUri("nodeType", contextNodeClass) + .bindVariableToUri("incoming", incomingPropertyUri) + .bindVariableToUri("outgoing", outgoingPropertyUri).execute() + .toStringFields("partner").flattenToSet(); + } + + private boolean isIncomingStatementOnAcceptableContextNode(Statement stmt) { + String subjectUri = stmt.getSubject().getURI(); + String predicateUri = stmt.getPredicate().getURI(); + + return incomingPropertyUri.equals(predicateUri) && (contextNodeClasses.isEmpty() || isAnyMatch( + contextNodeClasses, getTypes(subjectUri))); + } + + private boolean isAnyMatch(Set set1, Set set2) { + Set matches = new HashSet<>(set1); + matches.retainAll(set2); + return !matches.isEmpty(); + } + + private Set getTypes(String uri) { + return createSelectQueryContext(rdfService, GET_TYPES).bindVariableToUri("uri", uri).execute().toStringFields( + "type").flattenToSet(); + } + + private Set locateOtherPartners(Statement stmt) { + if (!stmt.getSubject().isURIResource()) { + return Collections.emptySet(); + } + + String nodeUri = stmt.getSubject().getURI(); + String objectUri = (stmt.getObject().isURIResource()) ? stmt.getObject().asResource().getURI() : "NO_MATCH"; + + return createSelectQueryContext(rdfService, + LOCATE_OTHER_PARTNERS_ON_THIS_NODE) + .bindVariableToUri("contextNode", nodeUri) + .bindVariableToUri("uri", objectUri) + .bindVariableToUri("outgoing", outgoingPropertyUri).execute() + .toStringFields("partner").flattenToSet(); + } + + private List filterByType(Collection uris) { + if (typeRestrictions.isEmpty()) { + return new ArrayList<>(uris); + } else { + List filtered = new ArrayList<>(); + for (String uri : uris) { + if (isAnyMatch(typeRestrictions, getTypes(uri))) { + filtered.add(uri); + } + } + return filtered; + } + } + + @Override + public void endIndexing() { + // Nothing to do. + } }