diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/TestController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/TestController.java index 6179c16a9..34ada0cc4 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/TestController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/TestController.java @@ -2,21 +2,15 @@ package edu.cornell.mannlib.vitro.webapp.controller.freemarker; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; import java.util.HashMap; -import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import edu.cornell.mannlib.vitro.webapp.beans.Portal; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; -import freemarker.template.Configuration; /** * Freemarker controller and template sandbox. diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/CollatedObjectPropertyTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/CollatedObjectPropertyTemplateModel.java index 60fbd3e60..523984ffd 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/CollatedObjectPropertyTemplateModel.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/CollatedObjectPropertyTemplateModel.java @@ -12,6 +12,7 @@ import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -26,7 +27,6 @@ import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; public class CollatedObjectPropertyTemplateModel extends ObjectPropertyTemplateModel { private static final Log log = LogFactory.getLog(CollatedObjectPropertyTemplateModel.class); - private static final String DEFAULT_CONFIG_FILE = "listViewConfig-default-collated.xml"; private static final Pattern SELECT_SUBCLASS_PATTERN = // SELECT ?subclass Pattern.compile("SELECT[^{]*\\?subclass\\b", Pattern.CASE_INSENSITIVE); @@ -34,19 +34,42 @@ public class CollatedObjectPropertyTemplateModel extends ObjectPropertyTemplateM // ORDER BY DESC(?subclass) private static final Pattern ORDER_BY_SUBCLASS_PATTERN = Pattern.compile("ORDER\\s+BY\\s+(DESC\\s*\\(\\s*)?\\?subclass", Pattern.CASE_INSENSITIVE); + + private static enum ConfigError { + NO_QUERY("Missing query specification"), + NO_SUBCLASS_SELECT("Query does not select a subclass variable"), + NO_SUBCLASS_ORDER_BY("Query does not sort first by subclass variable"); + + String message; + + ConfigError(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public String toString() { + return getMessage(); + } + } private SortedMap> subclasses; CollatedObjectPropertyTemplateModel(ObjectProperty op, Individual subject, VitroRequest vreq, EditingPolicyHelper policyHelper) - throws InvalidConfigurationException { + throws InvalidCollatedPropertyConfigurationException { super(op, subject, vreq, policyHelper); - String invalidConfigMessage = checkConfiguration(); - if ( ! invalidConfigMessage.isEmpty() ) { - throw new InvalidConfigurationException("Invalid configuration for collated property " + - op.getURI() + ":" + invalidConfigMessage + ". Creating uncollated display instead."); + // RY It would be more efficient to check for these errors in the super constructor, so that we don't + // go through the rest of that constructor before throwing an error. In that case, the subclasses + // could each have their own checkConfiguration() method. + ConfigError configError = checkConfiguration(); + if ( configError != null ) { + throw new InvalidCollatedPropertyConfigurationException("Invalid configuration for collated property " + + op.getURI() + ":" + configError + ". Creating uncollated display instead."); } /* Get the data */ @@ -78,22 +101,26 @@ public class CollatedObjectPropertyTemplateModel extends ObjectPropertyTemplateM } } - private String checkConfiguration() { + private ConfigError checkConfiguration() { String queryString = getQueryString(); - Matcher m; + if (StringUtils.isBlank(queryString)) { + return ConfigError.NO_QUERY; + } + + Matcher m; m = SELECT_SUBCLASS_PATTERN.matcher(queryString); if ( ! m.find() ) { - return("Query does not select a subclass variable."); + return ConfigError.NO_SUBCLASS_SELECT; } m = ORDER_BY_SUBCLASS_PATTERN.matcher(queryString); if ( ! m.find() ) { - return("Query does not sort first by subclass variable."); + return ConfigError.NO_SUBCLASS_ORDER_BY; } - return ""; + return null; } private Map> collate(String subjectUri, String propertyUri, @@ -133,11 +160,6 @@ public class CollatedObjectPropertyTemplateModel extends ObjectPropertyTemplateM } return subclassName; } - - @Override - protected String getDefaultConfigFileName() { - return DEFAULT_CONFIG_FILE; - } /* Access methods for templates */ diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyStatementTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyStatementTemplateModel.java index 20cf782a1..77c4ac985 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyStatementTemplateModel.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyStatementTemplateModel.java @@ -41,16 +41,13 @@ public class ObjectPropertyStatementTemplateModel extends PropertyStatementTempl /** * This method handles the special case where we are creating a DataPropertyStatementTemplateModel * outside the GroupedPropertyList. Specifically, it allows vitro:primaryLink and vitro:additionalLink - * to be treated like data property statements and thus have editing links. (In a future version, + * to be treated like object property statements and thus have editing links. (In a future version, * these properties will be replaced by vivo core ontology properties.) It could potentially be used * for other properties outside the property list as well. */ ObjectPropertyStatementTemplateModel(String subjectUri, String propertyUri, VitroRequest vreq, EditingPolicyHelper policyHelper) { - super(subjectUri, propertyUri, policyHelper); - - ObjectPropertyStatementDao opsDao = vreq.getWebappDaoFactory().getObjectPropertyStatementDao(); - + super(subjectUri, propertyUri, policyHelper); } private void setEditAccess(EditingPolicyHelper policyHelper) { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyTemplateModel.java index 5adfbaa55..ec6cc815e 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyTemplateModel.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyTemplateModel.java @@ -69,6 +69,26 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel // ?subject ?property ?\w+ Pattern.compile("\\?" + KEY_SUBJECT + "\\s+\\?" + KEY_PROPERTY + "\\s+\\?(\\w+)"); + private static enum ConfigError { + NO_QUERY("Missing query specification"), + NO_TEMPLATE("Missing template specification"), + TEMPLATE_NOT_FOUND("Specified template does not exist"); + + String message; + + ConfigError(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public String toString() { + return getMessage(); + } + } + private PropertyListConfig config; private String objectKey; @@ -76,9 +96,13 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel private boolean addAccess = false; ObjectPropertyTemplateModel(ObjectProperty op, Individual subject, VitroRequest vreq, EditingPolicyHelper policyHelper) { - super(op, subject, policyHelper); - setName(op.getDomainPublic()); + super(op, subject, policyHelper); + + log.debug("Creating template model for object property " + op.getURI()); + + setName(op.getDomainPublic()); + // Get the config for this object property try { config = new PropertyListConfig(op, vreq); @@ -140,7 +164,7 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel if (op.getCollateBySubclass()) { try { return new CollatedObjectPropertyTemplateModel(op, subject, vreq, policyHelper); - } catch (InvalidConfigurationException e) { + } catch (InvalidCollatedPropertyConfigurationException e) { log.warn(e.getMessage()); return new UncollatedObjectPropertyTemplateModel(op, subject, vreq, policyHelper); } @@ -225,12 +249,9 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel if (dateTimeEnd == null) { // If the first statement has a null end datetime, all subsequent statements in the list also do, // so there is nothing to reorder. - // NB This assumption is FALSE if the query orders by subclass but the property is not collated. - // This happens when a query is written with a subclass variable to support turning on collation - // in the back end. - // if (statements.indexOf(stmt) == 0) { - // break; - // } + if (statements.indexOf(stmt) == 0) { + break; + } tempList.add(stmt); iterator.remove(); } @@ -244,12 +265,13 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel return objectKey; } - protected abstract String getDefaultConfigFileName(); - - private class PropertyListConfig { - + private class PropertyListConfig { + private static final String CONFIG_FILE_PATH = "/config/"; - private static final String NODE_NAME_QUERY = "query"; + private static final String DEFAULT_CONFIG_FILE_NAME = "listViewConfig-default.xml"; + + private static final String NODE_NAME_QUERY_BASE = "query-base"; + private static final String NODE_NAME_QUERY_COLLATED = "query-collated"; private static final String NODE_NAME_TEMPLATE = "template"; private static final String NODE_NAME_POSTPROCESSOR = "postprocessor"; @@ -258,27 +280,26 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel private String templateName; private String postprocessor; - PropertyListConfig(ObjectProperty op, VitroRequest vreq) throws Exception { + PropertyListConfig(ObjectProperty op, VitroRequest vreq) { // Get the custom config filename - WebappDaoFactory wdf = vreq.getWebappDaoFactory(); - ObjectPropertyDao opDao = wdf.getObjectPropertyDao(); - String configFileName = opDao.getCustomListViewConfigFileName(op); + String configFileName = vreq.getWebappDaoFactory().getObjectPropertyDao().getCustomListViewConfigFileName(op); if (configFileName == null) { // no custom config; use default config - configFileName = getDefaultConfigFileName(); + configFileName = DEFAULT_CONFIG_FILE_NAME; } log.debug("Using list view config file " + configFileName + " for object property " + op.getURI()); String configFilePath = getConfigFilePath(configFileName); + try { File config = new File(configFilePath); if ( ! isDefaultConfig(configFileName) && ! config.exists() ) { log.warn("Can't find config file " + configFilePath + " for object property " + op.getURI() + "\n" + ". Using default config file instead."); - configFilePath = getConfigFilePath(getDefaultConfigFileName()); + configFilePath = getConfigFilePath(DEFAULT_CONFIG_FILE_NAME); // Should we test for the existence of the default, and throw an error if it doesn't exist? } - setValuesFromConfigFile(configFilePath); + setValuesFromConfigFile(configFilePath, op); } catch (Exception e) { log.error("Error processing config file " + configFilePath + " for object property " + op.getURI(), e); @@ -286,13 +307,18 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel } if ( ! isDefaultConfig(configFileName) ) { - String invalidConfigMessage = checkForInvalidConfig(vreq); - if ( StringUtils.isNotEmpty(invalidConfigMessage) ) { + ConfigError configError = checkForInvalidConfig(vreq); + // If the configuration contains an error, use the default configuration. + // Exception: a collated property with a missing collated query. This will + // be caught in CollatedObjectPropertyTemplateModel constructor and result + // in using an UncollatedObjectPropertyTemplateModel instead. + if ( configError != null && + ! (configError == ConfigError.NO_QUERY && op.getCollateBySubclass()) ) { log.warn("Invalid list view config for object property " + op.getURI() + " in " + configFilePath + ":\n" + - invalidConfigMessage + " Using default config instead."); - configFilePath = getConfigFilePath(getDefaultConfigFileName()); - setValuesFromConfigFile(configFilePath); + configError + " Using default config instead."); + configFilePath = getConfigFilePath(DEFAULT_CONFIG_FILE_NAME); + setValuesFromConfigFile(configFilePath, op); } } @@ -300,31 +326,31 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel } private boolean isDefaultConfig(String configFileName) { - return configFileName.equals(getDefaultConfigFileName()); + return configFileName.equals(DEFAULT_CONFIG_FILE_NAME); } - private String checkForInvalidConfig(VitroRequest vreq) { - String invalidConfigMessage = null; + private ConfigError checkForInvalidConfig(VitroRequest vreq) { + ConfigError error = null; if ( StringUtils.isBlank(queryString)) { - invalidConfigMessage = "Missing query specification."; + error = ConfigError.NO_QUERY; } else if ( StringUtils.isBlank(templateName)) { - invalidConfigMessage = "Missing template specification."; + error = ConfigError.NO_TEMPLATE; } else { Configuration fmConfig = (Configuration) vreq.getAttribute("freemarkerConfig"); TemplateLoader tl = fmConfig.getTemplateLoader(); try { if ( tl.findTemplateSource(templateName) == null ) { - invalidConfigMessage = "Specified template " + templateName + " does not exist."; + error = ConfigError.TEMPLATE_NOT_FOUND; } } catch (IOException e) { log.error("Error finding template " + templateName, e); } } - return invalidConfigMessage; + return error; } - private void setValuesFromConfigFile(String configFilePath) { + private void setValuesFromConfigFile(String configFilePath, ObjectProperty op) { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db; @@ -332,8 +358,11 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel try { db = dbf.newDocumentBuilder(); Document doc = db.parse(configFilePath); + // Required values - queryString = getConfigValue(doc, NODE_NAME_QUERY); + String queryNodeName = op.getCollateBySubclass() ? NODE_NAME_QUERY_COLLATED : NODE_NAME_QUERY_BASE; + log.debug("Using query element " + queryNodeName + " for object property " + op.getURI()); + queryString = getConfigValue(doc, queryNodeName); templateName = getConfigValue(doc, NODE_NAME_TEMPLATE); // Optional values @@ -362,11 +391,11 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel } } - protected class InvalidConfigurationException extends Exception { + protected class InvalidCollatedPropertyConfigurationException extends Exception { private static final long serialVersionUID = 1L; - protected InvalidConfigurationException(String s) { + protected InvalidCollatedPropertyConfigurationException(String s) { super(s); } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/UncollatedObjectPropertyTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/UncollatedObjectPropertyTemplateModel.java index 7fbf317e2..fec08152a 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/UncollatedObjectPropertyTemplateModel.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/UncollatedObjectPropertyTemplateModel.java @@ -18,7 +18,6 @@ import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; public class UncollatedObjectPropertyTemplateModel extends ObjectPropertyTemplateModel { private static final Log log = LogFactory.getLog(UncollatedObjectPropertyTemplateModel.class); - private static final String DEFAULT_CONFIG_FILE = "listViewConfig-default-uncollated.xml"; private List statements; @@ -47,11 +46,6 @@ public class UncollatedObjectPropertyTemplateModel extends ObjectPropertyTemplat postprocessStatementList(statements); } - @Override - protected String getDefaultConfigFileName() { - return DEFAULT_CONFIG_FILE; - } - /* Access methods for templates */ public List getStatements() { diff --git a/webapp/web/config/listViewConfig-default-uncollated.xml b/webapp/web/config/listViewConfig-default-uncollated.xml deleted file mode 100644 index e9c5ff27d..000000000 --- a/webapp/web/config/listViewConfig-default-uncollated.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - PREFIX vitro: <http://vitro.mannlib.cornell.edu/ns/vitro/0.7#> - PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> - - SELECT ?object ?name ?moniker { - GRAPH ?g1 { ?subject ?property ?object } - OPTIONAL { GRAPH ?g2 { ?object rdfs:label ?name } } - OPTIONAL { GRAPH ?g3 { ?object vitro:moniker ?moniker } } - } - - - edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.DefaultListViewDataPostProcessor - - - diff --git a/webapp/web/config/listViewConfig-default-collated.xml b/webapp/web/config/listViewConfig-default.xml similarity index 65% rename from webapp/web/config/listViewConfig-default-collated.xml rename to webapp/web/config/listViewConfig-default.xml index cb223304b..446a74a2d 100644 --- a/webapp/web/config/listViewConfig-default-collated.xml +++ b/webapp/web/config/listViewConfig-default.xml @@ -1,12 +1,23 @@ - + See guidelines in vitro/doc/list_view_configuration_guidelines.txt --> - + + PREFIX vitro: <http://vitro.mannlib.cornell.edu/ns/vitro/0.7#> + PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> + + SELECT ?object ?name ?moniker { + GRAPH ?g1 { ?subject ?property ?object } + OPTIONAL { GRAPH ?g2 { ?object rdfs:label ?name } } + OPTIONAL { GRAPH ?g3 { ?object vitro:moniker ?moniker } } + } + + + PREFIX vitro: <http://vitro.mannlib.cornell.edu/ns/vitro/0.7#> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> @@ -18,9 +29,9 @@ FILTER (?g4 != <http://vitro.mannlib.cornell.edu/default/inferred-tbox> && ?g4 != <http://vitro.mannlib.cornell.edu/default/vitro-kb-inf> ) } - } - - + } ORDER BY ?subclass + + edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.DefaultListViewDataPostProcessor diff --git a/webapp/web/config/listViewConfig-vitroLink.xml b/webapp/web/config/listViewConfig-vitroLink.xml index 2a2bdee3f..ab5dbca18 100644 --- a/webapp/web/config/listViewConfig-vitroLink.xml +++ b/webapp/web/config/listViewConfig-vitroLink.xml @@ -6,17 +6,20 @@ See guidelines in vitro/doc/list_view_configuration_guidelines.txt --> - + PREFIX vitro: <http://vitro.mannlib.cornell.edu/ns/vitro/0.7#> PREFIX afn: <http://jena.hpl.hp.com/ARQ/function#> - SELECT ?link (afn:localname(?link) AS ?linkName) ?anchor ?url WHERE { + SELECT ?link + (afn:localname(?link) AS ?linkName) + ?anchor + ?url WHERE { GRAPH ?g1 { ?subject ?property ?link } OPTIONAL { GRAPH ?g2 { ?link vitro:linkAnchor ?anchor } } OPTIONAL { GRAPH ?g3 { ?link vitro:linkURL ?url } } OPTIONAL { GRAPH ?g4 { ?link vitro:linkDisplayRank ?rank } } } ORDER BY ?rank - +