/** * Walk the XML tree and validate all nodes. * * @param received * @param source * @param validationContext */ private void validateXmlTree( Node received, Node source, XmlMessageValidationContext validationContext, NamespaceContext namespaceContext, TestContext context) { switch (received.getNodeType()) { case Node.DOCUMENT_TYPE_NODE: doDocumentTypeDefinition(received, source, validationContext, namespaceContext, context); break; case Node.DOCUMENT_NODE: validateXmlTree( received.getFirstChild(), source.getFirstChild(), validationContext, namespaceContext, context); break; case Node.ELEMENT_NODE: doElement(received, source, validationContext, namespaceContext, context); break; case Node.TEXT_NODE: case Node.CDATA_SECTION_NODE: doText(received, source); break; case Node.ATTRIBUTE_NODE: throw new IllegalStateException(); case Node.COMMENT_NODE: doComment(received); break; case Node.PROCESSING_INSTRUCTION_NODE: doPI(received); break; } }
/** * Handle processing instruction during validation. * * @param received */ private void doPI(Node received) { log.info( "Ignored processing instruction (" + received.getLocalName() + "=" + received.getNodeValue() + ")"); }
/** * Perform validation on namespace qualified attribute values if present. This includes the * validation of namespace presence and equality. * * @param receivedElement * @param receivedAttribute * @param sourceElement * @param sourceAttribute */ private void doNamespaceQualifiedAttributeValidation( Node receivedElement, Node receivedAttribute, Node sourceElement, Node sourceAttribute) { String receivedValue = receivedAttribute.getNodeValue(); String sourceValue = sourceAttribute.getNodeValue(); if (receivedValue.contains(":") && sourceValue.contains(":")) { // value has namespace prefix set, do special QName validation String receivedPrefix = receivedValue.substring(0, receivedValue.indexOf(':')); String sourcePrefix = sourceValue.substring(0, sourceValue.indexOf(':')); Map<String, String> receivedNamespaces = XMLUtils.lookupNamespaces(receivedAttribute.getOwnerDocument()); receivedNamespaces.putAll(XMLUtils.lookupNamespaces(receivedElement)); if (receivedNamespaces.containsKey(receivedPrefix)) { Map<String, String> sourceNamespaces = XMLUtils.lookupNamespaces(sourceAttribute.getOwnerDocument()); sourceNamespaces.putAll(XMLUtils.lookupNamespaces(sourceElement)); if (sourceNamespaces.containsKey(sourcePrefix)) { Assert.isTrue( sourceNamespaces.get(sourcePrefix).equals(receivedNamespaces.get(receivedPrefix)), ValidationUtils.buildValueMismatchErrorMessage( "Values not equal for attribute value namespace '" + receivedValue + "'", sourceNamespaces.get(sourcePrefix), receivedNamespaces.get(receivedPrefix))); // remove namespace prefixes as they must not form equality receivedValue = receivedValue.substring((receivedPrefix + ":").length()); sourceValue = sourceValue.substring((sourcePrefix + ":").length()); } else { throw new ValidationException( "Received attribute value '" + receivedAttribute.getLocalName() + "' describes namespace qualified attribute value," + " control value '" + sourceValue + "' does not"); } } } Assert.isTrue( receivedValue.equals(sourceValue), ValidationUtils.buildValueMismatchErrorMessage( "Values not equal for attribute '" + receivedAttribute.getLocalName() + "'", sourceValue, receivedValue)); }
/** * Checks whether the given node contains a validation matcher * * @param node * @return true if node value contains validation matcher, false if not */ private boolean isValidationMatcherExpression(Node node) { switch (node.getNodeType()) { case Node.ELEMENT_NODE: return node.getFirstChild() != null && StringUtils.hasText(node.getFirstChild().getNodeValue()) && ValidationMatcherUtils.isValidationMatcherExpression( node.getFirstChild().getNodeValue().trim()); case Node.ATTRIBUTE_NODE: return StringUtils.hasText(node.getNodeValue()) && ValidationMatcherUtils.isValidationMatcherExpression(node.getNodeValue().trim()); default: return false; // validation matchers makes no sense } }
private void doElementNameValidation(Node received, Node source) { // validate element name if (log.isDebugEnabled()) { log.debug( "Validating element: " + received.getLocalName() + " (" + received.getNamespaceURI() + ")"); } Assert.isTrue( received.getLocalName().equals(source.getLocalName()), ValidationUtils.buildValueMismatchErrorMessage( "Element names not equal", source.getLocalName(), received.getLocalName())); }
/** * Handle comment node during validation. * * @param received */ private void doComment(Node received) { log.info("Ignored comment node (" + received.getNodeValue() + ")"); }
/** * Handle attribute node during validation. * * @param receivedElement * @param receivedAttribute * @param sourceElement * @param validationContext */ private void doAttribute( Node receivedElement, Node receivedAttribute, Node sourceElement, XmlMessageValidationContext validationContext, NamespaceContext namespaceContext, TestContext context) { if (receivedAttribute.getNodeName().startsWith(XMLConstants.XMLNS_ATTRIBUTE)) { return; } String receivedAttributeName = receivedAttribute.getLocalName(); if (log.isDebugEnabled()) { log.debug( "Validating attribute: " + receivedAttributeName + " (" + receivedAttribute.getNamespaceURI() + ")"); } NamedNodeMap sourceAttributes = sourceElement.getAttributes(); Node sourceAttribute = sourceAttributes.getNamedItemNS(receivedAttribute.getNamespaceURI(), receivedAttributeName); Assert.isTrue( sourceAttribute != null, "Attribute validation failed for element '" + receivedElement.getLocalName() + "', unknown attribute " + receivedAttributeName + " (" + receivedAttribute.getNamespaceURI() + ")"); if (XmlValidationUtils.isAttributeIgnored( receivedElement, receivedAttribute, sourceAttribute, validationContext.getIgnoreExpressions(), namespaceContext)) { return; } String receivedValue = receivedAttribute.getNodeValue(); String sourceValue = sourceAttribute.getNodeValue(); if (isValidationMatcherExpression(sourceAttribute)) { ValidationMatcherUtils.resolveValidationMatcher( sourceAttribute.getNodeName(), receivedAttribute.getNodeValue().trim(), sourceAttribute.getNodeValue().trim(), context); } else if (receivedValue.contains(":") && sourceValue.contains(":")) { doNamespaceQualifiedAttributeValidation( receivedElement, receivedAttribute, sourceElement, sourceAttribute); } else { Assert.isTrue( receivedValue.equals(sourceValue), ValidationUtils.buildValueMismatchErrorMessage( "Values not equal for attribute '" + receivedAttributeName + "'", sourceValue, receivedValue)); } if (log.isDebugEnabled()) { log.debug("Attribute '" + receivedAttributeName + "'='" + receivedValue + "': OK"); } }
/** * Handle text node during validation. * * @param received * @param source */ private void doText(Node received, Node source) { if (log.isDebugEnabled()) { log.debug("Validating node value for element: " + received.getParentNode()); } if (received.getNodeValue() != null) { Assert.isTrue( source.getNodeValue() != null, ValidationUtils.buildValueMismatchErrorMessage( "Node value not equal for element '" + received.getParentNode().getLocalName() + "'", null, received.getNodeValue().trim())); Assert.isTrue( received.getNodeValue().trim().equals(source.getNodeValue().trim()), ValidationUtils.buildValueMismatchErrorMessage( "Node value not equal for element '" + received.getParentNode().getLocalName() + "'", source.getNodeValue().trim(), received.getNodeValue().trim())); } else { Assert.isTrue( source.getNodeValue() == null, ValidationUtils.buildValueMismatchErrorMessage( "Node value not equal for element '" + received.getParentNode().getLocalName() + "'", source.getNodeValue().trim(), null)); } if (log.isDebugEnabled()) { log.debug("Node value '" + received.getNodeValue().trim() + "': OK"); } }
/** * Handle element node. * * @param received * @param source * @param validationContext */ private void doElement( Node received, Node source, XmlMessageValidationContext validationContext, NamespaceContext namespaceContext, TestContext context) { doElementNameValidation(received, source); doElementNamespaceValidation(received, source); // check if element is ignored either by xpath or by ignore placeholder in source message if (XmlValidationUtils.isElementIgnored( source, received, validationContext.getIgnoreExpressions(), namespaceContext)) { return; } // work on attributes if (log.isDebugEnabled()) { log.debug("Validating attributes for element: " + received.getLocalName()); } NamedNodeMap receivedAttr = received.getAttributes(); NamedNodeMap sourceAttr = source.getAttributes(); Assert.isTrue( countAttributes(receivedAttr) == countAttributes(sourceAttr), ValidationUtils.buildValueMismatchErrorMessage( "Number of attributes not equal for element '" + received.getLocalName() + "'", countAttributes(sourceAttr), countAttributes(receivedAttr))); for (int i = 0; i < receivedAttr.getLength(); i++) { doAttribute( received, receivedAttr.item(i), source, validationContext, namespaceContext, context); } // check if validation matcher on element is specified if (isValidationMatcherExpression(source)) { ValidationMatcherUtils.resolveValidationMatcher( source.getNodeName(), received.getFirstChild().getNodeValue().trim(), source.getFirstChild().getNodeValue().trim(), context); return; } // work on child nodes NodeList receivedChilds = received.getChildNodes(); NodeList sourceChilds = source.getChildNodes(); Assert.isTrue( receivedChilds.getLength() == sourceChilds.getLength(), ValidationUtils.buildValueMismatchErrorMessage( "Number of child elements not equal for element '" + received.getLocalName() + "'", sourceChilds.getLength(), receivedChilds.getLength())); for (int i = 0; i < receivedChilds.getLength(); i++) { this.validateXmlTree( receivedChilds.item(i), sourceChilds.item(i), validationContext, namespaceContext, context); } if (log.isDebugEnabled()) { log.debug( "Validation successful for element: " + received.getLocalName() + " (" + received.getNamespaceURI() + ")"); } }
/** * Handle document type definition with validation of publicId and systemId. * * @param received * @param source * @param validationContext * @param namespaceContext */ private void doDocumentTypeDefinition( Node received, Node source, XmlMessageValidationContext validationContext, NamespaceContext namespaceContext, TestContext context) { Assert.isTrue( source instanceof DocumentType, "Missing document type definition in expected xml fragment"); DocumentType receivedDTD = (DocumentType) received; DocumentType sourceDTD = (DocumentType) source; if (log.isDebugEnabled()) { log.debug( "Validating document type definition: " + receivedDTD.getPublicId() + " (" + receivedDTD.getSystemId() + ")"); } if (!StringUtils.hasText(sourceDTD.getPublicId())) { Assert.isNull( receivedDTD.getPublicId(), ValidationUtils.buildValueMismatchErrorMessage( "Document type public id not equal", sourceDTD.getPublicId(), receivedDTD.getPublicId())); } else if (sourceDTD.getPublicId().trim().equals(CitrusConstants.IGNORE_PLACEHOLDER)) { if (log.isDebugEnabled()) { log.debug( "Document type public id: '" + receivedDTD.getPublicId() + "' is ignored by placeholder '" + CitrusConstants.IGNORE_PLACEHOLDER + "'"); } } else { Assert.isTrue( StringUtils.hasText(receivedDTD.getPublicId()) && receivedDTD.getPublicId().equals(sourceDTD.getPublicId()), ValidationUtils.buildValueMismatchErrorMessage( "Document type public id not equal", sourceDTD.getPublicId(), receivedDTD.getPublicId())); } if (!StringUtils.hasText(sourceDTD.getSystemId())) { Assert.isNull( receivedDTD.getSystemId(), ValidationUtils.buildValueMismatchErrorMessage( "Document type system id not equal", sourceDTD.getSystemId(), receivedDTD.getSystemId())); } else if (sourceDTD.getSystemId().trim().equals(CitrusConstants.IGNORE_PLACEHOLDER)) { if (log.isDebugEnabled()) { log.debug( "Document type system id: '" + receivedDTD.getSystemId() + "' is ignored by placeholder '" + CitrusConstants.IGNORE_PLACEHOLDER + "'"); } } else { Assert.isTrue( StringUtils.hasText(receivedDTD.getSystemId()) && receivedDTD.getSystemId().equals(sourceDTD.getSystemId()), ValidationUtils.buildValueMismatchErrorMessage( "Document type system id not equal", sourceDTD.getSystemId(), receivedDTD.getSystemId())); } validateXmlTree( received.getNextSibling(), source.getNextSibling(), validationContext, namespaceContext, context); }
private void doElementNamespaceValidation(Node received, Node source) { // validate element namespace if (log.isDebugEnabled()) { log.debug("Validating namespace for element: " + received.getLocalName()); } if (received.getNamespaceURI() != null) { Assert.isTrue( source.getNamespaceURI() != null, ValidationUtils.buildValueMismatchErrorMessage( "Element namespace not equal for element '" + received.getLocalName() + "'", null, received.getNamespaceURI())); Assert.isTrue( received.getNamespaceURI().equals(source.getNamespaceURI()), ValidationUtils.buildValueMismatchErrorMessage( "Element namespace not equal for element '" + received.getLocalName() + "'", source.getNamespaceURI(), received.getNamespaceURI())); } else { Assert.isTrue( source.getNamespaceURI() == null, ValidationUtils.buildValueMismatchErrorMessage( "Element namespace not equal for element '" + received.getLocalName() + "'", source.getNamespaceURI(), null)); } }