NIHVIVO-3748 Implement a shortview_config.n3 file to set up the short views. It had been hardcoded.
This commit is contained in:
parent
f839583068
commit
e8f01e9838
4 changed files with 381 additions and 48 deletions
|
@ -4,9 +4,13 @@ package edu.cornell.mannlib.vitro.webapp.services.shortview;
|
|||
|
||||
import static edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewService.ShortViewContext.BROWSE;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -19,28 +23,264 @@ import com.hp.hpl.jena.ontology.OntModel;
|
|||
import com.hp.hpl.jena.ontology.OntModelSpec;
|
||||
import com.hp.hpl.jena.rdf.model.ModelFactory;
|
||||
import com.hp.hpl.jena.rdf.model.Property;
|
||||
import com.hp.hpl.jena.rdf.model.RDFNode;
|
||||
import com.hp.hpl.jena.rdf.model.ResIterator;
|
||||
import com.hp.hpl.jena.rdf.model.Resource;
|
||||
import com.hp.hpl.jena.rdf.model.Statement;
|
||||
import com.hp.hpl.jena.rdf.model.StmtIterator;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.DisplayVocabulary;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.VClassDao;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
||||
import edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewService.ShortViewContext;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.SparqlQueryDataGetter;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* Read a config file that describes the short views. Read it into a model and
|
||||
* scan the model to determine what each view consists of (data getter URIs,
|
||||
* template names), what context each view applies to, and what classes map to
|
||||
* each view.
|
||||
*
|
||||
* Get rid of this when the Application Ontology is implemented.
|
||||
* Data getters must be SparqlQueryDataGetters, and must be described in the
|
||||
* same config file.
|
||||
*
|
||||
* TODO Get rid of this when the Application Ontology is implemented.
|
||||
*/
|
||||
public class FakeApplicationOntologyService {
|
||||
private static final Log log = LogFactory
|
||||
.getLog(FakeApplicationOntologyService.class);
|
||||
|
||||
private static final String FACULTY_MEMBER_CLASS_URI = "http://vivoweb.org/ontology/core#FacultyMember";
|
||||
private static final String PEOPLE_CLASSGROUP_URI = "http://vivoweb.org/ontology#vitroClassGrouppeople";
|
||||
public static final String FILE_OF_SHORT_VIEW_INFO = "/WEB-INF/resources/shortview_config.n3";
|
||||
|
||||
private static final String NS = "http://vitro.mannlib.cornell.edu/ontologies/display/1.1#";
|
||||
private static final String HAS_TEMPLATE = NS + "hasTemplate";
|
||||
private static final String CUSTOM_VIEW = NS + "customViewForIndividual";
|
||||
private static final String APPLIES_TO = NS + "appliesToContext";
|
||||
private static final String HAS_DATA_GETTER = NS + "hasDataGetter";
|
||||
private static final String HAS_VIEW = NS + "hasCustomView";
|
||||
private static final String RDF_TYPE = VitroVocabulary.RDF_TYPE;
|
||||
|
||||
private final OntModel viewModel;
|
||||
private final Map<String, List<ViewSpec>> classUriToViewSpecs;
|
||||
|
||||
/**
|
||||
* Load the model from the config file, and inspect it for Views and
|
||||
* mappings.
|
||||
*
|
||||
* Keep the model - we'll need it when its time to create the DataGetters
|
||||
* (on each request).
|
||||
*/
|
||||
public FakeApplicationOntologyService(ServletContext ctx)
|
||||
throws ShortViewConfigException {
|
||||
this.viewModel = createModelFromFile(ctx);
|
||||
|
||||
Map<String, ViewSpec> viewSpecsByUri = createViewSpecs();
|
||||
this.classUriToViewSpecs = createClassMappings(viewSpecsByUri);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Mapping: " + classUriToViewSpecs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If we fail to parse the config file, use this constructor instead, to
|
||||
* simulate an empty config file.
|
||||
*/
|
||||
public FakeApplicationOntologyService() {
|
||||
this.viewModel = ModelFactory
|
||||
.createOntologyModel(OntModelSpec.OWL_DL_MEM);
|
||||
this.classUriToViewSpecs = new HashMap<String, List<ViewSpec>>();
|
||||
|
||||
log.debug("Created empty FakeApplicationOntologyService.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the short view config file into an OntModel.
|
||||
*/
|
||||
private OntModel createModelFromFile(ServletContext ctx)
|
||||
throws ShortViewConfigException {
|
||||
InputStream stream = ctx.getResourceAsStream(FILE_OF_SHORT_VIEW_INFO);
|
||||
if (stream == null) {
|
||||
throw new ShortViewConfigException("The short view config file "
|
||||
+ "doesn't exist in the servlet context: '"
|
||||
+ FILE_OF_SHORT_VIEW_INFO + "'");
|
||||
}
|
||||
|
||||
OntModel m = ModelFactory.createOntologyModel(OntModelSpec.OWL_DL_MEM);
|
||||
try {
|
||||
m.read(stream, null, "N3");
|
||||
} catch (Exception e) {
|
||||
throw new ShortViewConfigException(
|
||||
"Parsing error in the short view config file.", e);
|
||||
} finally {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("Loaded " + m.size() + " statements");
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all of the views.
|
||||
*/
|
||||
private Map<String, ViewSpec> createViewSpecs()
|
||||
throws ShortViewConfigException {
|
||||
Property rdfType = viewModel.getProperty(RDF_TYPE);
|
||||
Property appliesTo = viewModel.getProperty(APPLIES_TO);
|
||||
Property dataGetter = viewModel.getProperty(HAS_DATA_GETTER);
|
||||
Property template = viewModel.getProperty(HAS_TEMPLATE);
|
||||
Resource customView = viewModel.getResource(CUSTOM_VIEW);
|
||||
|
||||
ResIterator views = viewModel.listResourcesWithProperty(rdfType,
|
||||
customView);
|
||||
try {
|
||||
Map<String, ViewSpec> map = new HashMap<String, ViewSpec>();
|
||||
while (views.hasNext()) {
|
||||
Resource view = views.next();
|
||||
List<String> contextNames = getDataPropertyValues(view,
|
||||
appliesTo, "context");
|
||||
List<ShortViewContext> contexts = contextsFromNames(view,
|
||||
contextNames);
|
||||
List<String> dataGetterUris = getObjectPropertyValues(view,
|
||||
dataGetter, "data getter");
|
||||
String tn = getDataProperty(view, template);
|
||||
map.put(view.getURI(), new ViewSpec(view.getURI(), contexts,
|
||||
dataGetterUris, tn));
|
||||
}
|
||||
return map;
|
||||
} finally {
|
||||
views.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Got a list of context names. Make sure that each one actually represents
|
||||
* a known context.
|
||||
*/
|
||||
private List<ShortViewContext> contextsFromNames(Resource view,
|
||||
List<String> contextNames) throws ShortViewConfigException {
|
||||
List<ShortViewContext> list = new ArrayList<ShortViewContext>();
|
||||
for (String name : contextNames) {
|
||||
ShortViewContext context = ShortViewContext.fromString(name);
|
||||
if (context == null) {
|
||||
throw new ShortViewConfigException("Unrecognized context '"
|
||||
+ name + "' for view '" + view.getURI() + "'");
|
||||
}
|
||||
list.add(context);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a map of classes to views.
|
||||
*/
|
||||
private Map<String, List<ViewSpec>> createClassMappings(
|
||||
Map<String, ViewSpec> viewSpecsByUri)
|
||||
throws ShortViewConfigException {
|
||||
Property hasView = viewModel.getProperty(HAS_VIEW);
|
||||
|
||||
StmtIterator stmts = viewModel.listStatements(null, hasView,
|
||||
(RDFNode) null);
|
||||
try {
|
||||
Map<String, List<ViewSpec>> map = new HashMap<String, List<ViewSpec>>();
|
||||
while (stmts.hasNext()) {
|
||||
Statement s = stmts.next();
|
||||
|
||||
String classUri = s.getSubject().getURI();
|
||||
|
||||
RDFNode node = s.getObject();
|
||||
if (!node.isResource()) {
|
||||
throw new ShortViewConfigException("The hasCustomView"
|
||||
+ " property for '" + classUri
|
||||
+ "' must be a resource.");
|
||||
}
|
||||
String viewUri = node.asResource().getURI();
|
||||
|
||||
ViewSpec view = viewSpecsByUri.get(viewUri);
|
||||
if (view == null) {
|
||||
throw new ShortViewConfigException("On '" + classUri
|
||||
+ "', the view '" + viewUri + "' does not exist.");
|
||||
}
|
||||
|
||||
if (!map.containsKey(classUri)) {
|
||||
map.put(classUri, new ArrayList<ViewSpec>());
|
||||
}
|
||||
map.get(classUri).add(view);
|
||||
}
|
||||
return map;
|
||||
} finally {
|
||||
stmts.close();
|
||||
}
|
||||
}
|
||||
|
||||
private String getDataProperty(Resource subject, Property predicate)
|
||||
throws ShortViewConfigException {
|
||||
Statement s = viewModel.getProperty(subject, predicate);
|
||||
if (s == null) {
|
||||
throw new ShortViewConfigException("The required property '"
|
||||
+ predicate.getURI() + "' is not present for '"
|
||||
+ subject.getURI() + "'");
|
||||
}
|
||||
RDFNode node = s.getObject();
|
||||
if (!node.isLiteral())
|
||||
throw new ShortViewConfigException("The value of '"
|
||||
+ predicate.getURI() + "' for '" + subject.getURI()
|
||||
+ "' must be a literal.");
|
||||
return node.asLiteral().getString();
|
||||
}
|
||||
|
||||
private List<String> getDataPropertyValues(Resource subject,
|
||||
Property predicate, String label) throws ShortViewConfigException {
|
||||
StmtIterator stmts = viewModel.listStatements(subject, predicate,
|
||||
(RDFNode) null);
|
||||
try {
|
||||
List<String> list = new ArrayList<String>();
|
||||
while (stmts.hasNext()) {
|
||||
Statement stmt = stmts.next();
|
||||
RDFNode node = stmt.getObject();
|
||||
if (!node.isLiteral()) {
|
||||
throw new ShortViewConfigException("The " + label
|
||||
+ " property for '" + subject.getURI()
|
||||
+ "' must be a literal.");
|
||||
}
|
||||
list.add(node.asLiteral().getString());
|
||||
}
|
||||
return list;
|
||||
} finally {
|
||||
stmts.close();
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getObjectPropertyValues(Resource subject,
|
||||
Property predicate, String label) throws ShortViewConfigException {
|
||||
StmtIterator stmts = viewModel.listStatements(subject, predicate,
|
||||
(RDFNode) null);
|
||||
try {
|
||||
List<String> list = new ArrayList<String>();
|
||||
while (stmts.hasNext()) {
|
||||
Statement stmt = stmts.next();
|
||||
RDFNode node = stmt.getObject();
|
||||
if (!node.isResource()) {
|
||||
throw new ShortViewConfigException("The " + label
|
||||
+ " property for '" + subject.getURI()
|
||||
+ "' must be a resource.");
|
||||
}
|
||||
list.add(node.asResource().getURI());
|
||||
}
|
||||
return list;
|
||||
} finally {
|
||||
stmts.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the template name and DataGetter instances associated with this
|
||||
|
@ -48,42 +288,42 @@ public class FakeApplicationOntologyService {
|
|||
*/
|
||||
public TemplateAndDataGetters getShortViewProperties(VitroRequest vreq,
|
||||
Individual individual, String classUri, String contextName) {
|
||||
/*
|
||||
* If we have a mapping for this class that applies to this context,
|
||||
* construct the DataGetter instances and return them with the template.
|
||||
*/
|
||||
if (classUriToViewSpecs.containsKey(classUri)) {
|
||||
for (ViewSpec view : classUriToViewSpecs.get(classUri)) {
|
||||
for (ShortViewContext context : view.getContexts()) {
|
||||
if (context.name().equalsIgnoreCase(contextName)) {
|
||||
List<DataGetter> dgList = new ArrayList<DataGetter>();
|
||||
for (String dgUri : view.getDataGetterUris()) {
|
||||
dgList.add(new SparqlQueryDataGetter(vreq,
|
||||
viewModel, dgUri));
|
||||
}
|
||||
return new TemplateAndDataGetters(
|
||||
view.getTemplateName(),
|
||||
dgList.toArray(new DataGetter[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise, check for this hard-coded kluge. Any class in the People
|
||||
* class group gets a special view with preferred title.
|
||||
*/
|
||||
if ((BROWSE.name().equals(contextName))
|
||||
&& (isClassInPeopleClassGroup(vreq.getWebappDaoFactory(),
|
||||
classUri))) {
|
||||
return new TemplateAndDataGetters("view-browse-people.ftl",
|
||||
new FakeVivoPeopleDataGetter(vreq, individual.getURI()));
|
||||
}
|
||||
// A mockup of Tammy's use case.
|
||||
// if ((SEARCH.name().equals(contextName))
|
||||
// && (classUri.equals(FACULTY_MEMBER_CLASS_URI))) {
|
||||
// return new TemplateAndDataGetters("view-search-faculty.ftl", new
|
||||
// FakeFacultyDataGetter());
|
||||
// } else {
|
||||
|
||||
/*
|
||||
* Otherwise, no custom view.
|
||||
*/
|
||||
return null;
|
||||
// }
|
||||
}
|
||||
|
||||
/** The info associated with a short view. */
|
||||
public static class TemplateAndDataGetters {
|
||||
private final String templateName;
|
||||
private final Set<DataGetter> dataGetters;
|
||||
|
||||
public TemplateAndDataGetters(String templateName,
|
||||
DataGetter... dataGetters) {
|
||||
this.templateName = templateName;
|
||||
this.dataGetters = new HashSet<DataGetter>(
|
||||
Arrays.asList(dataGetters));
|
||||
}
|
||||
|
||||
public String getTemplateName() {
|
||||
return templateName;
|
||||
}
|
||||
|
||||
public Set<DataGetter> getDataGetters() {
|
||||
return dataGetters;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean isClassInPeopleClassGroup(WebappDaoFactory wadf,
|
||||
|
@ -116,18 +356,85 @@ public class FakeApplicationOntologyService {
|
|||
return isPeople;
|
||||
}
|
||||
|
||||
private static class FakeFacultyDataGetter implements DataGetter {
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper classes
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** The info associated with a short view. */
|
||||
public static class TemplateAndDataGetters {
|
||||
private final String templateName;
|
||||
private final Set<DataGetter> dataGetters;
|
||||
|
||||
public TemplateAndDataGetters(String templateName,
|
||||
DataGetter... dataGetters) {
|
||||
this.templateName = templateName;
|
||||
this.dataGetters = new HashSet<DataGetter>(
|
||||
Arrays.asList(dataGetters));
|
||||
}
|
||||
|
||||
public String getTemplateName() {
|
||||
return templateName;
|
||||
}
|
||||
|
||||
public Set<DataGetter> getDataGetters() {
|
||||
return dataGetters;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** The view specifications that we read from the config file. */
|
||||
private class ViewSpec {
|
||||
private final String uri;
|
||||
private final List<ShortViewContext> contexts;
|
||||
private final List<String> dataGetterUris;
|
||||
private final String templateName;
|
||||
|
||||
public ViewSpec(String uri, List<ShortViewContext> contexts,
|
||||
List<String> dataGetterUris, String templateName) {
|
||||
this.uri = uri;
|
||||
this.contexts = contexts;
|
||||
this.dataGetterUris = dataGetterUris;
|
||||
this.templateName = templateName;
|
||||
}
|
||||
|
||||
public List<ShortViewContext> getContexts() {
|
||||
return contexts;
|
||||
}
|
||||
|
||||
public List<String> getDataGetterUris() {
|
||||
return dataGetterUris;
|
||||
}
|
||||
|
||||
public String getTemplateName() {
|
||||
return templateName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getData(Map<String, Object> valueMap) {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
Map<String, Object> extras = new HashMap<String, Object>();
|
||||
extras.put("departmentName", "Department of Redundancy Department");
|
||||
map.put("extra", extras);
|
||||
return map;
|
||||
public String toString() {
|
||||
return "ViewSpec[uri='" + uri + "', contexts=" + contexts
|
||||
+ ", dataGetterUris=" + dataGetterUris + ", templateName='"
|
||||
+ templateName + "']";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A custom exception that says something was wrong with the config file. */
|
||||
public class ShortViewConfigException extends Exception {
|
||||
public ShortViewConfigException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ShortViewConfigException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String PEOPLE_CLASSGROUP_URI = "http://vivoweb.org/ontology#vitroClassGrouppeople";
|
||||
|
||||
/**
|
||||
* A special data getter to support the kluge case of browsing an individual
|
||||
* that belongs to the People class group.
|
||||
*
|
||||
* A SPARQL query data getter that initializes itself from its own private
|
||||
* "display model". The query finds a preferred title for the individual.
|
||||
*/
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
package edu.cornell.mannlib.vitro.webapp.services.shortview;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -31,8 +30,10 @@ public class ShortViewServiceImpl implements ShortViewService {
|
|||
private static final Log log = LogFactory
|
||||
.getLog(ShortViewServiceImpl.class);
|
||||
|
||||
private static final Map<String, Object> EMPTY_MAP = Collections.emptyMap();
|
||||
|
||||
/*
|
||||
* TODO this should use a real connection to the ApplicationOntology to find
|
||||
* the short view to use for each individiual in a given context.
|
||||
*/
|
||||
private final FakeApplicationOntologyService faker;
|
||||
|
||||
public ShortViewServiceImpl(FakeApplicationOntologyService faker) {
|
||||
|
@ -64,6 +65,7 @@ public class ShortViewServiceImpl implements ShortViewService {
|
|||
|
||||
return fps.renderTemplate(templateName, fullModelMap, vreq);
|
||||
} catch (TemplateParsingException e) {
|
||||
log.error(e, e);
|
||||
return "<p>Can't parse the short view template '" + templateName
|
||||
+ "' for " + individual.getName() + "</p>";
|
||||
} catch (Exception e) {
|
||||
|
@ -78,7 +80,7 @@ public class ShortViewServiceImpl implements ShortViewService {
|
|||
ShortViewContext svContext, VitroRequest vreq) {
|
||||
TemplateAndDataGetters tdg = fetchTemplateAndDataGetters(individual,
|
||||
svContext, vreq);
|
||||
Map<String, Object> gotData = runDataGetters(tdg.getDataGetters());
|
||||
Map<String, Object> gotData = runDataGetters(tdg.getDataGetters(), individual);
|
||||
return new TemplateAndSupplementalDataImpl(tdg.getTemplateName(),
|
||||
gotData);
|
||||
}
|
||||
|
@ -113,10 +115,12 @@ public class ShortViewServiceImpl implements ShortViewService {
|
|||
}
|
||||
|
||||
/** Build a data map from the combined results of all data getters. */
|
||||
private Map<String, Object> runDataGetters(Set<DataGetter> dataGetters) {
|
||||
private Map<String, Object> runDataGetters(Set<DataGetter> dataGetters, Individual individual) {
|
||||
Map<String, Object> valueMap = new HashMap<String, Object>();
|
||||
valueMap.put("individualUri", individual.getURI());
|
||||
Map<String, Object> gotData = new HashMap<String, Object>();
|
||||
for (DataGetter dg : dataGetters) {
|
||||
gotData.putAll(dg.getData(EMPTY_MAP));
|
||||
gotData.putAll(dg.getData(valueMap));
|
||||
}
|
||||
return gotData;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import javax.servlet.ServletContext;
|
|||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.services.shortview.FakeApplicationOntologyService.ShortViewConfigException;
|
||||
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
|
||||
|
||||
/**
|
||||
|
@ -20,8 +21,16 @@ public class ShortViewServiceSetup implements ServletContextListener {
|
|||
ServletContext ctx = sce.getServletContext();
|
||||
StartupStatus ss = StartupStatus.getBean(ctx);
|
||||
|
||||
ShortViewServiceImpl svs = new ShortViewServiceImpl(
|
||||
new FakeApplicationOntologyService());
|
||||
FakeApplicationOntologyService faker;
|
||||
try {
|
||||
faker = new FakeApplicationOntologyService(ctx);
|
||||
} catch (ShortViewConfigException e) {
|
||||
ss.warning(this, "Failed to load the shortview_config.n3 file -- "
|
||||
+ e.getMessage(), e);
|
||||
faker = new FakeApplicationOntologyService();
|
||||
}
|
||||
|
||||
ShortViewServiceImpl svs = new ShortViewServiceImpl(faker);
|
||||
ctx.setAttribute(ATTRIBUTE_NAME, svs);
|
||||
|
||||
ss.info(this,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue