package com.thoughtworks.winstonwolfe.validators; import com.thoughtworks.winstonwolfe.config.WinstonConfig; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import java.util.*; public class RecursiveSiblingValidation implements Validation { private final Node node; private final String key; private final WinstonConfig expectations; private final WinstonConfig selectors; private final String xpath; public static final String XPATH_SEPARATOR = "/"; private ValidationResults results = new ValidationResults(); private final List<String> keysToIgnore = new ArrayList<String>(Arrays.asList("root")); public RecursiveSiblingValidation(Node node, String key, WinstonConfig expectations, WinstonConfig selectors, String xpath) { this.node = node; this.key = key; this.expectations = expectations; this.selectors = selectors; this.xpath = xpath; } @Override public void validate() throws XPathExpressionException { WinstonConfig siblingExpectations = expectations.getSubConfig(key); WinstonConfig siblingSelectors = selectors.getSubConfig(key); if (!siblingSelectors.exists("root")) { throw new RuntimeException(String.format("Sibling validation '%s' does not contain an element called 'root'", key)); } String siblingRootXpath = siblingSelectors.getString("root"); NodeList nodesMatchingRoot = (NodeList) XPathFactory.newInstance().newXPath().evaluate(siblingRootXpath, node, XPathConstants.NODESET); boolean siblingIsValid = false; for (int i = 0; i < nodesMatchingRoot.getLength(); i++) { Node node = nodesMatchingRoot.item(i); ValidationResults resultsForNode = new ValidationResults(); Set<String> keys = siblingExpectations.getMap().keySet(); keys.removeAll(keysToIgnore); for (String siblingKey : keys) { ValidationFactory validatorFactory = new ValidationFactory(node, siblingKey, siblingExpectations, siblingSelectors, getParentXpathForChildren(siblingRootXpath)); Validation validation = validatorFactory.buildValidation(); validation.validate(); resultsForNode.addValidationResults(validation.getResults()); } if (resultsForNode.hasFailure()) { continue; } siblingIsValid = true; results.getSuccessMessages().addAll(resultsForNode.getSuccessMessages()); } if (!siblingIsValid) { results.getFailureMessages().add(String.format("The set of nodes at '%s' with xpath '%s' had '%s' entries. None of them had the values %s", key, getParentXpathForChildren(siblingRootXpath), nodesMatchingRoot.getLength(), getSiblingExpectationsNotMetString(siblingExpectations.getFlatStringMap()))); } return; } private String getParentXpathForChildren (String siblingRootXpath) { return xpath + siblingRootXpath + XPATH_SEPARATOR; } private String getSiblingExpectationsNotMetString(Map<String, String> expectationsMap) { Set<String> keys = expectationsMap.keySet(); keys.removeAll(keysToIgnore); List<String> expectationStrings = new ArrayList<String>(); for (String siblingKey : keys) { expectationStrings.add(String.format("'%s' as '%s'", siblingKey, expectationsMap.get(siblingKey))); } return Arrays.toString(expectationStrings.toArray()); } @Override public ValidationResults getResults() { return results; } }