All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opengis.cite.iso19136.ETSAssert Maven / Gradle / Ivy

Go to download

Checks GML application schemas or data sets for conformance to ISO 19136:2007.

There is a newer version: 3.2.1-r18
Show newest version
package org.opengis.cite.iso19136;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ListIterator;
import java.util.Map;
import java.util.logging.Level;

import javax.ws.rs.core.MediaType;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Validator;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.xerces.xs.XSAttributeDeclaration;
import org.apache.xerces.xs.XSAttributeUse;
import org.apache.xerces.xs.XSComplexTypeDefinition;
import org.apache.xerces.xs.XSConstants;
import org.apache.xerces.xs.XSElementDeclaration;
import org.apache.xerces.xs.XSModel;
import org.apache.xerces.xs.XSModelGroup;
import org.apache.xerces.xs.XSObjectList;
import org.apache.xerces.xs.XSParticle;
import org.apache.xerces.xs.XSTerm;
import org.opengis.cite.iso19136.util.NamespaceBindings;
import org.opengis.cite.iso19136.util.TestSuiteLogger;
import org.opengis.cite.iso19136.util.XMLSchemaModelUtils;
import org.opengis.cite.iso19136.util.XMLUtils;
import org.opengis.cite.validation.SchematronValidator;
import org.opengis.cite.validation.ValidationErrorHandler;
import org.testng.Assert;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Provides a set of custom assertion methods.
 */
public class ETSAssert {

	private ETSAssert() {
	}

	/**
	 * Asserts that the qualified name of a DOM Node matches the expected value.
	 * 
	 * @param node
	 *            The Node to check.
	 * @param qName
	 *            A QName object containing a namespace name (URI) and a local
	 *            part.
	 */
	public static void assertQualifiedName(Node node, QName qName) {
		Assert.assertEquals(node.getLocalName(), qName.getLocalPart(),
				ErrorMessage.get(ErrorMessageKeys.LOCAL_NAME));
		Assert.assertEquals(node.getNamespaceURI(), qName.getNamespaceURI(),
				ErrorMessage.get(ErrorMessageKeys.NAMESPACE_NAME));
	}

