NIHVIVO-1625 For custom list views that order by end datetime descending, reorder (within a collation, if property is collated) to put nulls at beginning rather than end.

This commit is contained in:
rjy7 2011-01-07 17:13:44 +00:00
parent 7db0ea8938
commit 6b7415f5a0
5 changed files with 94 additions and 12 deletions

View file

@ -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<ObjectPropertyStatement> statementsForObject = object.getObjectPropertyStatements(op);
// Could be empty for statements with no linked individual.
if ( ! statementsForObject.isEmpty() ) {
relatedStatements.add(statementsForObject.get(0));
}
}
return relatedStatements;

View file

@ -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<String, String> 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<String, String> 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<ObjectPropertyStatementTemplateModel> list) {
}
}

View file

@ -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<String, List<ObjectPropertyStatementTemplateModel>> subclasses;
@ -65,7 +70,11 @@ public class CollatedObjectPropertyTemplateModel extends ObjectPropertyTemplateM
return o1.compareTo(o2);
}};
subclasses = new TreeMap<String, List<ObjectPropertyStatementTemplateModel>>(comparer);
subclasses.putAll(unsortedSubclasses);
subclasses.putAll(unsortedSubclasses);
for (List<ObjectPropertyStatementTemplateModel> 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.");
}

View file

@ -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<ObjectPropertyStatementTemplateModel> 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<ObjectPropertyStatementTemplateModel> 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<ObjectPropertyStatementTemplateModel> tempList = new ArrayList<ObjectPropertyStatementTemplateModel>();
Iterator<ObjectPropertyStatementTemplateModel> 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 {

View file

@ -40,6 +40,8 @@ public class UncollatedObjectPropertyTemplateModel extends ObjectPropertyTemplat
for (Map<String, String> map : statementData) {
statements.add(new ObjectPropertyStatementTemplateModel(subjectUri, propertyUri, map));
}
postprocessStatementList(statements);
}
@Override