updates for concept search service, adding LCSH search capability

This commit is contained in:
hudajkhan 2013-09-16 14:02:47 -04:00
parent e032ceeca4
commit ba1c6c7075
10 changed files with 1009 additions and 135 deletions

View file

@ -0,0 +1,510 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.semservices.service.impl;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.rpc.ServiceException;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.fao.www.webservices.AgrovocWS.ACSWWebService;
import org.fao.www.webservices.AgrovocWS.ACSWWebServiceServiceLocator;
import org.semanticweb.skos.SKOSAnnotation;
import org.semanticweb.skos.SKOSConcept;
import org.semanticweb.skos.SKOSDataFactory;
import org.semanticweb.skos.SKOSDataProperty;
import org.semanticweb.skos.SKOSDataRelationAssertion;
import org.semanticweb.skos.SKOSDataset;
import org.semanticweb.skos.SKOSEntity;
import org.semanticweb.skos.SKOSLiteral;
import org.semanticweb.skos.SKOSObjectRelationAssertion;
import org.semanticweb.skos.SKOSUntypedLiteral;
import org.semanticweb.skos.properties.*;
import org.semanticweb.skosapibinding.SKOSManager;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import edu.cornell.mannlib.semservices.bo.Concept;
import edu.cornell.mannlib.semservices.exceptions.ConceptsNotFoundException;
import edu.cornell.mannlib.semservices.service.ExternalConceptService;
import edu.cornell.mannlib.semservices.util.XMLUtils;
public class LCSHService implements ExternalConceptService {
protected final Log log = LogFactory.getLog(getClass());
private final String skosSuffix = ".skos.rdf";
private final String hostUri = "http://id.loc.gov";
private java.lang.String LCSHWS_address = hostUri + "/authorities/subjects";
private final String schemeUri = hostUri + "/authorities/subjects";
private final String baseUri = hostUri + "/search/";
private final String ontologyName = "LCSH";
private final String format = "SKOS";
private final String lang = "en";
private final String codeName = "hasCodeAgrovoc";
private final String searchMode = "Exact Match";
protected final String dbpedia_endpoint = " http://dbpedia.org/sparql";
//Property uris used for SKOS
protected final String SKOSNotePropertyURI = "http://www.w3.org/2004/02/skos/core#note";
protected final String SKOSPrefLabelURI = "http://www.w3.org/2004/02/skos/core#prefLabel";
protected final String SKOSAltLabelURI = "http://www.w3.org/2008/05/skos-xl#altLabel";
protected final String SKOSBroaderURI = "http://www.w3.org/2004/02/skos/core#broader";
protected final String SKOSNarrowerURI = "http://www.w3.org/2004/02/skos/core#narrower";
protected final String SKOSExactMatchURI = "http://www.w3.org/2004/02/skos/core#exactMatch";
protected final String SKOSCloseMatchURI = "http://www.w3.org/2004/02/skos/core#closeMatch";
@Override
public List<Concept> getConcepts(String term) throws Exception {
List<Concept> conceptList = new ArrayList<Concept>();
String results = null;
String dataUrl = baseUri + "?q=" + URLEncoder.encode(term, "UTF-8")
+ "&q=cs%3Ahttp%3A%2F%2Fid.loc.gov%2Fauthorities%2Fsubjects"
+ "&format=XML";
log.debug("dataURL " + dataUrl);
try {
StringWriter sw = new StringWriter();
URL rss = new URL(dataUrl);
BufferedReader in = new BufferedReader(new InputStreamReader(
rss.openStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
sw.write(inputLine);
}
in.close();
results = sw.toString();
log.debug(results);
} catch (Exception ex) {
log.error("error occurred in servlet", ex);
return null;
}
if (StringUtils.isEmpty(results)) {
return conceptList;
}
conceptList = processOutput(results);
return conceptList;
}
// Results are in json format (atom) - atom entries need to be extracted
// retrieve the URIs and get the SKOS version of the entry, getting broader
// and narrower terms as applicable as well as any description (skos:note)
// that might exist
private List<Concept> processOutput(String results) throws Exception {
List<Concept> conceptList = new ArrayList<Concept>();
SKOSManager manager = new SKOSManager();
// Get uris from the results
// Properties we will be querying for
SKOSDataFactory sdf = manager.getSKOSDataFactory();
List<String> uris = getConceptURIFromXML(results);
String bestMatch = "true";
int i = 0;
for (String uri : uris) {
if(i > 0) {
bestMatch = "false";
}
log.debug("-" + uri + "-");
String conceptUriString = getSKOSURI(uri);
URI conceptURI = null;
try {
conceptURI = new URI(conceptUriString);
} catch (URISyntaxException e) {
log.error("URI syntax exception in trying to get concept uri " + conceptUriString, e);
return conceptList;
}
log.debug("loading concept uri " + conceptUriString);
SKOSDataset dataset = manager.loadDataset(conceptURI);
Set<SKOSConcept> skosConcepts = dataset.getSKOSConcepts();
log.debug("Number of skos concepts " + skosConcepts.size());
for (SKOSConcept skosConcept : skosConcepts) {
Concept c = this.createConcept(sdf, bestMatch, skosConcept, dataset);
if(c != null) {
conceptList.add(c);
}
}
i++;
}
return conceptList;
}
//Will use skos if does not encounter error from skos api, otherwise will use regular XML parsing techniques
public Concept createConcept(SKOSDataFactory skosDataFactory, String bestMatch, SKOSConcept skosConcept, SKOSDataset dataset) {
Concept concept = new Concept();
String skosConceptURI = skosConcept.getURI().toString();
log.debug("SKOSConceptURI is " + skosConceptURI);
// get skos version of uri
concept.setUri(skosConceptURI);
concept.setConceptId(stripConceptId(skosConceptURI));
concept.setBestMatch(bestMatch);
concept.setDefinedBy(schemeUri);
concept.setSchemeURI(schemeUri);
concept.setType("");
//Get the skos annotations first to see if there is an error triggered, if so try and see if we can instead utilize XML
//For some of the SKOS concepts, a null pointer exception occurs while XML processing still works
//I do not yet know the reasons, hjk54
try {
Set<SKOSAnnotation> skosAnnots = skosConcept
.getSKOSAnnotations(dataset);
} catch(NullPointerException ex) {
concept = createConceptUsingXML(concept, bestMatch, skosConcept);
return concept;
} catch(Exception ex) {
log.debug("Error occurred for annotation retrieval for skos concept " + skosConceptURI, ex);
return null;
}
concept = this.createConceptUsingSKOS(skosDataFactory, concept, skosConcept, dataset);
return concept;
}
private Concept createConceptUsingSKOS(SKOSDataFactory skosDataFactory, Concept concept, SKOSConcept skosConcept, SKOSDataset dataset) {
SKOSPrefLabelProperty prefLabelProperty = skosDataFactory.getSKOSPrefLabelProperty();
SKOSAltLabelProperty altLabelProperty = skosDataFactory.getSKOSAltLabelProperty();
try {
List<String> labelLiterals = this.getSKOSLiteralValues(skosConcept
.getSKOSRelatedConstantByProperty(dataset,
prefLabelProperty));
if(labelLiterals.size() > 0) {
concept.setLabel(labelLiterals.get(0));
} else {
//This is an error because there should be at least one label returned
log.debug("The number of preferred labels is not greater than zero");
}
// get altLabels
List<String> altLabelList = this.getSKOSLiteralValues(skosConcept
.getSKOSRelatedConstantByProperty(dataset, altLabelProperty));
concept.setAltLabelList(altLabelList);
// See if we can get a description as well
List<String> notes = this.getSKOSAnnotationValues(skosConcept
.getSKOSAnnotationsByURI(dataset, new URI(this.SKOSNotePropertyURI)));
concept.setDefinition(StringUtils.join(notes, ","));
// get the broader property URI
List<String> broaderURIList = this.getSKOSAnnotationValues(skosConcept
.getSKOSAnnotationsByURI(dataset, new URI(this.SKOSBroaderURI)));
concept.setBroaderURIList(broaderURIList);
// get the narrower property URI
List<String> narrowerURIList = this.getSKOSAnnotationValues(skosConcept
.getSKOSAnnotationsByURI(dataset, new URI(this.SKOSNarrowerURI)));
concept.setNarrowerURIList(narrowerURIList);
// exact match
List<String> exactMatchURIList = this.getSKOSAnnotationValues(skosConcept
.getSKOSAnnotationsByURI(dataset,
new URI(this.SKOSExactMatchURI)));
concept.setExactMatchURIList(exactMatchURIList);
// close match
List<String> closeMatchURIList = this.getSKOSAnnotationValues(skosConcept
.getSKOSAnnotationsByURI(dataset,
new URI(this.SKOSCloseMatchURI)));
concept.setCloseMatchURIList(closeMatchURIList);
log.debug("add concept to list");
} catch (Exception ex) {
log.debug("Exception occurred for -" + skosConcept.getURI()
+ "- " + ex.getMessage(), ex);
return null;
}
return concept;
}
private List<String> getSKOSLiteralValues(Set<SKOSLiteral> skosLiterals) {
String lang = "";
List<String> literalValues = new ArrayList<String>();
for (SKOSLiteral literal : skosLiterals) {
if (!literal.isTyped()) {
// if it has language
SKOSUntypedLiteral untypedLiteral = literal
.getAsSKOSUntypedLiteral();
if (untypedLiteral.hasLang()) {
lang = untypedLiteral.getLang();
} else {
lang = "";
}
}
// log.debug("literal: "+ literal.getLiteral());
if (lang.equals("en")) {
log.debug("literal value: " + literal.getLiteral());
literalValues.add(literal.getLiteral());
}
}
return literalValues;
}
//For a given set of annotations (for example, for a specific property)
private List<String> getSKOSAnnotationValues(Set<SKOSAnnotation> skosAnnotations) {
List<String> valuesList = new ArrayList<String>();
for (SKOSAnnotation annotation : skosAnnotations) {
String value = this.getSKOSAnnotationStringValue(annotation);
valuesList.add(value);
}
return valuesList;
}
//Get string value for annotation
private String getSKOSAnnotationStringValue(SKOSAnnotation annotation) {
String value = new String();
if (annotation.isAnnotationByConstant()) {
SKOSLiteral literal = annotation
.getAnnotationValueAsConstant();
value = literal.getLiteral();
log.debug("broder uri: " + value);
} else {
// annotation is some resource
SKOSEntity entity = annotation.getAnnotationValue();
value = entity.getURI().toString();
}
return value;
}
//this method relies on the XML of the single SKOS rdf concept in case the SKOS api throws a null pointer exception
private Concept createConceptUsingXML(Concept concept, String bestMatch,
SKOSConcept skosConcept) {
String conceptUriString = skosConcept.getURI().toString() + this.skosSuffix;;
URL conceptURL = null;
try {
conceptURL = new URL(conceptUriString);
} catch (Exception e) {
log.error("Exception occurred in instantiating URL for " + conceptUriString, e);
//If the url is having trouble, just return null for the concept
return null;
}
log.debug("loading concept uri " + conceptUriString);
String results = null;
try {
StringWriter sw = new StringWriter();
BufferedReader in = new BufferedReader(new InputStreamReader(
conceptURL.openStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
sw.write(inputLine);
}
in.close();
results = sw.toString();
log.debug(results);
} catch (Exception ex) {
log.error("Error occurred in getting concept from the URL " + conceptUriString, ex);
return null;
}
try {
Document doc = XMLUtils.parse(results);
List<String> labelLiterals = this.getValuesFromXMLNodes(doc, "skos:prefLabel", null);
if(labelLiterals.size() > 0) {
concept.setLabel(labelLiterals.get(0));
} else {
//This is an error because there should be at least one label returned
log.debug("The number of preferred labels is not greater than zero");
}
List<String> altLabelList = this.getValuesFromXMLNodes(doc, "skos:altLabel", null);
concept.setAltLabelList(altLabelList);
List<String> broaderURIList = this.getValuesFromXMLNodes(doc, "skos:broader", "rdf:resource");
concept.setBroaderURIList(broaderURIList);
List<String> narrowerURIList = this.getValuesFromXMLNodes(doc, "skos:narrower", "rdf:resource");
concept.setNarrowerURIList(narrowerURIList);
List<String> exactMatchURIList = this.getValuesFromXMLNodes(doc, "skos:exactMatch", "rdf:resource");
concept.setExactMatchURIList(exactMatchURIList);
List<String> closeMatchURIList = this.getValuesFromXMLNodes(doc, "skos:closeMatch", "rdf:resource");
concept.setCloseMatchURIList(closeMatchURIList);
} catch (IOException e) {
log.error("error occurred in parsing " + results, e);
} catch (SAXException e) {
log.error("error occurred in parsing " + results, e);
} catch (ParserConfigurationException e) {
log.error("error occurred in parsing " + results, e);
}
return concept;
}
private String getSKOSURI(String uri) {
// Strip .xml at the end and replace with .skos.rdf
String skosURI = uri;
if (uri.endsWith(".xml")) {
skosURI = uri.substring(0, uri.length() - 4);
skosURI += skosSuffix;
}
return hostUri + skosURI;
}
public List<String> getConceptURISFromJSON(String results) {
List<String> uris = new ArrayList<String>();
try {
JSONObject json = (JSONObject) JSONSerializer.toJSON(results);
log.debug(json.toString());
// Get atom entry elements
} catch (Exception ex) {
log.error("Could not get concepts", ex);
throw ex;
}
return uris;
}
protected List<String> getConceptURIFromXML(String rdf) {
List<String> uris = new ArrayList<String>();
String conceptUri = new String();
try {
Document doc = XMLUtils.parse(rdf);
NodeList nodes = doc.getElementsByTagName("search:result");
int len = nodes.getLength();
int i;
for (i = 0; i < len; i++) {
Node node = nodes.item(i);
NamedNodeMap attrs = node.getAttributes();
Attr idAttr = (Attr) attrs.getNamedItem("uri");
conceptUri = idAttr.getTextContent();
log.debug("concept uri is " + conceptUri);
uris.add(conceptUri);
}
} catch (IOException e) {
log.error("error occurred in parsing " +rdf, e);
} catch (SAXException e) {
log.error("error occurred in parsing " +rdf, e);
} catch (ParserConfigurationException e) {
log.error("error occurred in parsing " +rdf, e);
}
return uris;
}
public List<Concept> processResults(String term) throws Exception {
return getConcepts(term);
}
/**
* @param uri
* @return
*/
protected String stripConceptId(String uri) {
String conceptId = new String();
int lastslash = uri.lastIndexOf('/');
conceptId = uri.substring(lastslash + 1, uri.length());
return conceptId;
}
/**
* @param str
* @return
*/
protected String extractConceptId(String str) {
try {
return str.substring(1, str.length() - 1);
} catch (Exception ex) {
log.error("Exception occurred in extracting concept id for " + str, ex);
return "";
}
}
@Override
public List<Concept> getConceptsByURIWithSparql(String uri)
throws Exception {
// TODO Auto-generated method stub
return null;
}
public List<String> getValuesFromXMLNodes(Document doc, String tagName, String attributeName) {
NodeList nodes = doc.getElementsByTagName(tagName);
return getValuesFromXML(nodes, attributeName);
}
//Returns list of values based on nodes and whether or not a specific attribute name should be used or just the text content
public List<String> getValuesFromXML(NodeList nodes, String attributeName) {
int len = nodes.getLength();
int i;
List<String> values = new ArrayList<String>();
for (i = 0; i < len; i++) {
Node node = nodes.item(i);
if(attributeName != null && !attributeName.isEmpty()) {
NamedNodeMap attrs = node.getAttributes();
Attr a = (Attr)attrs.getNamedItem(attributeName);
if(a != null) {
values.add(a.getTextContent());
}
} else {
values.add(node.getTextContent());
}
}
return values;
}
}

View file

@ -59,6 +59,8 @@ public class AddAssociatedConceptGenerator extends VivoBaseGenerator implements
//TODO: Set this to a dynamic mechanism
private static String VIVOCore = "http://vivoweb.org/ontology/core#";
private static String SKOSConceptType = "http://www.w3.org/2004/02/skos/core#Concept";
private static String SKOSBroaderURI = "http://www.w3.org/2004/02/skos/core#broader";
private static String SKOSNarrowerURI = "http://www.w3.org/2004/02/skos/core#narrower";
@Override
public EditConfigurationVTwo getEditConfiguration(VitroRequest vreq, HttpSession session) {
EditConfigurationVTwo editConfiguration = new EditConfigurationVTwo();
@ -97,7 +99,8 @@ public class AddAssociatedConceptGenerator extends VivoBaseGenerator implements
// Add preprocessors
addPreprocessors(editConfiguration,
ModelAccess.on(vreq).getJenaOntModel(),
ModelAccess.on(vreq).getOntModelSelector().getTBoxModel());
ModelAccess.on(vreq).getOntModelSelector().getTBoxModel(),
vreq.getWebappDaoFactory());
// Adding additional data, specifically edit mode
addFormSpecificData(editConfiguration, vreq);
// One override for basic functionality, changing url pattern
@ -189,7 +192,11 @@ public class AddAssociatedConceptGenerator extends VivoBaseGenerator implements
"?conceptNode <" + RDFS.isDefinedBy.getURI() + "> ?conceptSource .",
"?conceptNode <" + RDF.type + "> ?conceptSemanticTypeURI ." +
"?conceptSemanticTypeURI <" + RDFS.label.getURI() + "> ?conceptSemanticTypeLabel ." +
"?conceptSemanticTypeURI <" + RDFS.subClassOf + "> <" + SKOSConceptType + "> ."
"?conceptSemanticTypeURI <" + RDFS.subClassOf + "> <" + SKOSConceptType + "> .",
"?conceptNode <" + this.SKOSNarrowerURI + "> ?conceptNarrowerURI ." +
"?conceptNarrowerURI <" + this.SKOSBroaderURI + "> ?conceptNode .",
"?conceptNode <" + this.SKOSBroaderURI + "> ?conceptBroaderURI ." +
"?conceptBroaderURI <" + this.SKOSNarrowerURI + "> ?conceptNode ."
);
}
@ -254,6 +261,8 @@ public class AddAssociatedConceptGenerator extends VivoBaseGenerator implements
urisOnForm.add("conceptNode");
urisOnForm.add("conceptSource");
urisOnForm.add("conceptSemanticTypeURI");
urisOnForm.add("conceptBroaderURI");
urisOnForm.add("conceptNarrowerURI");
editConfiguration.setUrisOnform(urisOnForm);
//Also need to add the label of the concept
literalsOnForm.add("conceptLabel");
@ -288,8 +297,23 @@ public class AddAssociatedConceptGenerator extends VivoBaseGenerator implements
setVocabURIField(editConfiguration, vreq);
setConceptSemanticTypeURIField(editConfiguration,vreq);
setConceptSemanticTypeLabelField(editConfiguration,vreq);
setConceptBroaderURIField(editConfiguration, vreq);
setConceptNarrowerURIField(editConfiguration, vreq);
}
private void setConceptNarrowerURIField(
EditConfigurationVTwo editConfiguration, VitroRequest vreq) {
editConfiguration.addField(new FieldVTwo().
setName("conceptNarrowerURI"));
}
private void setConceptBroaderURIField(
EditConfigurationVTwo editConfiguration, VitroRequest vreq) {
editConfiguration.addField(new FieldVTwo().
setName("conceptBroaderURI"));
}
//this field will be hidden and include the concept node URI
private void setConceptNodeField(EditConfigurationVTwo editConfiguration,
VitroRequest vreq) {
@ -335,14 +359,17 @@ public class AddAssociatedConceptGenerator extends VivoBaseGenerator implements
//Add preprocessor
private void addPreprocessors(EditConfigurationVTwo editConfiguration, OntModel ontModel, OntModel modelChangeModel) {
private void addPreprocessors(EditConfigurationVTwo editConfiguration,
OntModel ontModel,
OntModel modelChangeModel,
WebappDaoFactory wdf) {
//An Edit submission preprocessor for enabling addition of multiple terms for a single search
//TODO: Check if this is the appropriate way of getting model
//Passing model to check for any URIs that are present
editConfiguration.addEditSubmissionPreprocessor(
new AddAssociatedConceptsPreprocessor(editConfiguration, ontModel));
new AddAssociatedConceptsPreprocessor(editConfiguration, ontModel, wdf));
editConfiguration.addModelChangePreprocessor(new ConceptSemanticTypesPreprocessor(
modelChangeModel));

View file

@ -9,6 +9,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -27,6 +28,8 @@ import com.hp.hpl.jena.vocabulary.RDFS;
import com.hp.hpl.jena.vocabulary.XSD;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.BaseEditSubmissionPreprocessorVTwo;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationUtils;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo;
@ -38,8 +41,8 @@ public class AddAssociatedConceptsPreprocessor extends
protected static final Log log = LogFactory
.getLog(AddAssociatedConceptsPreprocessor.class.getName());
//TODO: Check if better way to do this?
protected OntModel ontModel = null;
protected WebappDaoFactory wdf = null;
// Field names/variables names for n3 - these will have numbers added as
// suffix if more than one term
private static String conceptNodeBase = "conceptNode";
@ -47,6 +50,9 @@ public class AddAssociatedConceptsPreprocessor extends
private static String labelBase = "conceptLabel";
private static String conceptSemanticTypeLabelBase = "conceptSemanticTypeLabel";
private static String conceptSemanticTypeURIBase = "conceptSemanticTypeURI";
private static String conceptBroaderURIBase = "conceptBroaderURI";
private static String conceptNarrowerURIBase = "conceptNarrowerURI";
//keyed off label variable, specifies which uri variable should be used, useful if same label repeated twice
private HashMap<String, String> labelVarToUriVarHash = null;
private HashMap<String, List<String>> conceptSemanticTypeURIVarToValueMap = null;
@ -56,15 +62,21 @@ public class AddAssociatedConceptsPreprocessor extends
private static String conceptSourceValues = null;
private static String conceptSemanticTypeLabelValues = null;
private static String conceptSemanticTypeURIValues = null;
private static List<String> conceptBroaderURIValues = null;
private static List<String> conceptNarrowerURIValues = null;
private static MultiValueEditSubmission submission = null;
private static String SKOSBroaderURI = "http://www.w3.org/2004/02/skos/core#broader";
private static String SKOSNarrowerURI = "http://www.w3.org/2004/02/skos/core#narrower";
// String datatype
// Will be editing the edit configuration as well as edit submission here
public AddAssociatedConceptsPreprocessor(EditConfigurationVTwo editConfig, OntModel ontModel) {
public AddAssociatedConceptsPreprocessor(EditConfigurationVTwo editConfig, OntModel ontModel, WebappDaoFactory wadf) {
super(editConfig);
this.ontModel = ontModel;
this.wdf = wadf;
this.labelVarToUriVarHash = new HashMap<String, String>();
//Saves values of concept type uris
this.conceptSemanticTypeURIVarToValueMap = new HashMap<String, List<String>>();
@ -86,6 +98,7 @@ public class AddAssociatedConceptsPreprocessor extends
processConceptSemanticValues();
//Also need to see if any broader or narrower uris for the concepts that already exist in the system
//and set up the appropriate relationships between this concept and the broader/narrower uri
getExistingConceptRelationships();
if (numberConcepts > 1) {
processConceptNodes(numberConcepts);
}
@ -96,6 +109,8 @@ public class AddAssociatedConceptsPreprocessor extends
}
//Since we will change the uris and literals from form, we should make copies
//of the original values and store them, this will also make iterations
//and updates to the submission independent from accessing the values
@ -103,11 +118,104 @@ public class AddAssociatedConceptsPreprocessor extends
conceptLabelValues = getConceptLabelValues();
conceptNodeValues = getConceptNodeValues();
conceptSourceValues = getConceptSourceValues();
conceptBroaderURIValues = getConceptBroaderURIValues();
conceptNarrowerURIValues = getConceptNarrowerURIValues();
log.debug("concept label values are " + conceptLabelValues);
}
//
//For broader and narrower relationships, we will be
//linking the concept to broader and narrower terms where those terms already
//exist in the system
//This method or approach may change later in which case this method should change
private void getExistingConceptRelationships() {
List<String> existingNarrowerURIs = getExistingNarrowerURIs(conceptNarrowerURIValues);
List<String> existingBroaderURIs = getExistingBroaderURIs(conceptBroaderURIValues);
//Now set the submission values to these, overwriting the original
Map<String, List<String>> urisFromForm = submission.getUrisFromForm();
if(existingNarrowerURIs.size() > 0) {
urisFromForm.put("conceptNarrowerURI", existingNarrowerURIs);
} else {
//The original code for submission wouldn't put in a key if the values were null or size 0
urisFromForm.remove("conceptNarrowerURI");
}
if(existingBroaderURIs.size() > 0) {
urisFromForm.put("conceptBroaderURI", existingBroaderURIs);
} else {
urisFromForm.remove("conceptBroaderURI");
}
}
//get the broader and narrower uri values that already exist in the system from the ones returned in the search
//and use those to populate relationships between the concept and other concepts already in the system
//We should also make sure to use bidirectional n3 so the graph has both sets of relationships represented
private List<String> getConceptNarrowerURIValues() {
Map<String, List<String>> urisFromForm = submission.getUrisFromForm();
List<String> narrowerURIs = urisFromForm.get("conceptNarrowerURI");
return narrowerURIs;
}
private List<String> getConceptBroaderURIValues() {
Map<String, List<String>> urisFromForm = submission.getUrisFromForm();
List<String> broaderURIs = urisFromForm.get("conceptBroaderURI");
return broaderURIs;
}
private List<String> getExistingBroaderURIs(List<String> broaderURIs) {
if(broaderURIs == null) {
return new ArrayList<String>();
}
List<String> existingBroaderURIs = this.getExistingURIs(broaderURIs);
return existingBroaderURIs;
}
private List<String> getExistingNarrowerURIs(List<String> narrowerURIs) {
if(narrowerURIs == null)
return new ArrayList<String>();
List<String> existingNarrowerURIs = this.getExistingURIs(narrowerURIs);
return existingNarrowerURIs;
}
//We need to keep the number of elements the same if there are any entries at all in the original
//So we will use an empty string or null
private List<String> getExistingURIs(List<String> uris) {
//Important to keep the same formatting as original, because a comma delimited string as an element in the array
//refers to a list of uris appropriate for a given concept, where each element in the array corresponds to a different
//concept
List<String> existingURIs = new ArrayList<String>();
for(String uri:uris) {
if(uri.indexOf(",") != -1) {
List<String> existingURISet = new ArrayList<String>();
String[] uriSet = uri.split(",");
for(String u: uriSet) {
if(u != null && !u.isEmpty() && this.wdf.hasExistingURI(u)) {
existingURISet.add(u);
}
}
//Now add the comma delimited version back to the array
if(existingURISet.size() > 0) {
existingURIs.add(StringUtils.join(existingURISet, ","));
} else {
//add empty string to indicate no value here
existingURIs.add("");
}
} else {
if(uri != null && !uri.isEmpty() && this.wdf.hasExistingURI(uri)) {
existingURIs.add(uri);
}
else
{
existingURIs.add("");
}
}
}
return existingURIs;
}
//Process the semantic type label and URI values for the concepts
private void processConceptSemanticValues() {
conceptSemanticTypeLabelValues = getConceptSemanticTypeLabelValues();
conceptSemanticTypeURIValues = getConceptSemanticTypeURIValues();
@ -183,10 +291,10 @@ public class AddAssociatedConceptsPreprocessor extends
addConceptSourceInputs(numberConcepts);
addConceptLabelInputs(numberConcepts);
//for concept semantic type labels and uris where they exist
//TODO: Make into single method as URIs depend on labels
addConceptSemanticTypeLabelAndURIInputs(numberConcepts);
//addConceptSemanticTypeURIInputs(numberConcepts);
//For broader and narrower uris where they exist (this of course is in the case of multiple broader and narrower uris
addConceptBroaderURIInputs(numberConcepts);
addConceptNarrowerURIInputs(numberConcepts);
}
private void addConceptNodeInputs(int numberConcepts) {
@ -301,30 +409,53 @@ public class AddAssociatedConceptsPreprocessor extends
String[] uriValuesArray = uriVals.toArray(new String[uriVals.size()]);
submission.addUriToForm(editConfiguration, uriInputName, uriValuesArray);
}
//the number of existing values may not match up, or at least existing populated ones
/*
if(conceptSemanticTypeURIs != null && conceptSemanticTypeURIs.length == numberConcepts) {
int i;
for(i = 0; i < numberConcepts; i++) {
int suffix = i + 1;
String conceptInputName = conceptSemanticTypeURIBase + suffix;
String[] uriValues = new String[1];
uriValues[0] = conceptSemanticTypeURIs[i];
//Add value for uri to form
//TODO: Check if value is empty in which case don't add to submission
submission.addUriToForm(editConfiguration, conceptInputName, uriValues);
}
} else if(conceptSemanticTypeURIs != null && conceptSemanticTypeURIs.length != numberConcepts){
log.error("Number of concept nodes did not match the number of concepts to be added");
} else{
log.error("Concept nodes returned were null");
}
*/
}
private void addConceptBroaderURIInputs(int numberConcepts) {
int i;
//Add inputs based on if there are any broader uris to add
//Can't really compare number of existing broader uris to concepts
//as each concept may or may not have a broader uri
if(this.conceptBroaderURIValues.size() > 0 && this.conceptBroaderURIValues.size() <= numberConcepts) {
for(i = 0; i < numberConcepts; i++) {
int suffix = i + 1;
String conceptBroaderURIInputName = conceptBroaderURIBase + suffix;
String broaderURIs = this.conceptBroaderURIValues.get(i);
if(broaderURIs != null && !broaderURIs.isEmpty()) {
String[] broaderURISet = new String[1];
if(broaderURIs.indexOf(",") != -1) {
broaderURISet = broaderURIs.split(",");
} else {
broaderURISet[0] = broaderURIs;
}
//Add value for uri to form
submission.addUriToForm(editConfiguration, conceptBroaderURIInputName, broaderURISet);
}
}
}
}
private void addConceptNarrowerURIInputs(int numberConcepts) {
int i;
if(this.conceptNarrowerURIValues.size() > 0 && this.conceptNarrowerURIValues.size() <= numberConcepts) {
for(i = 0; i < numberConcepts; i++) {
int suffix = i + 1;
String conceptNarrowerURIInputName = conceptNarrowerURIBase + suffix;
String narrowerURIs = this.conceptNarrowerURIValues.get(i);
if(narrowerURIs != null && !narrowerURIs.isEmpty()) {
String[] narrowerURISet = new String[1];
if(narrowerURIs.indexOf(",") != -1) {
narrowerURISet = narrowerURIs.split(",");
} else {
narrowerURISet[0] = narrowerURIs;
}
//Add value for uri to form
submission.addUriToForm(editConfiguration, conceptNarrowerURIInputName, narrowerURISet);
}
}
}
}
//Fields
private void addFields(int numberConcepts) {
@ -340,7 +471,8 @@ public class AddAssociatedConceptsPreprocessor extends
String source = sourceBase + suffix;
String conceptSemanticTypeLabel = conceptSemanticTypeLabelBase + suffix;
String conceptSemanticTypeURI = this.getConceptSemanticTypeURIFieldName(conceptSemanticTypeLabel, suffix);
String conceptBroaderURI = conceptBroaderURIBase + suffix;
String conceptNarrowerURI = conceptNarrowerURIBase + suffix;
addConceptNodeField(conceptNode);
addLabelField(label);
addSourceField(source);
@ -351,9 +483,15 @@ public class AddAssociatedConceptsPreprocessor extends
conceptSemanticTypeUris.add(conceptSemanticTypeURI);
addConceptSemanticTypeURIField(conceptSemanticTypeURI);
}
//add fields for concept broader and narrower uris
addConceptBroaderURIField(conceptBroaderURI);
addConceptNarrowerURIField(conceptNarrowerURI);
}
}
private void addConceptNodeField(String conceptNode) {
List<String> validators = new ArrayList<String>();
validators.add("nonempty");
@ -394,7 +532,18 @@ public class AddAssociatedConceptsPreprocessor extends
}
}
private void addConceptNarrowerURIField(String conceptNarrowerURI) {
editConfiguration.addField(new FieldVTwo().
setName(conceptNarrowerURI));
}
private void addConceptBroaderURIField(String conceptBroaderURI) {
editConfiguration.addField(new FieldVTwo().
setName(conceptBroaderURI));
}
//original literals on form: label, uris on form: conceptNode and conceptSource
//This will overwrite the original values in the edit configuration
private void addLiteralsAndUrisOnForm(int numberTerms) {
@ -412,12 +561,16 @@ public class AddAssociatedConceptsPreprocessor extends
String conceptSemanticTypeLabel = conceptSemanticTypeLabelBase + suffix;
//String conceptSemanticTypeURI = conceptSemanticTypeURIBase + suffix;
String conceptSemanticTypeURI = this.getConceptSemanticTypeURIFieldName(conceptSemanticTypeLabel, suffix);
String conceptBroaderURI = conceptBroaderURIBase + suffix;
String conceptNarrowerURI = conceptNarrowerURIBase + suffix;
urisOnForm.add(conceptNode);
urisOnForm.add(source);
if(!conceptSemanticTypeURIs.contains(conceptSemanticTypeURI)) {
conceptSemanticTypeURIs.add(conceptSemanticTypeURI);
urisOnForm.add(conceptSemanticTypeURI);
}
urisOnForm.add(conceptBroaderURI);
urisOnForm.add(conceptNarrowerURI);
literalsOnForm.add(label);
literalsOnForm.add(conceptSemanticTypeLabel);
}
@ -456,10 +609,13 @@ public class AddAssociatedConceptsPreprocessor extends
String labelVar = "?" + labelBase;
String sourceVar = "?" + sourceBase;
String conceptSemanticTypeLabelVar = "?" + conceptSemanticTypeLabelBase;
String conceptBroaderURIVar = "?" + conceptBroaderURIBase;
String conceptNarrowerURIVar = "?" + conceptNarrowerURIBase;
String prefixStr = "@prefix core: <http://vivoweb.org/ontology/core#> .";
// First one already included so add new ones here
//We already have a label var to uri var setup
for (index = 1; index <= numberConcepts; index++) {
//Set up the variables based on which concept node
int suffix = index;
String node = nodeBase + suffix;
String label = labelVar + suffix;
@ -467,7 +623,9 @@ public class AddAssociatedConceptsPreprocessor extends
String conceptSemanticTypeLabel = conceptSemanticTypeLabelVar + suffix;
//get the URI appropriate for the concept semantic type label var
String conceptSemanticTypeURI = getConceptSemanticTypeURIVar(conceptSemanticTypeLabelBase + suffix, suffix);
//onceptSemanticTypeURIVar + suffix;
String conceptBroaderURI = conceptBroaderURIVar + suffix;
String conceptNarrowerURI = conceptNarrowerURIVar + suffix;
//Set up the n3 strings
String n3String = prefixStr;
n3String += node + " <" + RDFS.label.getURI() + "> " + label + " .\n" +
node + " <" + RDFS.isDefinedBy.getURI() + "> " + source + " .";
@ -475,10 +633,17 @@ public class AddAssociatedConceptsPreprocessor extends
n3ConceptTypeString += node + " <" + RDF.type.getURI() + "> " + conceptSemanticTypeURI + " ." +
conceptSemanticTypeURI + " <" + RDFS.label.getURI() + "> " + conceptSemanticTypeLabel + " .\n" +
conceptSemanticTypeURI + " <" + RDFS.subClassOf.getURI() + "> <http://www.w3.org/2004/02/skos/core#Concept> .\n" ;
//String representing the broader and narrower uri(s) for each of the concepts - these may or may not exist
String n3ConceptBroaderURI = prefixStr + node + " <" + this.SKOSNarrowerURI + "> " + conceptNarrowerURI + " ." +
conceptNarrowerURI + " <" + this.SKOSBroaderURI + "> " + node + " .";
String n3ConceptNarrowerURI = prefixStr + node + " <" + this.SKOSBroaderURI + "> " + conceptBroaderURI + " ." +
conceptBroaderURI + " <" + this.SKOSNarrowerURI + "> " + node + " .";
n3Optional.add(n3String);
//adding separately so their resolution does not depend on each other
n3Optional.add(n3ConceptTypeString);
n3Optional.add(n3ConceptBroaderURI);
n3Optional.add(n3ConceptNarrowerURI);
}
//Already have n3 required so need to add to that
@ -536,86 +701,93 @@ public class AddAssociatedConceptsPreprocessor extends
private String getConceptSemanticTypeLabelValues() {
Map<String, List<Literal>> literalsFromForm = submission.getLiteralsFromForm();
Map<String, List<String>> transformed = EditConfigurationUtils.transformLiteralMap(literalsFromForm);
return (String) getFirstElement(transformed.get("conceptSemanticTypeLabel"));
String label = (String) getFirstElement(transformed.get("conceptSemanticTypeLabel"));
if(label == null) {
label = "";
}
return label;
}
//This will either generate or retrieve URIs for the concept semantic type labels if they exist
//We will then update the submission to include this
private String getConceptSemanticTypeURIValues() {
String[] conceptSemanticTypeLabels = convertDelimitedStringToArray(conceptSemanticTypeLabelValues);
//keep track of what label values already exist and to which label variables they map
HashMap<String, List<Integer>> labelValueToVarSuffix = new HashMap<String, List<Integer>>();
int numberLabels = conceptSemanticTypeLabels.length;
String pseudoInputString = "";
//The rest of this code is really only relevant for multiple values, so we could break out the old code above
//as we don't need to set up hashes etc. if there is only one concept node being added
if(numberLabels == 1) {
String label = conceptSemanticTypeLabels[0];
String uri = getURIForSemanticTypeLabel(label);
if(uri != "") {
String[] urisToAdd = new String[1];
urisToAdd[0] = uri;
pseudoInputString = uri;
log.debug("uris to add" + uri);
submission.addUriToForm(this.editConfiguration, "conceptSemanticTypeURI", urisToAdd);
}
if(conceptSemanticTypeLabelValues != null && !conceptSemanticTypeLabelValues.isEmpty()) {
String[] conceptSemanticTypeLabels = convertDelimitedStringToArray(conceptSemanticTypeLabelValues);
//keep track of what label values already exist and to which label variables they map
HashMap<String, List<Integer>> labelValueToVarSuffix = new HashMap<String, List<Integer>>();
int numberLabels = conceptSemanticTypeLabels.length;
}
//if there is more than one concept node, we may have duplicate semantic types
//which will need to be referred to by the same semantic type uri
else if (numberLabels > 1){
for(int i = 0; i < numberLabels; i++) {
int suffix = i + 1;
String label = conceptSemanticTypeLabels[i];
String labelVar = this.conceptSemanticTypeLabelBase + suffix;
//if label has not already been encountered, create entry for label value
//and list with the label variables that would refer to it
//for unique values, the uri variable will be the same as label
Integer thisSuffix = new Integer(suffix);
if(!labelValueToVarSuffix.containsKey(label)) {
labelValueToVarSuffix.put(label, new ArrayList<Integer>());
//Add suffix to list if not already there
labelValueToVarSuffix.get(label).add(thisSuffix);
} else {
//in this case, the label already exists, get the very first element in the list
//and use that as the uri variable
List<Integer> suffixList = labelValueToVarSuffix.get(label);
if(suffixList != null && suffixList.size() > 0) {
thisSuffix = suffixList.get(0);
}
}
//Now add the uri var to the hash mapping label variable to uri variable
String uriVar = this.conceptSemanticTypeURIBase + thisSuffix.intValue();
this.labelVarToUriVarHash.put(labelVar, uriVar);
//Make or retrieve URI for this label
//TODO: Do we create this string with empty inputs ?
//The rest of this code is really only relevant for multiple values, so we could break out the old code above
//as we don't need to set up hashes etc. if there is only one concept node being added
if(numberLabels == 1) {
String label = conceptSemanticTypeLabels[0];
String uri = getURIForSemanticTypeLabel(label);
if(uri != "") {
//uri var shouldn't be repeated?
if(!this.conceptSemanticTypeURIVarToValueMap.containsKey(uriVar)) {
this.conceptSemanticTypeURIVarToValueMap.put(uriVar, new ArrayList<String>());
this.conceptSemanticTypeURIVarToValueMap.get(uriVar).add(uri);
}
String[] urisToAdd = new String[1];
urisToAdd[0] = uri;
pseudoInputString = uri;
log.debug("uris to add" + uri);
submission.addUriToForm(this.editConfiguration, "conceptSemanticTypeURI", urisToAdd);
}
if(i != 0) {
pseudoInputString += ",";
}
pseudoInputString += uri;
}
//if there is more than one concept node, we may have duplicate semantic types
//which will need to be referred to by the same semantic type uri
else if (numberLabels > 1){
//Add this string to the uris for the form
String[] urisToAdd = new String[1];
urisToAdd[0] = pseudoInputString;
log.debug("uris to add" + pseudoInputString);
submission.addUriToForm(this.editConfiguration, "conceptSemanticTypeURI", urisToAdd);
for(int i = 0; i < numberLabels; i++) {
int suffix = i + 1;
String label = conceptSemanticTypeLabels[i];
String labelVar = this.conceptSemanticTypeLabelBase + suffix;
//if label has not already been encountered, create entry for label value
//and list with the label variables that would refer to it
//for unique values, the uri variable will be the same as label
Integer thisSuffix = new Integer(suffix);
if(!labelValueToVarSuffix.containsKey(label)) {
labelValueToVarSuffix.put(label, new ArrayList<Integer>());
//Add suffix to list if not already there
labelValueToVarSuffix.get(label).add(thisSuffix);
} else {
//in this case, the label already exists, get the very first element in the list
//and use that as the uri variable
List<Integer> suffixList = labelValueToVarSuffix.get(label);
if(suffixList != null && suffixList.size() > 0) {
thisSuffix = suffixList.get(0);
}
}
//Now add the uri var to the hash mapping label variable to uri variable
String uriVar = this.conceptSemanticTypeURIBase + thisSuffix.intValue();
this.labelVarToUriVarHash.put(labelVar, uriVar);
//Make or retrieve URI for this label
//TODO: Do we create this string with empty inputs ?
String uri = getURIForSemanticTypeLabel(label);
if(uri != "") {
//uri var shouldn't be repeated?
if(!this.conceptSemanticTypeURIVarToValueMap.containsKey(uriVar)) {
this.conceptSemanticTypeURIVarToValueMap.put(uriVar, new ArrayList<String>());
this.conceptSemanticTypeURIVarToValueMap.get(uriVar).add(uri);
}
}
if(i != 0) {
pseudoInputString += ",";
}
pseudoInputString += uri;
}
//Add this string to the uris for the form
String[] urisToAdd = new String[1];
urisToAdd[0] = pseudoInputString;
log.debug("uris to add" + pseudoInputString);
submission.addUriToForm(this.editConfiguration, "conceptSemanticTypeURI", urisToAdd);
}
}
return pseudoInputString;
}

View file

@ -23,6 +23,7 @@ public class ConceptSearchServiceUtils {
private static final String UMLSVocabSource = "http://link.informatics.stonybrook.edu/umls";
private static final String AgrovocVocabSource = "http://aims.fao.org/aos/agrovoc/agrovocScheme";
private static final String GemetVocabSource = "http://www.eionet.europa.eu/gemet/gemetThesaurus";
private static final String LCSHVocabSource = "http://id.loc.gov/authorities/subjects";
//Get the class that corresponds to the appropriate search
public static String getConceptSearchServiceClassName(String searchServiceName) {
@ -41,6 +42,8 @@ public class ConceptSearchServiceUtils {
//Commenting out agrovoc for now until implementation is updated
map.put(AgrovocVocabSource, new VocabSourceDescription("AGROVOC", AgrovocVocabSource, "http://www.fao.org/agrovoc/", "Agricultural Vocabulary"));
map.put(GemetVocabSource, new VocabSourceDescription("GEMET", GemetVocabSource, "http://www.eionet.europa.eu/gemet", "GEneral Multilingual Environmental Thesaurus"));
map.put(LCSHVocabSource, new VocabSourceDescription("LCSH", LCSHVocabSource, "http://id.loc.gov/authorities/subjects/", "Library of Congress Subject Headings"));
return map;
}
@ -53,6 +56,8 @@ public class ConceptSearchServiceUtils {
map.put(UMLSVocabSource, "edu.cornell.mannlib.semservices.service.impl.UMLSService");
map.put(AgrovocVocabSource, "edu.cornell.mannlib.semservices.service.impl.AgrovocService");
map.put(GemetVocabSource, "edu.cornell.mannlib.semservices.service.impl.GemetService");
map.put(LCSHVocabSource, "edu.cornell.mannlib.semservices.service.impl.LCSHService");
return map;
}