	/**
	 * Asserts that an XPath 1.0 expression holds true for the given evaluation
	 * context. The following standard namespace bindings do not need to be
	 * explicitly declared:
	 * 
	 * 
    *
  • ows: {@value org.opengis.cite.iso19136.Namespaces#OWS}
  • *
  • xlink: {@value org.opengis.cite.iso19136.Namespaces#XLINK}
  • *
  • gml: {@value org.opengis.cite.iso19136.general.GML32#NS_NAME}
  • *
* * @param expr * A valid XPath 1.0 expression. * @param context * The context node. * @param namespaceBindings * A collection of namespace bindings for the XPath expression, * where each entry maps a namespace URI (key) to a prefix * (value). It may be {@code null}. */ public static void assertXPath(String expr, Node context, Map namespaceBindings) { if (null == context) { throw new NullPointerException("Context node is null."); } NamespaceBindings bindings = NamespaceBindings.withStandardBindings(); bindings.addAllBindings(namespaceBindings); XPath xpath = XPathFactory.newInstance().newXPath(); xpath.setNamespaceContext(bindings); Boolean result; try { result = (Boolean) xpath.evaluate(expr, context, XPathConstants.BOOLEAN); } catch (XPathExpressionException xpe) { String msg = ErrorMessage .format(ErrorMessageKeys.XPATH_ERROR, expr); TestSuiteLogger.log(Level.WARNING, msg, xpe); throw new AssertionError(msg); } Assert.assertTrue( result, ErrorMessage.format(ErrorMessageKeys.XPATH_RESULT, context.getNodeName(), expr)); } /** * Asserts that an XML resource is schema-valid. * * @param validator * The Validator to use. * @param source * The XML Source to be validated. */ public static void assertSchemaValid(Validator validator, Source source) { ValidationErrorHandler errHandler = new ValidationErrorHandler(); validator.setErrorHandler(errHandler); try { validator.validate(source); } catch (Exception e) { throw new AssertionError(ErrorMessage.format( ErrorMessageKeys.XML_ERROR, e.getMessage())); } Assert.assertFalse(errHandler.errorsDetected(), ErrorMessage.format( ErrorMessageKeys.NOT_SCHEMA_VALID, errHandler.getErrorCount(), errHandler.toString())); } /** * Asserts that an XML resource satisfies all applicable constraints * specified in a Schematron (ISO 19757-3) schema. The "xslt2" query * language binding is supported. All patterns are checked. * * @param schemaRef * A URL that denotes the location of a Schematron schema. * @param xmlSource * The XML Source to be validated. */ public static void assertSchematronValid(URL schemaRef, Source xmlSource) { SchematronValidator validator; try { validator = new SchematronValidator(new StreamSource( schemaRef.toString()), "#ALL"); } catch (Exception e) { StringBuilder msg = new StringBuilder( "Failed to process Schematron schema at "); msg.append(schemaRef).append('\n'); msg.append(e.getMessage()); throw new AssertionError(msg); } DOMResult result = validator.validate(xmlSource); Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage .format(ErrorMessageKeys.NOT_SCHEMA_VALID, validator.getRuleViolationCount(), XMLUtils.writeNodeToString(result.getNode()))); } /** * Asserts that the given URL is resolvable; that is, it can be dereferenced * to obtain a resource representation that corresponds to an expected media * type. * * @param url * The URL to be dereferenced. * @param expectedMediaType * The expected media type of the representation; if not * specified any type of content is acceptable. */ public static void assertURLIsResolvable(URL url, MediaType expectedMediaType) { MediaType contentType; int contentLength = 0; // WARNING: Ignores HTTP redirects 3xx try { URLConnection urlConnection = url.openConnection(); urlConnection.connect(); contentLength = urlConnection.getContentLength(); contentType = MediaType.valueOf(urlConnection.getContentType()); } catch (IOException iox) { throw new AssertionError(String.format( "Failed to connect to URL %s \n %s", url.toString(), iox)); } Assert.assertTrue(contentLength > 0, ErrorMessage.get(ErrorMessageKeys.MISSING_ENTITY)); if (null != expectedMediaType) { Assert.assertEquals(contentType, expectedMediaType, ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_MEDIA_TYPE)); } } /** * Asserts that the content model of the given type definition mimics a GML * property type. The type is presumably not derived (by restriction) from * some base GML property type (gml:AssociationRoleType, gml:ReferenceType, * gml:InlinePropertyType). The following constraints apply to the content * model: * *
    *
  • if the content model is not empty, one of the following cases must be * true: *
      *
    1. it contains only one element declaration (the property value), which * may occur more than once in an array property type;
    2. *
    3. it contains a choice compositor that allows at most one element (an * allowable substitution) to occur.
    4. *
    *
  • *
  • if it is empty (or possibly empty), it includes the attribute group * gml:AssociationAttributeGroup.
  • *
  • includes the attribute group gml:OwnershipAttributeGroup.
  • *
  • does not contain a wildcard schema component.
  • *
  • the property value must substitute for the designated head element * (if specified).
  • *
* *
Sources
*
    *
  • ISO 19136:2007, cl. 7.2.3.3: abstractAssociationRole, * AssociationRoleType
  • *
  • ISO 19136:2007, cl. 7.2.3.7: abstractReference, ReferenceType
  • *
  • ISO 19136:2007, cl. 7.2.3.8: abstractInlineProperty, * InlinePropertyType
  • *
* * @param model * An XSModel object representing an XML Schema resource. * @param propertyDecl * An XSElementDeclaration object representing a property element * declaration. * @param head * The head of the substitution group to which the property value * belongs (may be {@code null}). */ public static void assertValidPropertyType(XSModel model, XSElementDeclaration propertyDecl, XSElementDeclaration head) { XSComplexTypeDefinition propTypeDef = (XSComplexTypeDefinition) propertyDecl .getTypeDefinition(); String localName = (propTypeDef.getAnonymous()) ? propertyDecl .getName() : propTypeDef.getName(); boolean isEmpty = (propTypeDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_EMPTY); XSObjectList attrUses = propTypeDef.getAttributeUses(); if (isEmpty) { Assert.assertNotNull( getAttributeUseByName(attrUses, new QName(Namespaces.XLINK, "href")), ErrorMessage.format( ErrorMessageKeys.ATTRIB_REQUIRED, "xlink:href", propTypeDef.getNamespace(), localName)); } else { XSParticle topParticle = propTypeDef.getParticle(); if (topParticle.getMinOccurs() == 0) { // may be empty, so a reference is required Assert.assertNotNull( getAttributeUseByName(attrUses, new QName( Namespaces.XLINK, "href")), "Property value has minOccurs = 0. " + ErrorMessage.format( ErrorMessageKeys.ATTRIB_REQUIRED, "xlink:href", propTypeDef.getNamespace(), localName)); } XSTerm term = topParticle.getTerm(); Assert.assertTrue(XSModelGroup.class.isInstance(term), ErrorMessage .format(ErrorMessageKeys.MODEL_GRP_EXPECTED, propTypeDef.getNamespace(), localName)); XSModelGroup group = (XSModelGroup) term; Assert.assertEquals(group.getCompositor(), XSModelGroup.COMPOSITOR_SEQUENCE, ErrorMessage.format( ErrorMessageKeys.SEQ_EXPECTED, propTypeDef.getNamespace(), localName)); boolean isArrayProperty = (topParticle.getMaxOccursUnbounded() || (topParticle .getMaxOccurs() > 1)); if (isArrayProperty) { Assert.assertNull( getAttributeUseByName(attrUses, new QName( Namespaces.XLINK, "href")), ErrorMessage .format(ErrorMessageKeys.ATTRIB_PROHIBITED, "xlink:href", propTypeDef.getNamespace(), localName)); } Assert.assertTrue(group.getParticles().size() == 1, ErrorMessage .format(ErrorMessageKeys.TOO_MANY_PARTS, propTypeDef.getNamespace(), localName)); XSParticle particle = (XSParticle) group.getParticles().item(0); switch (particle.getTerm().getType()) { case XSConstants.ELEMENT_DECLARATION: if (null != head) { XSElementDeclaration propDecl = (XSElementDeclaration) particle .getTerm(); Assert.assertTrue( XMLSchemaModelUtils.getElementsByAffiliation(model, head).contains(propDecl), ErrorMessage.format( ErrorMessageKeys.DISALLOWED_SUBSTITUTION, propDecl, head, XMLSchemaModelUtils.getQName(propTypeDef))); } break; case XSConstants.MODEL_GROUP: XSModelGroup modelGroup = XSModelGroup.class.cast(particle .getTerm()); if (modelGroup.getCompositor() != XSModelGroup.COMPOSITOR_CHOICE) { throw new AssertionError( "Only a choice compositor is allowed. Found " + particle.getTerm().getClass().getName()); } @SuppressWarnings("unchecked") ListIterator itr = modelGroup.getParticles() .listIterator(); while (itr.hasNext()) { XSParticle xsParticle = itr.next(); if (xsParticle.getTerm().getType() != XSConstants.ELEMENT_DECLARATION) { throw new AssertionError( "Only element declarations are allowed in choice compositor."); } if (null != head) { XSElementDeclaration elemDecl = (XSElementDeclaration) xsParticle .getTerm(); Assert.assertTrue(XMLSchemaModelUtils .getElementsByAffiliation(model, head) .contains(elemDecl), ErrorMessage.format( ErrorMessageKeys.DISALLOWED_SUBSTITUTION, elemDecl, head, XMLSchemaModelUtils.getQName(propTypeDef))); } } break; default: throw new AssertionError( "Wildcard component not permitted in property type: " + XMLSchemaModelUtils.getQName(propTypeDef)); } } } /** * Asserts that the given XML entity contains the expected number of * descendant elements having the specified name. * * @param xmlEntity * A Document representing an XML entity. * @param elementName * The qualified name of the element. * @param expectedCount * The expected number of occurrences. */ public static void assertDescendantElementCount(Document xmlEntity, QName elementName, int expectedCount) { NodeList features = xmlEntity.getElementsByTagNameNS( elementName.getNamespaceURI(), elementName.getLocalPart()); Assert.assertEquals(features.getLength(), expectedCount, String.format( "Unexpected number of %s descendant elements.", elementName)); } /** * Returns an attribute use schema component identified by qualified name. * * @param attrUses * An XSObjectList containing a collection of XSAttributeUse * items. * @param qName * The qualified name of the attribute declaration to seek. The * namespace name may be empty (""). * @return The matching XSAttributeUse object, or {@code null} if one cannot * be found. */ private static XSAttributeUse getAttributeUseByName(XSObjectList attrUses, QName qName) { XSAttributeUse attrUse = null; for (int i = 0; i < attrUses.getLength(); i++) { XSAttributeUse item = (XSAttributeUse) attrUses.item(i); XSAttributeDeclaration attrDecl = item.getAttrDeclaration(); if (attrDecl.getName().equals(qName.getLocalPart())) { if (attrDecl.getNamespace() != null && !attrDecl.getNamespace().equals( qName.getNamespaceURI())) { // XSObject.getNamespace() may return null!! continue; } attrUse = item; break; } } return attrUse; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy