diff --git a/productMods/templates/freemarker/edit/forms/addAssociatedConceptVocabSpecificDisplay.ftl b/productMods/templates/freemarker/edit/forms/addAssociatedConceptVocabSpecificDisplay.ftl
new file mode 100644
index 00000000..43ab963e
--- /dev/null
+++ b/productMods/templates/freemarker/edit/forms/addAssociatedConceptVocabSpecificDisplay.ftl
@@ -0,0 +1,14 @@
+<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
+
+<#--The original concept javascript is service independent, i.e. all vocabulary service information is returned from the servlet
+and the template itself generates the same display for all the services. Right now we would like to show a different label
+in the search results based on the service. I am storing that information here and later we can consider how the display
+can return to being independent of vocabulary service-specific display options.
+These values will be passed to the javascript-->
+
+<#assign vocabSpecificDisplay = {
+"http://link.informatics.stonybrook.edu/umls":"${i18n().label_type}",
+"http://aims.fao.org/aos/agrovoc/agrovocScheme":"${i18n().label_altLabels}",
+"http://www.eionet.europa.eu/gemet/gemetThesaurus":"${i18n().label_type}",
+"http://id.loc.gov/authorities/subjects":"${i18n().label_altLabels}"
+}/>
diff --git a/productMods/templates/freemarker/edit/forms/css/addConcept.css b/productMods/templates/freemarker/edit/forms/css/addConcept.css
index 967571ab..225b86bf 100644
--- a/productMods/templates/freemarker/edit/forms/css/addConcept.css
+++ b/productMods/templates/freemarker/edit/forms/css/addConcept.css
@@ -1,5 +1,9 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+.conceptsListContainer {
+ overflow:hidden;
+ width:100%;
+}
.concepts .column {
float:left;
padding-right:3px;
@@ -44,4 +48,29 @@ form#addConceptForm {
form#addConceptForm span#createOwnOne{
float:left;
margin-top:24px
+}
+
+/*For existing concepts, display will also be tabular with columns*/
+
+.existingConcept .row, .conceptHeadings .row {
+ clear:both;
+ float:left;
+}
+
+.existingConcept .column , .conceptHeadings .column {
+ float:left;
+ padding-right:3px;
+ clear:none !important; /*Overriding customFor div's clearing*/
+}
+/*label and semantic type if it exists*/
+.existingConcept .conceptLabelInfo, .conceptHeadings .conceptLabelInfo {
+ width:220px;
+}
+
+.existingConcept .conceptVocabSource, .conceptHeadings .conceptVocabSource {
+ width:220px;
+}
+
+.conceptHeadings .row {
+ border-bottom: 1px solid #5F6464;
}
\ No newline at end of file
diff --git a/productMods/templates/freemarker/edit/forms/js/addConcept.js b/productMods/templates/freemarker/edit/forms/js/addConcept.js
index d650d349..ce9200a6 100644
--- a/productMods/templates/freemarker/edit/forms/js/addConcept.js
+++ b/productMods/templates/freemarker/edit/forms/js/addConcept.js
@@ -55,12 +55,19 @@ var addConceptForm = {
this.externalConceptLabel = $('#conceptLabel');
this.externalConceptSource = $('#conceptSource');
this.externalConceptSemanticTypeLabel = $("#conceptSemanticTypeLabel");
+ this.externalConceptBroaderUris = $("#conceptBroaderURI");
+ this.externalConceptNarrowerUris = $("#conceptNarrowerURI");
//remove links
this.removeConceptLinks = $('a.remove');
this.errors = $('#errors');
this.createOwn1 = $('#createOwnOne');
this.createOwn2 = $('#createOwnTwo');
this.orSpan = $('span.or')
+ this.loadingIndicator = $("#indicator");
+ this.showHideSearchResults = $("#showHideResults");
+ //Value we are setting to cut off length of alternate labels string
+ this.maxNumberAlternateLabels = 4;
+ this.numberOfMaxInitialSearchResults = 7;
},
initPage: function() {
@@ -87,6 +94,10 @@ var addConceptForm = {
addConceptForm.removeExistingConcept(this);
return false;
});
+ this.showHideSearchResults.find("a#showHideLink").click(function() {
+ addConceptForm.showHideMultipleSearchResults(this);
+ return false;
+ });
},
initForm: function() {
// Hide the button that shows the form
@@ -99,7 +110,9 @@ var addConceptForm = {
//Also clear the search input
this.searchTerm.val("");
this.cancel.unbind('click');
-
+ //make sure results loading indicator is hidden
+ this.loadingIndicator.addClass("hidden");
+ this.showHideSearchResults.hide();
// Show the form
this.form.show();
},
@@ -114,6 +127,8 @@ var addConceptForm = {
},
clearSearchResults:function() {
$('#selectedConcept').empty();
+ //Hide the indicator icon if still there
+ $("#indicator").addClass("hidden");
},
clearErrors:function() {
addConceptForm.errors.empty();
@@ -134,6 +149,25 @@ var addConceptForm = {
this.hideForm();
this.showFormButtonWrapper.show();
},
+ showHideMultipleSearchResults: function(link) {
+ if($(link).hasClass("showmore")) {
+ //if clicking and already says show more then need to show the rest of the results
+ $("li.concepts").show(); //show everything
+ $(link).html("Show fewer results");
+ $(link).removeClass("showmore");
+ } else {
+ //if clicking and does not say show more than need to show less
+ $("li.concepts").slice(addConceptForm.numberOfMaxInitialSearchResults).hide();
+ $(link).html("Show more results");
+ $(link).addClass("showmore");
+ }
+ },
+ //reset this to default, which is hidden with show more link
+ resetShowHideMultipleSearchResults: function() {
+ addConceptForm.showHideSearchResults.hide();
+ addConceptForm.showHideSearchResults.find("a#showHideLink").html("Show more results");
+ addConceptForm.showHideSearchResults.find("a#showHideLink").addClass("showmore");
+ },
submitSearchTerm: function() {
//Get value of search term
var searchValue = this.searchTerm.val();
@@ -145,7 +179,11 @@ var addConceptForm = {
}
var vocabSourceValue = checkedVocabSource.val();
var dataServiceUrl = addConceptForm.dataServiceUrl + "?searchTerm=" + encodeURIComponent(searchValue) + "&source=" + encodeURIComponent(vocabSourceValue);
- //This should return an object including the concept list or any errors if there are any
+ //Show the loading icon until the results appear
+ addConceptForm.loadingIndicator.removeClass("hidden");
+ //Hide and reset the show more button
+ addConceptForm.resetShowHideMultipleSearchResults();
+ //This should return an object including the concept list or any errors if there are any
$.getJSON(dataServiceUrl, function(results) {
var htmlAdd = "";
var vocabUnavailable = "
" + addConceptForm.vocServiceUnavailable + "
";
@@ -166,7 +204,7 @@ var addConceptForm = {
//For each result, display
if(numberTotalMatches > 0) {
htmlAdd = "
";
- htmlAdd+= addConceptForm.addResultsHeader();
+ htmlAdd+= addConceptForm.addResultsHeader(vocabSourceValue);
//Show best matches first
for(i = 0; i < numberBestMatches; i++) {
var conceptResult = bestMatchResults[i];
@@ -184,6 +222,8 @@ var addConceptForm = {
}
if(htmlAdd.length) {
+ //hide the loading icon again
+ addConceptForm.loadingIndicator.addClass("hidden");
$('#selectedConcept').html(htmlAdd);
if (htmlAdd.indexOf("No search results") >= 0) {
addConceptForm.showHiddenElements(hasResults);
@@ -191,6 +231,8 @@ var addConceptForm = {
else {
hasResults = true;
addConceptForm.showHiddenElements(hasResults);
+ //Here, tweak the display based on the number of results
+ addConceptForm.displayUptoMaxResults();
}
}
});
@@ -203,9 +245,12 @@ var addConceptForm = {
var definedBy = conceptResult.definedBy;
var type = conceptResult.type;
var uri = conceptResult.uri;
+ //also adding broader and narrower uris wherever they exist
+ var broaderUris = conceptResult.broaderURIList;
+ var narrowerUris = conceptResult.narrowerURIList;
//this will be null if there are no alternate labels
var altLabels = conceptResult.altLabelList;
- return addConceptForm.generateIndividualConceptDisplay(uri, label, altLabels, definition, type, definedBy, isBestMatch);
+ return addConceptForm.generateIndividualConceptDisplay(uri, label, altLabels, definition, type, definedBy, isBestMatch, broaderUris, narrowerUris);
},
//This should now return all best matches in one array and other results in another
parseResults:function(resultsArray) {
@@ -225,10 +270,19 @@ var addConceptForm = {
}
return {"bestMatch":bestMatchResults, "alternate":alternateResults};
},
- addResultsHeader:function() {
- var htmlAdd = "
" +
addConceptForm.generateIndividualDefinitionDisplay(definition) +
addConceptForm.generateBestOrAlternate(isBestMatch) +
@@ -274,14 +338,23 @@ var addConceptForm = {
"";
return htmlAdd;
},
- generateIndividualCUIInput:function(cuiURI, label, type, definedBy) {
- return "";
+ generateIndividualCUIInput:function(cuiURI, label, type, definedBy, broaderUris, narrowerUris) {
+ return "";
},
//In case there are multiple labels display those
generateIndividualLabelsDisplay:function(label, altLabels) {
var labelDisplay = label;
+ var displayAltLabels = altLabels;
if(altLabels != null && altLabels.length > 0) {
- labelDisplay += " [" + altLabels + "]";
+ //Certain vocabulary services might return a long list of alternate labels, in which case we will show fewer
+ //display only upto a certain number of alternate labels and use an ellipsis to signify there
+ //are additional terms
+ if(altLabels.length > addConceptForm.maxNumberAlternateLabels) {
+ displayAltLabels = altLabels.slice(0, addConceptForm.maxNumberAlternateLabels) + ",...";
+ }
+ labelDisplay += " (" + displayAltLabels + ")";
}
return labelDisplay;
},
@@ -307,6 +380,18 @@ var addConceptForm = {
}
return "
";
},
+ //Certain vocabulary services return a great number of results, we would like the ability to show more or less of those results
+ displayUptoMaxResults:function() {
+ var numberConcepts = $("li.concepts").length;
+ if(numberConcepts > addConceptForm.numberOfMaxInitialSearchResults) {
+ $("li.concepts").slice(addConceptForm.numberOfMaxInitialSearchResults).hide();
+ //Hide the link for showing/hiding search results
+ addConceptForm.showHideSearchResults.show();
+ addConceptForm.showHideSearchResults.find("a#showHideLink").html("Show more results");
+ addConceptForm.showHideSearchResults.find("a#showHideLink").addClass("showmore");
+ }
+
+ },
validateConceptSelection:function(checkedElements) {
var numberElements = checkedElements.length;
if(numberElements < 1) {
diff --git a/rdf/abox/filegraph/vocabularySource.n3 b/rdf/abox/filegraph/vocabularySource.n3
index a111d896..cb51ff84 100644
--- a/rdf/abox/filegraph/vocabularySource.n3
+++ b/rdf/abox/filegraph/vocabularySource.n3
@@ -3,4 +3,6 @@
.
"UMLS"^^ .
"AGROVOC"^^ .
- "GEMET"^^ .
\ No newline at end of file
+ "GEMET"^^ .
+ .
+ "LCSH"^^ .
diff --git a/src/edu/cornell/mannlib/semservices/service/impl/LCSHService.java b/src/edu/cornell/mannlib/semservices/service/impl/LCSHService.java
new file mode 100644
index 00000000..14a59dd1
--- /dev/null
+++ b/src/edu/cornell/mannlib/semservices/service/impl/LCSHService.java
@@ -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 getConcepts(String term) throws Exception {
+ List conceptList = new ArrayList();
+ 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 processOutput(String results) throws Exception {
+ List conceptList = new ArrayList();
+ SKOSManager manager = new SKOSManager();
+ // Get uris from the results
+ // Properties we will be querying for
+ SKOSDataFactory sdf = manager.getSKOSDataFactory();
+
+
+ List 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 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 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 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 altLabelList = this.getSKOSLiteralValues(skosConcept
+ .getSKOSRelatedConstantByProperty(dataset, altLabelProperty));
+ concept.setAltLabelList(altLabelList);
+
+ // See if we can get a description as well
+ List notes = this.getSKOSAnnotationValues(skosConcept
+ .getSKOSAnnotationsByURI(dataset, new URI(this.SKOSNotePropertyURI)));
+
+ concept.setDefinition(StringUtils.join(notes, ","));
+
+ // get the broader property URI
+ List broaderURIList = this.getSKOSAnnotationValues(skosConcept
+ .getSKOSAnnotationsByURI(dataset, new URI(this.SKOSBroaderURI)));
+ concept.setBroaderURIList(broaderURIList);
+
+ // get the narrower property URI
+ List narrowerURIList = this.getSKOSAnnotationValues(skosConcept
+ .getSKOSAnnotationsByURI(dataset, new URI(this.SKOSNarrowerURI)));
+ concept.setNarrowerURIList(narrowerURIList);
+
+ // exact match
+ List exactMatchURIList = this.getSKOSAnnotationValues(skosConcept
+ .getSKOSAnnotationsByURI(dataset,
+ new URI(this.SKOSExactMatchURI)));
+ concept.setExactMatchURIList(exactMatchURIList);
+
+ // close match
+ List 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 getSKOSLiteralValues(Set skosLiterals) {
+ String lang = "";
+ List literalValues = new ArrayList();
+ 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 getSKOSAnnotationValues(Set skosAnnotations) {
+ List valuesList = new ArrayList();
+ 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 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 altLabelList = this.getValuesFromXMLNodes(doc, "skos:altLabel", null);
+ concept.setAltLabelList(altLabelList);
+
+ List broaderURIList = this.getValuesFromXMLNodes(doc, "skos:broader", "rdf:resource");
+ concept.setBroaderURIList(broaderURIList);
+ List narrowerURIList = this.getValuesFromXMLNodes(doc, "skos:narrower", "rdf:resource");
+ concept.setNarrowerURIList(narrowerURIList);
+
+ List exactMatchURIList = this.getValuesFromXMLNodes(doc, "skos:exactMatch", "rdf:resource");
+ concept.setExactMatchURIList(exactMatchURIList);
+ List 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 getConceptURISFromJSON(String results) {
+ List uris = new ArrayList();
+ 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 getConceptURIFromXML(String rdf) {
+ List uris = new ArrayList();
+ 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 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 getConceptsByURIWithSparql(String uri)
+ throws Exception {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+
+
+ public List 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 getValuesFromXML(NodeList nodes, String attributeName) {
+ int len = nodes.getLength();
+ int i;
+ List values = new ArrayList();
+ 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;
+ }
+
+
+
+}
diff --git a/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/AddAssociatedConceptGenerator.java b/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/AddAssociatedConceptGenerator.java
index 23f4e1bf..98d38df6 100644
--- a/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/AddAssociatedConceptGenerator.java
+++ b/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/AddAssociatedConceptGenerator.java
@@ -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));
diff --git a/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/AddAssociatedConceptsPreprocessor.java b/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/AddAssociatedConceptsPreprocessor.java
index 1b81dbf1..87deceb4 100644
--- a/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/AddAssociatedConceptsPreprocessor.java
+++ b/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/AddAssociatedConceptsPreprocessor.java
@@ -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 labelVarToUriVarHash = null;
private HashMap> 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 conceptBroaderURIValues = null;
+ private static List 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();
//Saves values of concept type uris
this.conceptSemanticTypeURIVarToValueMap = new HashMap>();
@@ -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 existingNarrowerURIs = getExistingNarrowerURIs(conceptNarrowerURIValues);
+ List existingBroaderURIs = getExistingBroaderURIs(conceptBroaderURIValues);
+ //Now set the submission values to these, overwriting the original
+ Map> 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 getConceptNarrowerURIValues() {
+ Map> urisFromForm = submission.getUrisFromForm();
+ List narrowerURIs = urisFromForm.get("conceptNarrowerURI");
+ return narrowerURIs;
+ }
+
+ private List getConceptBroaderURIValues() {
+ Map> urisFromForm = submission.getUrisFromForm();
+ List broaderURIs = urisFromForm.get("conceptBroaderURI");
+ return broaderURIs;
+ }
+
+ private List getExistingBroaderURIs(List broaderURIs) {
+ if(broaderURIs == null) {
+ return new ArrayList();
+ }
+ List existingBroaderURIs = this.getExistingURIs(broaderURIs);
+ return existingBroaderURIs;
+ }
+
+ private List getExistingNarrowerURIs(List narrowerURIs) {
+ if(narrowerURIs == null)
+ return new ArrayList();
+ List 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 getExistingURIs(List 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 existingURIs = new ArrayList();
+ for(String uri:uris) {
+ if(uri.indexOf(",") != -1) {
+ List existingURISet = new ArrayList();
+ 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 validators = new ArrayList();
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: .";
// 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() + "> .\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> literalsFromForm = submission.getLiteralsFromForm();
Map> 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> labelValueToVarSuffix = new HashMap>();
- 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> labelValueToVarSuffix = new HashMap>();
+ 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());
- //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 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());
- 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());
+ //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 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());
+ 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;
}
diff --git a/src/edu/cornell/mannlib/vitro/webapp/utils/ConceptSearchService/ConceptSearchServiceUtils.java b/src/edu/cornell/mannlib/vitro/webapp/utils/ConceptSearchService/ConceptSearchServiceUtils.java
index d232e875..68705bd3 100644
--- a/src/edu/cornell/mannlib/vitro/webapp/utils/ConceptSearchService/ConceptSearchServiceUtils.java
+++ b/src/edu/cornell/mannlib/vitro/webapp/utils/ConceptSearchService/ConceptSearchServiceUtils.java
@@ -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;
}
diff --git a/themes/wilma/i18n/all.properties b/themes/wilma/i18n/all.properties
index a0f2585f..37d9ac61 100644
--- a/themes/wilma/i18n/all.properties
+++ b/themes/wilma/i18n/all.properties
@@ -692,6 +692,7 @@ or_add_new_one = or add a new one.
vocabulary_service_unavailable = The vocabulary service is unavailable. Please try again later.
no_serch_results_found = No search results were found.
label_type = Label (Type)
+label_altLabels = Label (Alternate Labels)
definition_capitalized = Definition
best_match = Best Match
select_term_from_results = Please select at least one term from the search search results.