NIHVIVO-3628 Create the CustomListViewConfigFile class, with tests, and use it to replace much of the code in ObjectPropertyTemplateMode.PropertyListConfig
This commit is contained in:
parent
c5f3d7cc05
commit
28c06329f7
4 changed files with 564 additions and 120 deletions
|
@ -0,0 +1,261 @@
|
|||
/* $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.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* Parses a configuration file for a custom list view, and makes the information
|
||||
* readily accessible.
|
||||
*
|
||||
* If not editing the page, include clauses that will filter out statements with
|
||||
* missing linked individual or other critical information missing (e.g., anchor
|
||||
* and url on a link). If editing, omit those clauses so the incomplete
|
||||
* statements will show, and can be edited or removed.
|
||||
*
|
||||
* We might want to refine this based on whether the user can edit the statement
|
||||
* in question, but that would require a completely different approach: include
|
||||
* the statement in the query results, and then during the postprocessing phase,
|
||||
* check the editing policy, and remove the statement if it's not editable. We
|
||||
* would not preprocess the query, as here.
|
||||
*
|
||||
* If not collating by subgroup, omit clauses that only involve subgroups.
|
||||
*/
|
||||
public class CustomListViewConfigFile {
|
||||
private static final Log log = LogFactory
|
||||
.getLog(CustomListViewConfigFile.class);
|
||||
|
||||
private static final String TAG_CONSTRUCT = "query-construct";
|
||||
private static final String TAG_SELECT = "query-select";
|
||||
private static final String TAG_TEMPLATE = "template";
|
||||
private static final String TAG_POSTPROCESSOR = "postprocessor";
|
||||
private static final String TAG_COLLATED = "collated";
|
||||
private static final String TAG_CRITICAL = "critical-data-required";
|
||||
|
||||
// Will not be null. This is mutable, but don't modify it. Clone it and
|
||||
// modify the clone.
|
||||
private final Element selectQueryElement;
|
||||
|
||||
// The set might be empty but will not be null. Each query will not be empty
|
||||
// or null.
|
||||
private final Set<String> constructQueries;
|
||||
|
||||
// Will not be empty or null.
|
||||
private final String templateName;
|
||||
|
||||
// Might be empty but will not be null.
|
||||
private final String postprocessorName;
|
||||
|
||||
CustomListViewConfigFile(Reader reader) throws InvalidConfigFileException {
|
||||
Document doc = parseDocument(reader);
|
||||
selectQueryElement = parseSelectQuery(doc);
|
||||
constructQueries = parseConstructQueries(doc);
|
||||
templateName = parseTemplateName(doc);
|
||||
postprocessorName = parsePostprocessorName(doc);
|
||||
}
|
||||
|
||||
private Document parseDocument(Reader reader)
|
||||
throws InvalidConfigFileException {
|
||||
if (reader == null) {
|
||||
throw new InvalidConfigFileException("Config file reader is null.");
|
||||
}
|
||||
|
||||
try {
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
Document doc = db.parse(new InputSource(reader));
|
||||
return doc;
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw new InvalidConfigFileException("Problem with XML parser.", e);
|
||||
} catch (SAXException e) {
|
||||
throw new InvalidConfigFileException(
|
||||
"Config file is not valid XML: " + e.getMessage(), e);
|
||||
} catch (IOException e) {
|
||||
throw new InvalidConfigFileException("Unable to read config file.",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
private Element parseSelectQuery(Document doc)
|
||||
throws InvalidConfigFileException {
|
||||
Element element = getExactlyOneElement(doc, TAG_SELECT);
|
||||
elementMustNotBeEmpty(element);
|
||||
return element;
|
||||
}
|
||||
|
||||
private Set<String> parseConstructQueries(Document doc) {
|
||||
Set<String> queries = new HashSet<String>();
|
||||
for (Element element : getElements(doc, TAG_CONSTRUCT)) {
|
||||
String content = element.getTextContent();
|
||||
if (!content.trim().isEmpty()) {
|
||||
queries.add(content);
|
||||
}
|
||||
}
|
||||
return queries;
|
||||
}
|
||||
|
||||
private String parseTemplateName(Document doc)
|
||||
throws InvalidConfigFileException {
|
||||
Element element = getExactlyOneElement(doc, TAG_TEMPLATE);
|
||||
elementMustNotBeEmpty(element);
|
||||
return element.getTextContent();
|
||||
}
|
||||
|
||||
private String parsePostprocessorName(Document doc)
|
||||
throws InvalidConfigFileException {
|
||||
Element element = getZeroOrOneElement(doc, TAG_POSTPROCESSOR);
|
||||
if (element == null) {
|
||||
return "";
|
||||
} else {
|
||||
return element.getTextContent();
|
||||
}
|
||||
}
|
||||
|
||||
private List<Element> getElements(Document doc, String tagName) {
|
||||
List<Element> list = new ArrayList<Element>();
|
||||
NodeList nodes = doc.getElementsByTagName(tagName);
|
||||
if (nodes != null) {
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
list.add((Element) nodes.item(i));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private Element getExactlyOneElement(Document doc, String tagName)
|
||||
throws InvalidConfigFileException {
|
||||
List<Element> elements = getElements(doc, tagName);
|
||||
|
||||
if (elements.size() == 1) {
|
||||
return elements.get(0);
|
||||
} else if (elements.isEmpty()) {
|
||||
throw new InvalidConfigFileException("Config file must contain a "
|
||||
+ tagName + " element");
|
||||
} else {
|
||||
throw new InvalidConfigFileException(
|
||||
"Config file may not contain more than one " + tagName
|
||||
+ " element");
|
||||
}
|
||||
}
|
||||
|
||||
private Element getZeroOrOneElement(Document doc, String tagName)
|
||||
throws InvalidConfigFileException {
|
||||
List<Element> elements = getElements(doc, tagName);
|
||||
if (elements.size() == 1) {
|
||||
return elements.get(0);
|
||||
} else if (elements.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
throw new InvalidConfigFileException(
|
||||
"Config file may not contain more than one " + tagName
|
||||
+ " element");
|
||||
}
|
||||
}
|
||||
|
||||
private void elementMustNotBeEmpty(Element element)
|
||||
throws InvalidConfigFileException {
|
||||
String contents = element.getTextContent();
|
||||
if (contents.trim().isEmpty()) {
|
||||
throw new InvalidConfigFileException("In a config file, the <"
|
||||
+ element.getTagName() + "> element must not be empty.");
|
||||
}
|
||||
}
|
||||
|
||||
private void removeChildElements(Element element, String tagName) {
|
||||
/*
|
||||
* When we remove a child from the element, it disappears from the
|
||||
* NodeList. We process the NodeList in reverse order, so we won't be
|
||||
* disturbed by nodes disappearing off the end. Strange.
|
||||
*/
|
||||
NodeList doomed = element.getElementsByTagName(tagName);
|
||||
for (int i = doomed.getLength() - 1; i >= 0; i--) {
|
||||
element.removeChild(doomed.item(i));
|
||||
}
|
||||
}
|
||||
|
||||
public String getSelectQuery(boolean collated, boolean editing) {
|
||||
Element cloned = (Element) selectQueryElement.cloneNode(true);
|
||||
|
||||
if (!collated) {
|
||||
removeChildElements(cloned, TAG_COLLATED);
|
||||
}
|
||||
if (editing) {
|
||||
removeChildElements(cloned, TAG_CRITICAL);
|
||||
}
|
||||
|
||||
return cloned.getTextContent();
|
||||
}
|
||||
|
||||
public Set<String> getConstructQueries() {
|
||||
return constructQueries;
|
||||
}
|
||||
|
||||
public String getTemplateName() {
|
||||
return templateName;
|
||||
}
|
||||
|
||||
public String getPostprocessorName() {
|
||||
return postprocessorName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CustomListViewConfigFile[selectQuery='"
|
||||
+ formatXmlNode(selectQueryElement) + "', constructQueries="
|
||||
+ constructQueries + ", templateName='" + templateName
|
||||
+ "', postprocessorName='" + postprocessorName + "']";
|
||||
}
|
||||
|
||||
private String formatXmlNode(Node node) {
|
||||
try {
|
||||
// Set up the output transformer
|
||||
Transformer t = TransformerFactory.newInstance().newTransformer();
|
||||
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
|
||||
t.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
|
||||
// Print the DOM node
|
||||
StringWriter sw = new StringWriter();
|
||||
StreamResult result = new StreamResult(sw);
|
||||
DOMSource source = new DOMSource(node);
|
||||
t.transform(source, result);
|
||||
return sw.toString();
|
||||
} catch (TransformerException e) {
|
||||
return "Failed to format XML node: " + node.getTextContent();
|
||||
}
|
||||
}
|
||||
|
||||
public static class InvalidConfigFileException extends Exception {
|
||||
public InvalidConfigFileException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InvalidConfigFileException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,10 +3,10 @@
|
|||
package edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -14,15 +14,9 @@ import java.util.Set;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
|
||||
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestActionConstants;
|
||||
|
@ -341,13 +335,6 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel
|
|||
private static final String CONFIG_FILE_PATH = "/config/";
|
||||
private static final String DEFAULT_CONFIG_FILE_NAME = "listViewConfig-default.xml";
|
||||
|
||||
private static final String NODE_NAME_QUERY_CONSTRUCT = "query-construct";
|
||||
private static final String NODE_NAME_QUERY_SELECT = "query-select";
|
||||
private static final String NODE_NAME_TEMPLATE = "template";
|
||||
private static final String NODE_NAME_POSTPROCESSOR = "postprocessor";
|
||||
private static final String NODE_NAME_COLLATED = "collated";
|
||||
private static final String NODE_NAME_CRITICAL_DATA_REQUIRED = "critical-data-required";
|
||||
|
||||
/* 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.
|
||||
|
@ -442,79 +429,38 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel
|
|||
|
||||
private void setValuesFromConfigFile(String configFilePath, ObjectProperty op, WebappDaoFactory wdf,
|
||||
boolean editing) {
|
||||
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db;
|
||||
|
||||
try {
|
||||
db = dbf.newDocumentBuilder();
|
||||
Document doc = db.parse(configFilePath);
|
||||
String propertyUri = op.getURI();
|
||||
|
||||
// Required values
|
||||
selectQuery = getSelectQuery(doc, propertyUri, editing);
|
||||
|
||||
templateName = getConfigValue(doc, NODE_NAME_TEMPLATE, propertyUri);
|
||||
|
||||
// Optional values
|
||||
constructQueries = getConfigValues(doc, NODE_NAME_QUERY_CONSTRUCT, propertyUri);
|
||||
|
||||
String postprocessorName = getConfigValue(doc, NODE_NAME_POSTPROCESSOR, propertyUri);
|
||||
|
||||
try {
|
||||
FileReader reader = new FileReader(configFilePath);
|
||||
CustomListViewConfigFile configFileContents = new CustomListViewConfigFile(reader);
|
||||
boolean collated = ObjectPropertyTemplateModel.this instanceof CollatedObjectPropertyTemplateModel;
|
||||
|
||||
if (StringUtils.isBlank(postprocessorName)) {
|
||||
log.debug("No postprocessor specified for property " + propertyUri + ". Using default postprocessor.");
|
||||
postprocessorName = DEFAULT_POSTPROCESSOR;
|
||||
}
|
||||
try {
|
||||
getPostProcessor(postprocessorName, wdf);
|
||||
} catch (Exception e) {
|
||||
if (! postprocessorName.equals(DEFAULT_POSTPROCESSOR)) {
|
||||
log.debug("Cannot find postprocessor specified for property " + propertyUri + ". Using default postprocessor.");
|
||||
postprocessorName = DEFAULT_POSTPROCESSOR;
|
||||
getPostProcessor(postprocessorName, wdf);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Error processing config file " + configFilePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getSelectQuery(Document doc, String propertyUri, boolean editing) {
|
||||
Node selectQueryNode = doc.getElementsByTagName(NODE_NAME_QUERY_SELECT).item(0);
|
||||
String value = null;
|
||||
if (selectQueryNode != null) {
|
||||
boolean collated = ObjectPropertyTemplateModel.this instanceof CollatedObjectPropertyTemplateModel;
|
||||
/* If not editing the page (editing == false), hide statements with missing linked individual or other
|
||||
* critical information missing (e.g., anchor and url on a link); otherwise, show these statements.
|
||||
* We might want to refine this based on whether the user can edit the statement in question, but that
|
||||
* would require a completely different approach: include the statement in the query results, and then during the
|
||||
* postprocessing phase, check the editing policy, and remove the statement if it's not editable. We would not
|
||||
* preprocess the query, as here.
|
||||
*/
|
||||
boolean criticalDataRequired = !editing;
|
||||
NodeList children = selectQueryNode.getChildNodes();
|
||||
int childCount = children.getLength();
|
||||
value = "";
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
Node node = children.item(i);
|
||||
if (node.getNodeName().equals(NODE_NAME_COLLATED)) {
|
||||
if (collated) {
|
||||
value += node.getChildNodes().item(0).getNodeValue();
|
||||
} // else ignore this node
|
||||
} else if (node.getNodeName().equals(NODE_NAME_CRITICAL_DATA_REQUIRED)) {
|
||||
if (criticalDataRequired) {
|
||||
value += node.getChildNodes().item(0).getNodeValue();
|
||||
} // else ignore this node
|
||||
} else {
|
||||
value += node.getNodeValue();
|
||||
}
|
||||
}
|
||||
log.debug("Found config parameter " + NODE_NAME_QUERY_SELECT + " for object property " + propertyUri + " with value " + value);
|
||||
} else {
|
||||
log.error("No value found for config parameter " + NODE_NAME_QUERY_SELECT + " for object property " + propertyUri);
|
||||
}
|
||||
return value;
|
||||
selectQuery = configFileContents.getSelectQuery(collated, editing);
|
||||
templateName = configFileContents.getTemplateName();
|
||||
constructQueries = configFileContents.getConstructQueries();
|
||||
|
||||
String postprocessorName = configFileContents.getPostprocessorName();
|
||||
|
||||
if (StringUtils.isBlank(postprocessorName)) {
|
||||
log.debug("No postprocessor specified for property "
|
||||
+ propertyUri + ". Using default postprocessor.");
|
||||
postprocessorName = DEFAULT_POSTPROCESSOR;
|
||||
}
|
||||
try {
|
||||
getPostProcessor(postprocessorName, wdf);
|
||||
} catch (Exception e) {
|
||||
if (!postprocessorName.equals(DEFAULT_POSTPROCESSOR)) {
|
||||
log.debug("Cannot find postprocessor specified for property "
|
||||
+ propertyUri
|
||||
+ ". Using default postprocessor.");
|
||||
postprocessorName = DEFAULT_POSTPROCESSOR;
|
||||
getPostProcessor(postprocessorName, wdf);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Error processing config file " + configFilePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void getPostProcessor(String name, WebappDaoFactory wdf) throws Exception {
|
||||
|
@ -523,36 +469,6 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel
|
|||
postprocessor = (ObjectPropertyDataPostProcessor) constructor.newInstance(ObjectPropertyTemplateModel.this, wdf);
|
||||
}
|
||||
|
||||
private String getConfigValue(Document doc, String nodeName, String propertyUri) {
|
||||
Node node = doc.getElementsByTagName(nodeName).item(0);
|
||||
String value = null;
|
||||
if (node != null) {
|
||||
value = node.getChildNodes().item(0).getNodeValue();
|
||||
log.debug("Found config parameter " + nodeName + " for object property " + propertyUri + " with value " + value);
|
||||
} else {
|
||||
log.debug("No value found for config parameter " + nodeName + " for object property " + propertyUri);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private Set<String> getConfigValues(Document doc, String nodeName, String propertyUri) {
|
||||
Set<String> values = null;
|
||||
NodeList nodes = doc.getElementsByTagName(nodeName);
|
||||
int nodeCount = nodes.getLength();
|
||||
if (nodeCount > 0) {
|
||||
values = new HashSet<String>(nodeCount);
|
||||
for (int i = 0; i < nodeCount; i++) {
|
||||
Node node = nodes.item(i);
|
||||
String value = node.getChildNodes().item(0).getNodeValue();
|
||||
values.add(value);
|
||||
log.debug("Found config parameter " + nodeName + " for object property " + propertyUri + " with value " + value);
|
||||
}
|
||||
} else {
|
||||
log.debug("No values found for config parameter " + nodeName + " for object property " + propertyUri);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private String getConfigFilePath(String filename) {
|
||||
return servletContext.getRealPath(CONFIG_FILE_PATH + filename);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,267 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.matchers.JUnitMatchers;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
|
||||
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.CustomListViewConfigFile.InvalidConfigFileException;
|
||||
|
||||
/**
|
||||
* Note: when testing, an "empty" element may be self-closing, or with
|
||||
* zero-length text content, or with only blank text content.
|
||||
*/
|
||||
public class CustomListViewConfigFileTest extends AbstractTestClass {
|
||||
/**
|
||||
* Use this XML to test the methods that strip tags from the select clause.
|
||||
*
|
||||
* If not collated, omit the "collated" tag. If editing, omit the
|
||||
* "critical-data-required" tag.
|
||||
*/
|
||||
private static final String XML_WITH_RICH_SELECT_CLAUSE = "<list-view-config>"
|
||||
+ "<query-select>SELECT"
|
||||
+ " <collated>collated1</collated>"
|
||||
+ " <collated>collated2</collated>"
|
||||
+ " <critical-data-required>critical</critical-data-required>"
|
||||
+ "</query-select>"
|
||||
+ "<template>template.ftl</template>"
|
||||
+ "</list-view-config>";
|
||||
|
||||
/**
|
||||
* In general, we expect no exception, but individual tests may override,
|
||||
* like this:
|
||||
*
|
||||
* <pre>
|
||||
* thrown.expect(InvalidConfigurationException.class);
|
||||
* thrown.expectMessage("Bozo");
|
||||
* </pre>
|
||||
*/
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private CustomListViewConfigFile configFile;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Test failures
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void readerIsNull() throws InvalidConfigFileException {
|
||||
expectException("Config file reader is null.");
|
||||
configFile = new CustomListViewConfigFile(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readerThrowsIOException() throws InvalidConfigFileException {
|
||||
expectException("Unable to read config file.");
|
||||
configFile = new CustomListViewConfigFile(new ExplodingReader());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidXml() throws InvalidConfigFileException {
|
||||
suppressSyserr(); // catch the error report from the XML parser
|
||||
expectException(JUnitMatchers
|
||||
.containsString("Config file is not valid XML:"));
|
||||
readConfigFile("<unbalancedTag>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectQueryMissing() throws InvalidConfigFileException {
|
||||
expectException("Config file must contain a query-select element");
|
||||
readConfigFile("<list-view-config>"
|
||||
+ "<template>template.ftl</template>" + "</list-view-config>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectQueryMultiple() throws InvalidConfigFileException {
|
||||
expectException("Config file may not contain more than one query-select element");
|
||||
readConfigFile("<list-view-config>"
|
||||
+ "<query-select>SELECT</query-select>"
|
||||
+ "<query-select>ANOTHER</query-select>"
|
||||
+ "<template>template.ftl</template>" + "</list-view-config>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectQueryEmpty() throws InvalidConfigFileException {
|
||||
expectException("In a config file, the <query-select> element must not be empty.");
|
||||
readConfigFile("<list-view-config>" + "<query-select/>"
|
||||
+ "<template>template.ftl</template>" + "</list-view-config>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void templateNameMissing() throws InvalidConfigFileException {
|
||||
expectException("Config file must contain a template element");
|
||||
readConfigFile("<list-view-config>"
|
||||
+ "<query-select>SELECT</query-select>" + "</list-view-config>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void templateNameMultiple() throws InvalidConfigFileException {
|
||||
expectException("Config file may not contain more than one template element");
|
||||
readConfigFile("<list-view-config>"
|
||||
+ "<query-select>SELECT</query-select>"
|
||||
+ "<template>template.ftl</template>"
|
||||
+ "<template>another.ftl</template>" + "</list-view-config>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void templateNameEmpty() throws InvalidConfigFileException {
|
||||
expectException("In a config file, the <template> element must not be empty.");
|
||||
readConfigFile("<list-view-config>"
|
||||
+ "<query-select>SELECT</query-select>"
|
||||
+ "<template> </template>" + "</list-view-config>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postprocessorNameMultiple() throws InvalidConfigFileException {
|
||||
expectException("Config file may not contain more than one postprocessor element");
|
||||
readConfigFile("<list-view-config>"
|
||||
+ "<query-select>SELECT</query-select>"
|
||||
+ "<template>template.ftl</template>"
|
||||
+ "<postprocessor>ONE</postprocessor>"
|
||||
+ "<postprocessor>TWO</postprocessor>" + "</list-view-config>");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Test successes
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void minimalSuccess() throws InvalidConfigFileException {
|
||||
readConfigFile("<list-view-config>"
|
||||
+ "<query-select>SELECT</query-select>"
|
||||
+ "<template>template.ftl</template>" + "</list-view-config>");
|
||||
assertConfigFile(true, false, "SELECT", constructs(), "template.ftl",
|
||||
"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maximalSuccess() throws InvalidConfigFileException {
|
||||
readConfigFile("<list-view-config>"
|
||||
+ "<query-select>SELECT</query-select>"
|
||||
+ "<query-construct>CONSTRUCT ONE</query-construct>"
|
||||
+ "<query-construct>CONSTRUCT TWO</query-construct>"
|
||||
+ "<template>template.ftl</template>"
|
||||
+ "<postprocessor>post.processor.name</postprocessor>"
|
||||
+ "</list-view-config>");
|
||||
assertConfigFile(true, false, "SELECT",
|
||||
constructs("CONSTRUCT ONE", "CONSTRUCT TWO"), "template.ftl",
|
||||
"post.processor.name");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postprocessorEmptyIsOK() throws InvalidConfigFileException {
|
||||
readConfigFile("<list-view-config>"
|
||||
+ "<query-select>SELECT</query-select>"
|
||||
+ "<query-construct>CONSTRUCT</query-construct>"
|
||||
+ "<template>template.ftl</template>"
|
||||
+ "<postprocessor></postprocessor>" + "</list-view-config>");
|
||||
assertConfigFile(true, false, "SELECT", constructs("CONSTRUCT"),
|
||||
"template.ftl", "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectCollatedEditing() throws InvalidConfigFileException {
|
||||
readConfigFile(XML_WITH_RICH_SELECT_CLAUSE);
|
||||
assertConfigFile(true, true, "SELECT collated1 collated2 ", constructs(),
|
||||
"template.ftl", "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectCollatedNotEditing() throws InvalidConfigFileException {
|
||||
readConfigFile(XML_WITH_RICH_SELECT_CLAUSE);
|
||||
assertConfigFile(true, false, "SELECT collated1 collated2 critical",
|
||||
constructs(), "template.ftl", "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectNotCollatedEditing() throws InvalidConfigFileException {
|
||||
readConfigFile(XML_WITH_RICH_SELECT_CLAUSE);
|
||||
assertConfigFile(false, true, "SELECT ", constructs(),
|
||||
"template.ftl", "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectNotCollatedNotEditing() throws InvalidConfigFileException {
|
||||
readConfigFile(XML_WITH_RICH_SELECT_CLAUSE);
|
||||
assertConfigFile(false, false, "SELECT critical", constructs(),
|
||||
"template.ftl", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* TODO Successes:
|
||||
* select query with all tags
|
||||
* collated, editing, both, neither
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
private void expectException(String message) {
|
||||
thrown.expect(InvalidConfigFileException.class);
|
||||
thrown.expectMessage(message);
|
||||
}
|
||||
|
||||
private void expectException(Matcher<String> matcher) {
|
||||
thrown.expect(InvalidConfigFileException.class);
|
||||
thrown.expectMessage(matcher);
|
||||
}
|
||||
|
||||
private void readConfigFile(String xmlString)
|
||||
throws InvalidConfigFileException {
|
||||
StringReader reader = new StringReader(xmlString);
|
||||
configFile = new CustomListViewConfigFile(reader);
|
||||
}
|
||||
|
||||
private String[] constructs(String... constructQueries) {
|
||||
return constructQueries;
|
||||
}
|
||||
|
||||
private void assertConfigFile(boolean collated, boolean editing,
|
||||
String selectQuery, String[] constructQueries, String templateName,
|
||||
String postprocessorName) {
|
||||
assertEquals("select query", selectQuery,
|
||||
configFile.getSelectQuery(collated, editing));
|
||||
assertEquals("construct queries",
|
||||
new HashSet<String>(Arrays.asList(constructQueries)),
|
||||
configFile.getConstructQueries());
|
||||
assertEquals("template name", templateName,
|
||||
configFile.getTemplateName());
|
||||
assertEquals("postprocessor name", postprocessorName,
|
||||
configFile.getPostprocessorName());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Supporting classes
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
private class ExplodingReader extends Reader {
|
||||
@Override
|
||||
public int read(char[] arg0, int arg1, int arg2) throws IOException {
|
||||
throw new IOException("ExplodingReader threw an exception.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
// Nothing to close.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -276,7 +276,7 @@ public class ObjectPropertyTemplateModel_PropertyListConfigTest extends
|
|||
optm = new NonCollatingOPTM(op, subject, vreq, false);
|
||||
|
||||
assertLogMessagesContains("no template node",
|
||||
"Missing template specification");
|
||||
"Config file must contain a template element");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -289,7 +289,7 @@ public class ObjectPropertyTemplateModel_PropertyListConfigTest extends
|
|||
optm = new NonCollatingOPTM(op, subject, vreq, false);
|
||||
|
||||
assertLogMessagesContains("empty template node",
|
||||
"Missing template specification");
|
||||
"In a config file, the <template> element must not be empty.");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -410,8 +410,8 @@ public class ObjectPropertyTemplateModel_PropertyListConfigTest extends
|
|||
|
||||
// TODO This should not cause an error. If it did, it should not swallow
|
||||
// the exception. It should use the default PP.
|
||||
assertLogMessagesContains("pp name empty", "NullPointerException");
|
||||
assertPostProcessorClass("pp name empty", null);
|
||||
assertPostProcessorClass("pp name empty",
|
||||
DefaultObjectPropertyDataPostProcessor.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Add table
Reference in a new issue