NIHVIVO-1341 Implement preprocessing structure to process object property statement query data before sending to template. Reorganize markup in templates to reduce repetition.

This commit is contained in:
rjy7 2010-12-16 14:57:04 +00:00
parent dbb07aa152
commit b6b3499922
19 changed files with 241 additions and 169 deletions

View file

@ -244,9 +244,18 @@ public class UrlBuilder {
return getPath(route.path(), params);
}
public static String getIndividualProfileUrl(String individualUri, WebappDaoFactory wadf) {
Individual individual = wadf.getIndividualDao().getIndividualByURI(individualUri);
return getIndividualProfileUrl(individual, individualUri, wadf);
}
public static String getIndividualProfileUrl(Individual individual, WebappDaoFactory wadf) {
String profileUrl = null;
String individualUri = individual.getURI();
return getIndividualProfileUrl(individual, individualUri, wadf);
}
private static String getIndividualProfileUrl(Individual individual, String individualUri, WebappDaoFactory wadf) {
String profileUrl = null;
URI uri = new URIImpl(individualUri);
String namespace = uri.getNamespace();
String defaultNamespace = wadf.getDefaultNamespace();

View file

@ -30,5 +30,5 @@ public interface ObjectPropertyStatementDao {
int insertNewObjectPropertyStatement(ObjectPropertyStatement objPropertyStmt );
public List<Map<String, Object>> getObjectPropertyStatementsForIndividualByProperty(String subjectUri, String propertyUri, String query);
public List<Map<String, String>> getObjectPropertyStatementsForIndividualByProperty(String subjectUri, String propertyUri, String query);
}

View file

@ -84,7 +84,7 @@ class ObjectPropertyStatementDaoFiltering extends BaseFiltering implements Objec
@Override
// RY What about filtering?
public List<Map<String, Object>> getObjectPropertyStatementsForIndividualByProperty(
public List<Map<String, String>> getObjectPropertyStatementsForIndividualByProperty(
String subjectUri, String propertyUri, String query) {
return innerObjectPropertyStatementDao.getObjectPropertyStatementsForIndividualByProperty(subjectUri, propertyUri, query);
}

View file

@ -898,9 +898,7 @@ public class ObjectPropertyDaoJena extends PropertyDaoJena implements ObjectProp
ResultSet results = qexec.execSelect();
while (results.hasNext()) {
QuerySolution soln = results.next();
Resource resource = soln.getResource("property");
String uri = resource.getURI();
ObjectProperty prop = getObjectPropertyByURI(uri);
ObjectProperty prop = getObjectPropertyByURI(soln.getResource("property").getURI());
String filename = soln.getLiteral("filename").getLexicalForm();
customListViewConfigFileMap.put(prop, filename);
}

View file

@ -251,7 +251,7 @@ public class ObjectPropertyStatementDaoJena extends JenaBaseDao implements Objec
* custom queries that could request any data in addition to just the object of the statement.
* However, we do need to get the object of the statement so that we have it to create editing links.
*/
public List<Map<String, Object>> getObjectPropertyStatementsForIndividualByProperty(String subjectUri, String propertyUri, String queryString) {
public List<Map<String, String>> getObjectPropertyStatementsForIndividualByProperty(String subjectUri, String propertyUri, String queryString) {
log.debug("Object property query string: " + queryString);
@ -269,47 +269,16 @@ public class ObjectPropertyStatementDaoJena extends JenaBaseDao implements Objec
// Run the SPARQL query to get the properties
QueryExecution qexec = QueryExecutionFactory.create(query, getOntModelSelector().getFullModel(), bindings);
return executeQueryToObjectValueCollection(qexec);
}
protected List<Map<String, Object>> executeQueryToObjectValueCollection(
QueryExecution qexec) {
List<Map<String, Object>> rv = new ArrayList<Map<String, Object>>();
ResultSet results = qexec.execSelect();
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
while (results.hasNext()) {
QuerySolution soln = results.nextSolution();
rv.add(querySolutionToObjectValueMap(soln));
list.add(QueryUtils.querySolutionToStringValueMap(soln));
}
return rv;
return list;
}
protected Map<String,Object> querySolutionToObjectValueMap( QuerySolution soln){
Map<String,Object> map = new HashMap<String,Object>();
Iterator<String> varNames = soln.varNames();
while(varNames.hasNext()){
String varName = varNames.next();
map.put(varName, nodeToObject( soln.get(varName)));
}
return map;
}
protected Object nodeToObject( RDFNode node ){
if( node == null ){
return "";
}else if( node.isLiteral() ){
Literal literal = node.asLiteral();
//return literal.getValue();
return literal.getLexicalForm();
}else if( node.isURIResource() ){
Resource resource = node.asResource();
// See notes in ObjectPropertyStatementTemplateModel about why we are returning the individual
// here instead of just the URI.
return getWebappDaoFactory().getIndividualDao().getIndividualByURI(resource.getURI());
}else if( node.isAnon() ){
Resource resource = node.asResource();
return resource.getId().getLabelString(); //get b-node id
}else{
return "";
}
}
}

View file

@ -0,0 +1,78 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.dao.jena;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.hp.hpl.jena.query.QueryExecution;
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;
/**
* Utilities for executing queries and working with query results.
*
*/
public class QueryUtils {
protected static Map<String,Object> querySolutionToObjectValueMap( QuerySolution soln){
Map<String,Object> map = new HashMap<String,Object>();
Iterator<String> varNames = soln.varNames();
while(varNames.hasNext()){
String varName = varNames.next();
map.put(varName, nodeToObject( soln.get(varName)));
}
return map;
}
protected static Map<String,String> querySolutionToStringValueMap( QuerySolution soln ){
Map<String,String> map = new HashMap<String,String>();
Iterator<String> varNames = soln.varNames();
while(varNames.hasNext()){
String varName = varNames.next();
map.put(varName, nodeToString( soln.get(varName)));
}
return map;
}
protected static Object nodeToObject( RDFNode node ){
if( node == null ){
return "";
}else if( node.isLiteral() ){
Literal literal = node.asLiteral();
return literal.getValue();
}else if( node.isURIResource() ){
Resource resource = node.asResource();
return resource.getURI();
}else if( node.isAnon() ){
Resource resource = node.asResource();
return resource.getId().getLabelString(); //get b-node id
}else{
return "";
}
}
protected static String nodeToString( RDFNode node ){
if( node == null ){
return "";
}else if( node.isLiteral() ){
Literal literal = node.asLiteral();
return literal.getLexicalForm();
}else if( node.isURIResource() ){
Resource resource = node.asResource();
return resource.getURI();
}else if( node.isAnon() ){
Resource resource = node.asResource();
return resource.getId().getLabelString(); //get b-node id
}else{
return "";
}
}
}

View file

@ -22,7 +22,7 @@ public class CollatedObjectPropertyTemplateModel extends ObjectPropertyTemplateM
private static final Log log = LogFactory.getLog(CollatedObjectPropertyTemplateModel.class);
private Map<String, List<ObjectPropertyStatementTemplateModel>> collatedStatements;
private Map<String, List<ObjectPropertyStatementTemplateModel>> subclasses;
//private List<SubclassList> subclassList;
CollatedObjectPropertyTemplateModel(ObjectProperty op, Individual subject, WebappDaoFactory wdf) throws Exception {
@ -53,16 +53,16 @@ public class CollatedObjectPropertyTemplateModel extends ObjectPropertyTemplateM
ObjectPropertyStatementDao opDao = wdf.getObjectPropertyStatementDao();
String subjectUri = subject.getURI();
String propertyUri = op.getURI();
List<Map<String, Object>> statementData = opDao.getObjectPropertyStatementsForIndividualByProperty(subjectUri, propertyUri, getQueryString());
collatedStatements = new HashMap<String, List<ObjectPropertyStatementTemplateModel>>(statementData.size());
List<Map<String, String>> statementData = opDao.getObjectPropertyStatementsForIndividualByProperty(subjectUri, propertyUri, getQueryString());
subclasses = new HashMap<String, List<ObjectPropertyStatementTemplateModel>>(statementData.size());
// for (Map<String, Object> map : statementData) {
// statements.add(new ObjectPropertyStatementTemplateModel(subjectUri, propertyUri, map, wdf));
// }
if (statementData.size() > 0) {
String collationTarget = getCollationTarget();
List<VClass> vclasses = getDirectVClasses(collationTarget, statementData);
}
// if (statementData.size() > 0) {
// String collationTarget = getCollationTarget();
// List<VClass> vclasses = getDirectVClasses(collationTarget, statementData);
// }
}
@ -92,7 +92,7 @@ public class CollatedObjectPropertyTemplateModel extends ObjectPropertyTemplateM
/* Access methods for templates */
public Map<String, List<ObjectPropertyStatementTemplateModel>> getCollatedStatements() {
return collatedStatements;
return subclasses;
}
@Override

View file

@ -19,24 +19,12 @@ public class ObjectPropertyStatementTemplateModel extends BaseTemplateModel {
private String subjectUri; // we'll use these to make the edit links
private String propertyUri;
private Map<String, Object> data;
private WebappDaoFactory wdf;
private Map<String, String> data;
ObjectPropertyStatementTemplateModel(String subjectUri, String propertyUri, Map<String, Object> data, WebappDaoFactory wdf) {
ObjectPropertyStatementTemplateModel(String subjectUri, String propertyUri, Map<String, String> data) {
this.subjectUri = subjectUri;
this.propertyUri = propertyUri;
this.wdf = wdf;
this.data = new HashMap<String, Object>(data.size());
// See comments above the StatementObject class definition on why we don't just set this.data = data.
for (String key : data.keySet()) {
Object value = data.get(key);
if (value instanceof Individual) {
Individual i = (Individual) value;
this.data.put(key, new StatementObject(i));
} else {
this.data.put(key, value);
}
}
this.data = data;
}
/* This is a hopefully temporary solution to account for the fact that in the default
@ -75,28 +63,7 @@ public class ObjectPropertyStatementTemplateModel extends BaseTemplateModel {
* object getProfileUrl(String uri), so in the template we call ${statement.profileUrl(object)} (but still the semantics
* is a bit weird, since the profile url doesn't belong to the statement).
*/
public class StatementObject {
private Individual individual;
StatementObject(Individual individual) {
this.individual = individual;
}
/* Access methods for templates */
public String getName() {
return individual.getName();
}
public String getMoniker() {
return individual.getMoniker();
}
public String getUrl() {
return UrlBuilder.getIndividualProfileUrl(individual, wdf);
}
}
/* Access methods for templates */

View file

@ -3,6 +3,8 @@
package edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual;
import java.io.File;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@ -15,6 +17,7 @@ import org.w3c.dom.NodeList;
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyDao;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
@ -45,6 +48,11 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel
return config.collationTarget;
}
protected String getLinkTarget() {
return config.linkTarget;
}
protected static ObjectPropertyTemplateModel getObjectPropertyTemplateModel(ObjectProperty op, Individual subject, WebappDaoFactory wdf) {
if (op.getCollateBySubclass()) {
try {
@ -58,6 +66,23 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel
}
}
/** Applies preprocessing to query results to prepare for template */
protected void preprocess(List<Map<String, String>> data, WebappDaoFactory wdf) {
/* First apply standard post-processing for all object properties */
// Add urls for link targets to the data
String linkTarget = config.linkTarget;
for (Map<String, String> map : data) {
String targetUri = map.get(linkTarget);
if (targetUri != null) {
String targetUrl = UrlBuilder.getIndividualProfileUrl(targetUri, wdf);
map.put(linkTarget + "Url", targetUrl);
}
}
/* Then apply custom post-processing specified in config */
}
private class PropertyListConfig {
private static final String DEFAULT_CONFIG_FILE = "objectPropertyList-default.xml";
@ -65,10 +90,14 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel
private static final String NODE_NAME_QUERY = "query";
private static final String NODE_NAME_TEMPLATE = "template";
private static final String NODE_NAME_COLLATION_TARGET = "collation-target";
private static final String NODE_NAME_LINK_TARGET = "link-target";
private static final String NODE_NAME_PREPROCESSOR = "preprocessor";
private String queryString;
private String templateName;
private String collationTarget;
private String linkTarget; // we could easily make this a list if we ever want multiple links
private String preprocessor;
PropertyListConfig(ObjectProperty op, WebappDaoFactory wdf) throws Exception {
@ -93,9 +122,13 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(configFilePath);
// Required values
queryString = getConfigValue(doc, NODE_NAME_QUERY);
templateName = getConfigValue(doc, NODE_NAME_TEMPLATE);
// Optional values
collationTarget = getConfigValue(doc, NODE_NAME_COLLATION_TARGET);
linkTarget = getConfigValue(doc, NODE_NAME_LINK_TARGET); // if this is null, no link will be generated
preprocessor = getConfigValue(doc, NODE_NAME_PREPROCESSOR);
} catch (Exception e) {
log.error("Error processing config file " + configFilePath + " for object property " + op.getURI(), e);
// What should we do here?

View file

@ -1,41 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.BaseTemplateModel;
/** List of object property statements for an individual, where the objects belong to a single subclass **/
// RY ** This can probably be eliminated. It can be a map of subclass name to statements.
public class SubclassList extends BaseTemplateModel {
private static final Log log = LogFactory.getLog(SubclassList.class);
String name;
List<ObjectPropertyStatementTemplateModel> statements;
SubclassList(String name) {
this.name = name;
this.statements = new ArrayList<ObjectPropertyStatementTemplateModel>();
// get the obj property statements for this subclass from the db via a sparql query
}
/* Access methods for templates */
public String getName() {
return name;
}
public List<ObjectPropertyStatementTemplateModel> getStatements() {
return statements;
}
}

View file

@ -25,10 +25,11 @@ public class UncollatedObjectPropertyTemplateModel extends ObjectPropertyTemplat
ObjectPropertyStatementDao opDao = wdf.getObjectPropertyStatementDao();
String subjectUri = subject.getURI();
String propertyUri = op.getURI();
List<Map<String, Object>> statementData = opDao.getObjectPropertyStatementsForIndividualByProperty(subjectUri, propertyUri, getQueryString());
List<Map<String, String>> statementData = opDao.getObjectPropertyStatementsForIndividualByProperty(subjectUri, propertyUri, getQueryString());
preprocess(statementData, wdf);
statements = new ArrayList<ObjectPropertyStatementTemplateModel>(statementData.size());
for (Map<String, Object> map : statementData) {
statements.add(new ObjectPropertyStatementTemplateModel(subjectUri, propertyUri, map, wdf));
for (Map<String, String> map : statementData) {
statements.add(new ObjectPropertyStatementTemplateModel(subjectUri, propertyUri, map));
}
}

View file

@ -0,0 +1,13 @@
package edu.cornell.mannlib.vitro.webapp.web.templatemodels.preprocessors;
import java.util.List;
import java.util.Map;
public abstract class BaseObjectPropertyDataPreprocessor implements
ObjectPropertyDataPreprocessor {
@Override
public abstract void preprocess(List<Map<String, String>> data);
}

View file

@ -0,0 +1,17 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.web.templatemodels.preprocessors;
import java.util.List;
import java.util.Map;
public class DefaultObjectPropertyDataPreprocessor extends
BaseObjectPropertyDataPreprocessor {
@Override
public void preprocess(List<Map<String, String>> data) {
// TODO Auto-generated method stub
}
}

View file

@ -0,0 +1,20 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.web.templatemodels.preprocessors;
import java.util.List;
import java.util.Map;
/**
* These preprocessors take a list of object property statement data derived from a
* SPARQL query (or other source) and prepare it for insertion into the template data model.
*
* @author rjy7
*
*/
public interface ObjectPropertyDataPreprocessor {
public void preprocess(List<Map<String, String>> data);
}

View file

@ -2,12 +2,21 @@
<!-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<view-config>
<query>
SELECT ?object {
PREFIX vitro: &lt;http://vitro.mannlib.cornell.edu/ns/vitro/0.7#&gt;
PREFIX rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#&gt;
SELECT ?object ?name ?moniker {
?subject ?property ?object .
OPTIONAL { ?object rdfs:label ?name . }
OPTIONAL { ?object vitro:moniker ?moniker . }
}
</query>
<collation-target>object</collation-target>
<template>objectPropertyList-default.ftl</template>
<link-target>object</link-target>
<preprocessor></preprocessor>
<template>shortView-default.ftl</template>
</view-config>

View file

@ -2,10 +2,6 @@
<#-- Template for property listing on individual profile page -->
<#-- RY Just a temporary fix to prevent classgroup heading from being pushed to the right edge of the page.
Feel free to redo/remove. -->
<#--><div style="clear: both;" />-->
<#list propertyGroups as group>
<#-- Get the group name -->
@ -17,7 +13,7 @@ Feel free to redo/remove. -->
<#assign groupName = "other">
</#if>
<#else>
<#-- If there are no groups, a dummy group has been created with a null name. -->
<#-- If there are no groups, a dummy group has been created with a null (as opposed to empty) name. -->
<#assign groupName = "">
</#if>
@ -28,32 +24,27 @@ Feel free to redo/remove. -->
<h2><a name="${groupName}"></a>${groupName}</h2
</#if>
<#-- Now list the properties in the group -->
<#list group.properties as property>
<#-- List the properties in the group -->
<#list group.properties as property>
<article class="property-group" role="article">
<#-- Property display name -->
<h3>${property.name}</h3>
<#-- List the statements for each property -->
<#if property.type == "data"> <#-- data property -->
<#-- data property -->
<#if property.type == "data">
<#list property.statements as statement>
<p class="data-property">${statement.value}</p>
<!-- end data-prop-stmt-value -->
</#list>
<#else> <#-- object property -->
<#if property.collatedBySubclass>
<#include "objectPropertyList-collated.ftl">
<#else>
<ul class="object-property" role="list">
<#include "${property.template}">
</ul> <!-- end obj-prop-stmt-obj -->
</#if>
<#-- object property -->
<#elseif property.collatedBySubclass>
<#include "objectPropertyList-collated.ftl">
<#else>
<#include "objectPropertyList-statements.ftl">
</#if>
<!-- end property -->
</article>
</#list>
<!-- end properties -->
</section> <!-- end property-group -->
</article>
</#list>
</section>
</#list>

View file

@ -1,7 +0,0 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Default object property list template -->
<#list property.statements as statement>
<li role="listitem"><a href="${statement.object.url}">${statement.object.name}</a> | ${statement.object.moniker!}</li>
</#list>

View file

@ -0,0 +1,10 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Template to list statements for an object property -->
<ul class="object-property" role="list">
<#list property.statements as statement>
<li role="listitem">
<#include "${property.template}">
</li>
</#list>
</ul>

View file

@ -0,0 +1,5 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Default object property statement template -->
<a href="${statement.objectUrl}">${statement.name!"object name here"}</a> ${statement.moniker!"moniker here"}