package org.archstudio.schematron.core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import org.archstudio.archlight.ArchlightElementIdentifier;
import org.archstudio.archlight.ArchlightIssue;
import org.archstudio.archlight.ArchlightTestResult;
import org.archstudio.xarchadt.IXArchADT;
import org.archstudio.xarchadt.ObjRef;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
public class SchematronTestResultParser {
public static final String CONTENT_ELEMENT_DELIMITER_REGEX = "\\|\\*\\|";
//Returns an array of objects. Each Object is either a TronTestResult
//or a SchematronTestException in case the test result parsing failed.
public static List<? extends Object> parseTestResults(IXArchADT xarch, ObjRef documentRef, String toolID,
Document schematronProcessingResults) {
List<Object> testResultList = new ArrayList<Object>();
List<TestResultSnippet> resultSnippets = null;
try {
resultSnippets = parseTestResultSnippets(schematronProcessingResults);
for (TestResultSnippet resultSnippet : resultSnippets) {
try {
ArchlightTestResult testResult = parseResultSnippet(xarch, documentRef, toolID, resultSnippet);
testResultList.add(testResult);
}
catch (SchematronTestException ste) {
testResultList.add(ste);
}
}
}
catch (SchematronTestException ste) {
testResultList.add(ste);
}
return Collections.unmodifiableList(testResultList);
}
private static ArchlightTestResult parseResultSnippet(IXArchADT xarch, ObjRef documentRef, String toolID,
TestResultSnippet snippet) throws SchematronTestException {
String testUID = snippet.activePatternElement.getAttribute("id");
if (testUID == null) {
String desc = "Test result had no UID";
String name = snippet.activePatternElement.getAttribute("name");
if (name != null) {
desc += ": " + name;
}
throw new SchematronTestException(desc);
}
List<ArchlightIssue> archlightIssues = new ArrayList<ArchlightIssue>(snippet.failedAssertElements.length);
for (Element failedAssertElt : snippet.failedAssertElements) {
Element contentElt = (Element) failedAssertElt.getFirstChild();
String content = getElementContents(contentElt);
if (content == null || content.equals("")) {
SchematronTestException ste = new SchematronTestException("Failed assert had no content.");
ste.setTestUID(testUID);
throw ste;
}
ContentProperties contentProperties = ContentProperties.create(content);
//We have the testUID
//We have the documentRef
//We have the toolID,
int severity = ArchlightIssue.SEVERITY_ERROR;
String severityString = contentProperties.getSeverity();
if (severityString != null) {
if (severityString.toLowerCase().equals("warning")) {
severity = ArchlightIssue.SEVERITY_WARNING;
}
else if (severityString.toLowerCase().equals("info")) {
severity = ArchlightIssue.SEVERITY_INFO;
}
}
//We have the severity.
String headline = contentProperties.getText();
if (headline == null) {
SchematronTestException ste = new SchematronTestException("Failed assert had no text.");
ste.setTestUID(testUID);
throw ste;
}
//We have the headline.
String detailedDescription = contentProperties.getDetail();
//if(detailedDescription == null){
// detailedDescription = "[No additional detail.]";
//}
//We have the detail
String iconHref = contentProperties.getIconHref();
//We have the icon href
List<ArchlightElementIdentifier> elementIdentifiers = contentProperties.getElementIdentifiers();
//We have the element identifiers.
if (elementIdentifiers.isEmpty()) {
//Can't find it. Try resolving the XPath?
}
/*
* else{ String id = elementIdentifiers[0].getElementID(); ObjRef ref = xarch.getByID(documentRef, id);
* System.out.println(ref); System.out.println(xarch.getXArchPath(ref)); }
*/
archlightIssues.add(new ArchlightIssue(testUID, documentRef, toolID, severity, headline,
detailedDescription, iconHref, elementIdentifiers));
}
return new ArchlightTestResult(documentRef, testUID, archlightIssues);
}
private static String getElementContents(Element elt) {
elt.normalize();
NodeList childNodes = elt.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
if (childNodes.item(i) instanceof Text) {
return ((Text) childNodes.item(i)).getNodeValue();
}
}
return "";
}
//The test results file is organized weirdly. It looks like:
//<ns...>
//<ns...> ...
//<active-pattern ... />
//<failed-assert ... />
//<failed-assert ... />
//<active-pattern ... />
//<failed-assert ... />
//...
//So all the failed assertions are at the top level but split
//up by active-patterns. There will always be an active-pattern
//heading even if there are no failed-asserts
private static List<TestResultSnippet> parseTestResultSnippets(Document schematronProcessingResults)
throws SchematronTestException {
List<TestResultSnippet> testResultSnippetList = new ArrayList<TestResultSnippet>();
Element rootElement = schematronProcessingResults.getDocumentElement();
NodeList topLevelElements = rootElement.getChildNodes();
Element activePatternElement = null;
List<Element> failedAssertElementList = new ArrayList<Element>();
for (int i = 0; i < topLevelElements.getLength(); i++) {
Node childNode = topLevelElements.item(i);
if (childNode instanceof Element) {
Element childElt = (Element) childNode;
String tagName = childElt.getLocalName();
if (tagName.equals("active-pattern")) {
if (activePatternElement != null) {
//Save the old snippet
TestResultSnippet snippet = new TestResultSnippet();
snippet.activePatternElement = activePatternElement;
snippet.failedAssertElements = failedAssertElementList.toArray(new Element[0]);
testResultSnippetList.add(snippet);
}
activePatternElement = childElt;
failedAssertElementList.clear();
}
if (tagName.equals("failed-assert")) {
failedAssertElementList.add(childElt);
}
}
}
if (activePatternElement != null) {
//Save the old snippet
TestResultSnippet snippet = new TestResultSnippet();
snippet.activePatternElement = activePatternElement;
snippet.failedAssertElements = failedAssertElementList.toArray(new Element[0]);
testResultSnippetList.add(snippet);
}
return Collections.unmodifiableList(testResultSnippetList);
}
private static class TestResultSnippet {
public Element activePatternElement;
public Element[] failedAssertElements;
}
private static class ContentProperties {
private final Properties properties;
public ContentProperties(Properties properties) {
this.properties = properties;
}
public String getSeverity() {
return properties.getProperty("severity");
}
public String getIconHref() {
return properties.getProperty("iconHref");
}
public String getDetail() {
return properties.getProperty("detail");
}
public String getText() {
return properties.getProperty("text");
}
public List<ArchlightElementIdentifier> getElementIdentifiers() {
List<ArchlightElementIdentifier> elementIdentifierList = new ArrayList<ArchlightElementIdentifier>();
//Get the default ID
String id = properties.getProperty("id");
if (id != null) {
String iddesc = properties.getProperty("iddesc");
ArchlightElementIdentifier eltIdentifier = new ArchlightElementIdentifier(id, iddesc);
elementIdentifierList.add(eltIdentifier);
}
int index = 0;
while (true) {
String indexid = properties.getProperty("id" + index);
if (indexid != null) {
String indexiddesc = properties.getProperty("iddesc" + index);
ArchlightElementIdentifier eltIdentifier = new ArchlightElementIdentifier(indexid, indexiddesc);
elementIdentifierList.add(eltIdentifier);
index++;
}
else {
break;
}
}
return Collections.unmodifiableList(elementIdentifierList);
}
public static ContentProperties create(String content) throws SchematronTestException {
Properties newProperties = new Properties();
String[] propertyElements = content.split(CONTENT_ELEMENT_DELIMITER_REGEX);
for (String propertyElement : propertyElements) {
int equalsIndex = propertyElement.indexOf("=");
if (equalsIndex == -1) {
if (newProperties.containsKey("text")) {
throw new SchematronTestException("Test result has multiple segments with name 'text'");
}
newProperties.put("text", propertyElement.trim());
}
else {
String propName = propertyElement.substring(0, equalsIndex).trim();
String propValue = propertyElement.substring(equalsIndex + 1).trim();
if (newProperties.containsKey(propName)) {
throw new SchematronTestException("Test result has multiple segments with name '" + propName
+ "'");
}
newProperties.put(propName, propValue);
}
}
return new ContentProperties(newProperties);
}
}
}