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 5dcbce7ea..16190b222 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 @@ -24,277 +24,313 @@ import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.dao.VClassDao; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.customlistview.InvalidConfigurationException; -public class CollatedObjectPropertyTemplateModel extends ObjectPropertyTemplateModel { +public class CollatedObjectPropertyTemplateModel extends + ObjectPropertyTemplateModel { - private static final Log log = LogFactory.getLog(CollatedObjectPropertyTemplateModel.class); - private static final String SUBCLASS_VARIABLE_NAME = "subclass"; - - private static final Pattern SELECT_SUBCLASS_PATTERN = - // SELECT ?subclass - Pattern.compile("SELECT[^{]*\\?" + SUBCLASS_VARIABLE_NAME + "\\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_VARIABLE_NAME, Pattern.CASE_INSENSITIVE); - - private final List subclasses; - private final VClassDao vclassDao; - - CollatedObjectPropertyTemplateModel(ObjectProperty op, Individual subject, - VitroRequest vreq, boolean editing, - List populatedObjectPropertyList) - throws InvalidConfigurationException { - - super(op, subject, vreq, editing); + private static final Log log = LogFactory + .getLog(CollatedObjectPropertyTemplateModel.class); + private static final String SUBCLASS_VARIABLE_NAME = "subclass"; - vclassDao = vreq.getWebappDaoFactory().getVClassDao(); - - if (populatedObjectPropertyList.contains(op)) { - log.debug("Getting data for populated object property " + op.getURI()); - - /* Get the data */ - List> statementData = getStatementData(); - - /* Apply post-processing */ - postprocess(statementData); - - /* Collate the data */ - subclasses = collate(subjectUri, op, statementData, editing); - - for (SubclassTemplateModel subclass : subclasses) { - List list = subclass.getStatements(); - postprocessStatementList(list); - } + private static final Pattern SELECT_SUBCLASS_PATTERN = + // SELECT ?subclass + Pattern.compile("SELECT[^{]*\\?" + SUBCLASS_VARIABLE_NAME + "\\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_VARIABLE_NAME, + Pattern.CASE_INSENSITIVE); - Collections.sort(subclasses); - - } else { - log.debug("Object property " + getUri() + " is unpopulated."); - subclasses = new ArrayList(); - } - } - - @Override - protected boolean isEmpty() { - return subclasses.isEmpty(); - } - - public ConfigError checkQuery(String queryString) { - - if (StringUtils.isBlank(queryString)) { - return ConfigError.NO_SELECT_QUERY; - } - - Matcher m; - m = SELECT_SUBCLASS_PATTERN.matcher(queryString); - if ( ! m.find() ) { - return ConfigError.NO_SUBCLASS_SELECT; - } - - m = ORDER_BY_SUBCLASS_PATTERN.matcher(queryString); - if ( ! m.find() ) { - return ConfigError.NO_SUBCLASS_ORDER_BY; - } - - return null; - } - - @Override - protected void removeDuplicates(List> data) { - filterSubclasses(data); - super.removeDuplicates(data); - } - - /* - * The query returns subclasses of a specific superclass that the object belongs to; for example, - * in the case of authorInAuthorship, subclasses of core:InformationResource. Here we remove all but - * the most specific subclass for the object. - */ - private void filterSubclasses(List> statementData) { - String objectVariableName = getObjectKey(); - if (objectVariableName == null) { - log.error("Cannot remove duplicate statements for property " + getUri() + " because no object found to dedupe."); - return; - } - - if (log.isDebugEnabled()) { - log.debug("Data before subclass filtering"); - logData(statementData); - } - - // Compile a list of the statements with most specific subclasses; others will be removed - List> filteredList = new ArrayList>(); - Set processedObjects = new HashSet(); - for (Map outerMap : statementData) { - String objectUri = outerMap.get(objectVariableName); - if (processedObjects.contains(objectUri)) { - continue; - } - processedObjects.add(objectUri); - List> dataForThisObject = new ArrayList>(); - for (Map innerMap : statementData) { - if ( innerMap.get(objectVariableName) == objectUri ) { - dataForThisObject.add(innerMap); - } - } - // Sort the data for this object from most to least specific subclass, with nulls at end - Collections.sort(dataForThisObject, new DataComparatorBySubclass()); - filteredList.add(dataForThisObject.get(0)); - } + private final List subclasses; + private final VClassDao vclassDao; - statementData.retainAll(filteredList); - - if (log.isDebugEnabled()) { - log.debug("Data after subclass filtering"); - logData(statementData); - } - } - - private class DataComparatorBySubclass implements Comparator> { + CollatedObjectPropertyTemplateModel(ObjectProperty op, Individual subject, + VitroRequest vreq, boolean editing, + List populatedObjectPropertyList) + throws InvalidConfigurationException { - @Override - public int compare(Map map1, Map map2) { - - String subclass1 = map1.get(SUBCLASS_VARIABLE_NAME); - String subclass2 = map2.get(SUBCLASS_VARIABLE_NAME); - - if (subclass1 == null) { - if (subclass2 == null) { - return 0; - } else { - return 1; // nulls rank highest - } - } - - if (subclass2 == null) { - return -1; // nulls rank highest - } - - if (subclass1.equals(subclass2)) { - return 0; - } - - List superclasses = vclassDao.getAllSuperClassURIs(subclass1); - if (superclasses.contains(subclass2)) { - return -1; - } - - superclasses = vclassDao.getAllSuperClassURIs(subclass2); - if (superclasses.contains(subclass1)) { - return 1; - } - - return 0; - } - } - - // Collate the statements by subclass. - private List collate(String subjectUri, ObjectProperty property, - List> statementData, boolean editing) { - - String objectKey = getObjectKey(); - - List subclasses = new ArrayList(); - - for (Map map : statementData) { - - String subclassUri = map.get(SUBCLASS_VARIABLE_NAME); + super(op, subject, vreq, editing); - VClass vclass = vclassDao.getVClassByURI(subclassUri); - - List listForThisSubclass = null; + vclassDao = vreq.getWebappDaoFactory().getVClassDao(); - for (SubclassTemplateModel subclass : subclasses) { - VClass subclassVClass = subclass.getVClass(); - if ( ( vclass == null && subclassVClass == null ) || - ( vclass != null && vclass.equals(subclassVClass) ) ) { - listForThisSubclass = subclass.getStatements(); - break; - } - } + if (populatedObjectPropertyList.contains(op)) { + log.debug("Getting data for populated object property " + + op.getURI()); - if (listForThisSubclass == null) { - listForThisSubclass = new ArrayList(); - subclasses.add(new SubclassTemplateModel(vclass, listForThisSubclass)); - } + /* Get the data */ + List> statementData = getStatementData(); - listForThisSubclass.add(new ObjectPropertyStatementTemplateModel(subjectUri, - property, objectKey, map, getTemplateName(), vreq)); + /* Apply post-processing */ + postprocess(statementData); - } - - return subclasses; - } - -// class SubclassComparatorByDisplayRank implements Comparator { -// -// private List vclasses; -// -// SubclassComparatorByDisplayRank(List vclasses) { -// this.vclasses = vclasses; -// } -// -// @Override -// public int compare(String nameLeft, String nameRight) { -// -// if (StringUtils.isBlank(uriLeft)) { -// return StringUtils.isBlank(uriRight) ? 0 : 1; -// } -// -// VClass vclassLeft = vclassDao.getVClassByURI(uriLeft); -// VClass vclassRight = vclassDao.getVClassByURI(uriRight); -// -// if (vclassLeft == null) { -// return vclassRight == null ? 0 : 1; -// } -// -// int rankLeft = vclassLeft.getDisplayRank(); -// int rankRight = vclassRight.getDisplayRank(); -// -// int intCompare = 0; -// -// // Values < 1 are undefined and go at end, not beginning -// if (rankLeft < 1) { -// intCompare = rankRight < 1 ? 0 : 1; -// } else if (rankRight < 1) { -// intCompare = -1; -// } else { -// intCompare = ((Integer)rankLeft).compareTo(rankRight); -// } -// -// if (intCompare != 0) { -// return intCompare; -// } -// -// // If display ranks are equal, sort by name -// if (nameLeft == null) { -// return nameRight == null ? 0 : 1; -// } -// if (nameRight == null) { -// return -1; -// } -// return nameLeft.compareToIgnoreCase(nameRight); -// -// } -// } - -// private String getSubclassName(String subclassUri) { -// if (subclassUri.isEmpty()) { -// return ""; -// } -// VClass vclass = vclassDao.getVClassByURI(subclassUri); -// return vclass != null ? vclass.getName() : ""; -// } - - - /* Template properties */ - - public List getSubclasses() { - return subclasses; - } - - @Override - public boolean isCollatedBySubclass() { - return true; - } + /* Collate the data */log.debug("Collate:collating subclasses for " + + subjectUri + " - predicate= " + op.getURI()); + subclasses = collate(subjectUri, op, statementData, editing); + + for (SubclassTemplateModel subclass : subclasses) { + List list = subclass + .getStatements(); + postprocessStatementList(list); + } + + Collections.sort(subclasses); + + } else { + log.debug("Object property " + getUri() + " is unpopulated."); + subclasses = new ArrayList(); + } + } + + @Override + protected boolean isEmpty() { + return subclasses.isEmpty(); + } + + public ConfigError checkQuery(String queryString) { + + if (StringUtils.isBlank(queryString)) { + return ConfigError.NO_SELECT_QUERY; + } + + Matcher m; + m = SELECT_SUBCLASS_PATTERN.matcher(queryString); + if (!m.find()) { + return ConfigError.NO_SUBCLASS_SELECT; + } + + m = ORDER_BY_SUBCLASS_PATTERN.matcher(queryString); + if (!m.find()) { + return ConfigError.NO_SUBCLASS_ORDER_BY; + } + + return null; + } + + @Override + protected void removeDuplicates(List> data) { + filterSubclasses(data); + super.removeDuplicates(data); + } + + /* + * The query returns subclasses of a specific superclass that the object + * belongs to; for example, in the case of authorInAuthorship, subclasses of + * core:InformationResource. Here we remove all but the most specific + * subclass for the object. + */ + private void filterSubclasses(List> statementData) { + String objectVariableName = getObjectKey(); + if (objectVariableName == null) { + log.error("Cannot remove duplicate statements for property " + + getUri() + " because no object found to dedupe."); + return; + } + + if (log.isDebugEnabled()) { + log.debug("Data before subclass filtering"); + logData(statementData); + } + + // Compile a list of the statements with most specific subclasses; + // others will be removed + List> filteredList = new ArrayList>(); + Set processedObjects = new HashSet(); + for (Map outerMap : statementData) { + + String objectUri = outerMap.get(objectVariableName); + if (processedObjects.contains(objectUri)) { + continue; + } + processedObjects.add(objectUri); + List> dataForThisObject = new ArrayList>(); + for (Map innerMap : statementData) { + if (innerMap.get(objectVariableName) == objectUri) { + dataForThisObject.add(innerMap); + } + } + // Sort the data for this object from most to least specific + // subclass, with nulls at end + + Collections.sort(dataForThisObject, new DataComparatorBySubclass()); + + filteredList.add(dataForThisObject.get(0)); + } + + statementData.retainAll(filteredList); + + if (log.isDebugEnabled()) { + log.debug("Data after subclass filtering"); + logData(statementData); + } + } + + private class DataComparatorBySubclass implements + Comparator> { + + @Override + public int compare(Map map1, Map map2) { + + String subclass1 = map1.get(SUBCLASS_VARIABLE_NAME); + String subclass2 = map2.get(SUBCLASS_VARIABLE_NAME); + + if (subclass1 == null) { + if (subclass2 == null) { + return 0; + } else { + return 1; // nulls rank highest + } + } + + if (subclass2 == null) { + return -1; // nulls rank highest + } + + // if subclasses are equal, return 0 + if (subclass1.equals(subclass2)) + return 0; + String subclass1Superclasses = this + .getSuperclassesAsString(subclass1); + String subclass2Superclasses = this + .getSuperclassesAsString(subclass2); + + int s1SuperclassesLength = subclass1Superclasses.length(); + int s2SuperclassesLength = subclass2Superclasses.length(); + + if (s1SuperclassesLength == s2SuperclassesLength) { + // If the length is the same, then compare the strings + // alphabetically + return subclass1Superclasses.compareTo(subclass2Superclasses); + } else { + // if the length is different, the longer string is the more + // specific type + return (s1SuperclassesLength > s2SuperclassesLength ? -1 : 1); + } + } + + // Given a particular class URI, generate a string representing the + // superclasses and types of the class + // The types are first sorted alphabetically and then concatenated to + // form the string representation + private String getSuperclassesAsString(String classURI) { + List superclasses = vclassDao + .getAllSuperClassURIs(classURI); + if (superclasses == null || superclasses.size() == 0) + return ""; + Collections.sort(superclasses); + return StringUtils.join(superclasses, " | "); + + } + } + + // Collate the statements by subclass. + private List collate(String subjectUri, + ObjectProperty property, List> statementData, + boolean editing) { + + String objectKey = getObjectKey(); + + List subclasses = new ArrayList(); + + for (Map map : statementData) { + + String subclassUri = map.get(SUBCLASS_VARIABLE_NAME); + + VClass vclass = vclassDao.getVClassByURI(subclassUri); + + List listForThisSubclass = null; + + for (SubclassTemplateModel subclass : subclasses) { + VClass subclassVClass = subclass.getVClass(); + if ((vclass == null && subclassVClass == null) + || (vclass != null && vclass.equals(subclassVClass))) { + listForThisSubclass = subclass.getStatements(); + break; + } + } + + if (listForThisSubclass == null) { + listForThisSubclass = new ArrayList(); + subclasses.add(new SubclassTemplateModel(vclass, + listForThisSubclass)); + } + + listForThisSubclass.add(new ObjectPropertyStatementTemplateModel( + subjectUri, property, objectKey, map, getTemplateName(), + vreq)); + + } + + return subclasses; + } + + // class SubclassComparatorByDisplayRank implements Comparator { + // + // private List vclasses; + // + // SubclassComparatorByDisplayRank(List vclasses) { + // this.vclasses = vclasses; + // } + // + // @Override + // public int compare(String nameLeft, String nameRight) { + // + // if (StringUtils.isBlank(uriLeft)) { + // return StringUtils.isBlank(uriRight) ? 0 : 1; + // } + // + // VClass vclassLeft = vclassDao.getVClassByURI(uriLeft); + // VClass vclassRight = vclassDao.getVClassByURI(uriRight); + // + // if (vclassLeft == null) { + // return vclassRight == null ? 0 : 1; + // } + // + // int rankLeft = vclassLeft.getDisplayRank(); + // int rankRight = vclassRight.getDisplayRank(); + // + // int intCompare = 0; + // + // // Values < 1 are undefined and go at end, not beginning + // if (rankLeft < 1) { + // intCompare = rankRight < 1 ? 0 : 1; + // } else if (rankRight < 1) { + // intCompare = -1; + // } else { + // intCompare = ((Integer)rankLeft).compareTo(rankRight); + // } + // + // if (intCompare != 0) { + // return intCompare; + // } + // + // // If display ranks are equal, sort by name + // if (nameLeft == null) { + // return nameRight == null ? 0 : 1; + // } + // if (nameRight == null) { + // return -1; + // } + // return nameLeft.compareToIgnoreCase(nameRight); + // + // } + // } + + // private String getSubclassName(String subclassUri) { + // if (subclassUri.isEmpty()) { + // return ""; + // } + // VClass vclass = vclassDao.getVClassByURI(subclassUri); + // return vclass != null ? vclass.getName() : ""; + // } + + /* Template properties */ + + public List getSubclasses() { + return subclasses; + } + + @Override + public boolean isCollatedBySubclass() { + return true; + } }