diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/EntityMergedPropertyListController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/EntityMergedPropertyListController.java index a30cb3ab4..259dea21c 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/EntityMergedPropertyListController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/EntityMergedPropertyListController.java @@ -573,7 +573,11 @@ public class EntityMergedPropertyListController extends VitroHttpServlet { for (ObjectPropertyStatement statement : statements) { Individual object = statement.getObject(); - relatedStatements.add(object.getObjectPropertyStatements(op).get(0)); + List statementsForObject = object.getObjectPropertyStatements(op); + // Could be empty for statements with no linked individual. + if ( ! statementsForObject.isEmpty() ) { + relatedStatements.add(statementsForObject.get(0)); + } } return relatedStatements; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/BaseObjectPropertyDataPostProcessor.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/BaseObjectPropertyDataPostProcessor.java index a1ca1fdf4..3b013cb66 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/BaseObjectPropertyDataPostProcessor.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/BaseObjectPropertyDataPostProcessor.java @@ -22,7 +22,9 @@ public abstract class BaseObjectPropertyDataPostProcessor implements private static String KEY_SUBJECT = "subject"; private static final String KEY_PROPERTY = "property"; private static final String DEFAULT_LIST_VIEW_QUERY_OBJECT_VARIABLE_NAME = "object"; - private static final Pattern QUERY_PATTERN = Pattern.compile("\\?" + KEY_SUBJECT + "\\s+\\?" + KEY_PROPERTY + "\\s+\\?(\\w+)"); + private static final Pattern SUBJECT_PROPERTY_OBJECT_PATTERN = + // ?subject ?property ?\w+ + Pattern.compile("\\?" + KEY_SUBJECT + "\\s+\\?" + KEY_PROPERTY + "\\s+\\?(\\w+)"); protected ObjectPropertyTemplateModel objectPropertyTemplateModel; protected WebappDaoFactory wdf; @@ -41,6 +43,7 @@ public abstract class BaseObjectPropertyDataPostProcessor implements } removeDuplicates(data); + for (Map map : data) { process(map); } @@ -89,7 +92,7 @@ public abstract class BaseObjectPropertyDataPostProcessor implements ", so query object = '" + object + "'"); } else { String queryString = objectPropertyTemplateModel.getQueryString(); - Matcher m = QUERY_PATTERN.matcher(queryString); + Matcher m = SUBJECT_PROPERTY_OBJECT_PATTERN.matcher(queryString); if (m.find()) { object = m.group(1); log.debug("Query object for property " + objectPropertyTemplateModel.getUri() + " = '" + object + "'"); @@ -99,7 +102,7 @@ public abstract class BaseObjectPropertyDataPostProcessor implements return object; } - /* Postprocessor helper methods callable from any postprocessor */ + /* Postprocessor methods callable from any postprocessor */ protected void addName(Map map, String nameKey, String objectKey) { String name = map.get(nameKey); @@ -124,4 +127,8 @@ public abstract class BaseObjectPropertyDataPostProcessor implements protected Individual getIndividual(String uri) { return wdf.getIndividualDao().getIndividualByURI(uri); } + + protected static void moveNullEndDateTimesToTop(List list) { + + } } 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 b5ebb3605..517037c27 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 @@ -27,8 +27,13 @@ public class CollatedObjectPropertyTemplateModel extends ObjectPropertyTemplateM 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_QUERY_PATTERN = Pattern.compile("SELECT[^{]*\\?subclass\\b", Pattern.CASE_INSENSITIVE); - private static final Pattern ORDER_BY_QUERY_PATTERN = Pattern.compile("ORDER\\s+BY\\s+(DESC\\s*\\(\\s*)?\\?subclass", Pattern.CASE_INSENSITIVE); + private static final Pattern SELECT_SUBCLASS_PATTERN = + // SELECT ?subclass + Pattern.compile("SELECT[^{]*\\?subclass\\b", Pattern.CASE_INSENSITIVE); + // ORDER BY ?subclass + // 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 SortedMap> subclasses; @@ -65,7 +70,11 @@ public class CollatedObjectPropertyTemplateModel extends ObjectPropertyTemplateM return o1.compareTo(o2); }}; subclasses = new TreeMap>(comparer); - subclasses.putAll(unsortedSubclasses); + subclasses.putAll(unsortedSubclasses); + + for (List list : subclasses.values()) { + postprocessStatementList(list); + } } private String checkConfiguration() { @@ -73,12 +82,12 @@ public class CollatedObjectPropertyTemplateModel extends ObjectPropertyTemplateM String queryString = getQueryString(); Matcher m; - m = SELECT_QUERY_PATTERN.matcher(queryString); + m = SELECT_SUBCLASS_PATTERN.matcher(queryString); if ( ! m.find() ) { return("Query does not select a subclass variable."); } - m = ORDER_BY_QUERY_PATTERN.matcher(queryString); + m = ORDER_BY_SUBCLASS_PATTERN.matcher(queryString); if ( ! m.find() ) { return("Query does not sort first by subclass variable."); } 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 de3632268..07ea1d1ce 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 @@ -5,8 +5,12 @@ package edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -31,13 +35,21 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel private static final Log log = LogFactory.getLog(ObjectPropertyTemplateModel.class); private static final String TYPE = "object"; - /* NB The default postprocessor is not the same as the postprocessor for the default view. The latter - * actually defines its own postprocessor, whereas the default postprocessor is used for custom views - * that don't define a postprocessor, to ensure that the standard postprocessing applies. + /* NB The default post-processor is not the same as the post-processor for the default view. The latter + * actually defines its own post-processor, whereas the default post-processor is used for custom views + * that don't define a post-processor, to ensure that the standard post-processing applies. */ private static final String DEFAULT_POSTPROCESSOR = "edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.DefaultObjectPropertyDataPostProcessor"; + private static final String END_DATE_TIME_VARIABLE = "dateTimeEnd"; + private static final Pattern ORDER_BY_END_DATE_TIME_PATTERN = + /* ORDER BY DESC(?dateTimeEnd) + * ORDER BY ?subclass ?dateTimeEnd + * ORDER BY DESC(?subclass) DESC(?dateTimeEnd) + */ + Pattern.compile("ORDER\\s+BY\\s+((DESC\\()?\\?subclass\\)?\\s+)?DESC\\s*\\(\\s*\\?" + END_DATE_TIME_VARIABLE + "\\)", Pattern.CASE_INSENSITIVE); + private PropertyListConfig config; ObjectPropertyTemplateModel(ObjectProperty op, Individual subject, VitroRequest vreq) { @@ -91,6 +103,54 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel } } + /* Post-processing that must occur after collation, because it does reordering on collated subclass + * lists rather than on the entire list. This should ideally be configurable in the config file + * like the pre-collation post-processing, but for now due to time constraints it applies to all views. + */ + protected void postprocessStatementList(List statements) { + moveEndDateTimesToTop(statements); + } + + /* SPARQL ORDER BY gives null values the lowest value, so null datetimes occur at the end + * of a list in descending sort order. Generally we assume that a null end datetime means the + * activity is ongoing in the present, so we want to display those at the top of the list. + * Application of this method should be configurable in the config file, but for now due to + * time constraints it applies to all views that sort by DESC(?dateTimeEnd), and the variable + * name is hard-coded here. (Note, therefore, that using a different variable name + * effectively turns off this post-processing.) + */ + protected void moveEndDateTimesToTop(List statements) { + String queryString = getQueryString(); + Matcher m = ORDER_BY_END_DATE_TIME_PATTERN.matcher(queryString); + if ( ! m.find() ) { + return; + } + + // Store the statements with null end datetimes in a temporary list, remove them from the original list, + // and move them back to the top of the original list. + List tempList = new ArrayList(); + Iterator iterator = statements.iterator(); + while (iterator.hasNext()) { + ObjectPropertyStatementTemplateModel stmt = (ObjectPropertyStatementTemplateModel)iterator.next(); + String dateTimeEnd = (String) stmt.get(END_DATE_TIME_VARIABLE); + 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 because all the queries are written with a subclass variable to support + // collation if switched on in the back end. + //if (statements.indexOf(stmt) == 0) { + // break; + //} + tempList.add(stmt); + iterator.remove(); + } + } + // Put all the statements with null end datetimes at the top of the list, preserving their original order. + statements.addAll(0, tempList); + + } + protected abstract String getDefaultConfigFileName(); private class PropertyListConfig { 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 22e1ff685..67ff1f02a 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 @@ -40,6 +40,8 @@ public class UncollatedObjectPropertyTemplateModel extends ObjectPropertyTemplat for (Map map : statementData) { statements.add(new ObjectPropertyStatementTemplateModel(subjectUri, propertyUri, map)); } + + postprocessStatementList(statements); } @Override