| /* XMLSchemaValidatorHandler.java -- |
| Copyright (C) 2006 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| package gnu.xml.validation.xmlschema; |
| |
| import java.util.ArrayList; |
| import java.util.LinkedList; |
| import javax.xml.XMLConstants; |
| import javax.xml.namespace.QName; |
| import javax.xml.validation.TypeInfoProvider; |
| import javax.xml.validation.ValidatorHandler; |
| import org.relaxng.datatype.DatatypeException; |
| import org.relaxng.datatype.DatatypeLibrary; |
| import org.relaxng.datatype.helpers.DatatypeLibraryLoader; |
| import org.w3c.dom.TypeInfo; |
| import org.w3c.dom.ls.LSResourceResolver; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.ContentHandler; |
| import org.xml.sax.ErrorHandler; |
| import org.xml.sax.Locator; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.ext.Attributes2Impl; |
| import org.xml.sax.helpers.NamespaceSupport; |
| import gnu.xml.validation.datatype.SimpleType; |
| import gnu.xml.validation.datatype.Type; |
| |
| /** |
| * Streaming validator. |
| * |
| * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> |
| */ |
| final class XMLSchemaValidatorHandler |
| extends ValidatorHandler |
| { |
| |
| final XMLSchema schema; |
| final TypeInfoProvider typeInfoProvider; |
| final NamespaceSupport namespaceSupport; |
| final DatatypeLibrary typeLibrary; |
| Locator loc; |
| ContentHandler contentHandler; |
| ErrorHandler errorHandler; |
| LSResourceResolver resourceResolver; |
| final LinkedList context; // element context |
| final ArrayList attributes; // attribute context; |
| |
| XMLSchemaValidatorHandler(XMLSchema schema) |
| { |
| this.schema = schema; |
| typeInfoProvider = new XMLSchemaTypeInfoProvider(this); |
| namespaceSupport = new NamespaceSupport(); |
| context = new LinkedList(); |
| attributes = new ArrayList(); |
| final String ns = XMLConstants.W3C_XML_SCHEMA_NS_URI; |
| typeLibrary = new DatatypeLibraryLoader().createDatatypeLibrary(ns); |
| } |
| |
| public ContentHandler getContentHandler() |
| { |
| return contentHandler; |
| } |
| |
| public void setContentHandler(ContentHandler contentHandler) |
| { |
| this.contentHandler = contentHandler; |
| } |
| |
| public ErrorHandler getErrorHandler() |
| { |
| return errorHandler; |
| } |
| |
| public void setErrorHandler(ErrorHandler errorHandler) |
| { |
| this.errorHandler = errorHandler; |
| } |
| |
| public LSResourceResolver getResourceResolver() |
| { |
| return resourceResolver; |
| } |
| |
| public void setResourceResolver(LSResourceResolver resourceResolver) |
| { |
| this.resourceResolver = resourceResolver; |
| } |
| |
| public TypeInfoProvider getTypeInfoProvider() |
| { |
| return typeInfoProvider; |
| } |
| |
| TypeInfo getElementTypeInfo() |
| { |
| return (XMLSchemaElementTypeInfo) context.getFirst(); |
| } |
| |
| TypeInfo getAttributeTypeInfo(int index) |
| { |
| return (XMLSchemaAttributeTypeInfo) attributes.get(index); |
| } |
| |
| boolean isIdAttribute(int index) |
| { |
| XMLSchemaAttributeTypeInfo typeInfo = |
| (XMLSchemaAttributeTypeInfo) attributes.get(index); |
| return typeInfo.id; |
| } |
| |
| boolean isSpecified(int index) |
| { |
| XMLSchemaAttributeTypeInfo typeInfo = |
| (XMLSchemaAttributeTypeInfo) attributes.get(index); |
| return typeInfo.specified; |
| } |
| |
| public void setDocumentLocator(Locator locator) |
| { |
| loc = locator; |
| if (contentHandler != null) |
| { |
| contentHandler.setDocumentLocator(locator); |
| } |
| } |
| |
| public void startDocument() |
| throws SAXException |
| { |
| namespaceSupport.reset(); |
| context.clear(); |
| attributes.clear(); |
| if (contentHandler != null) |
| { |
| contentHandler.startDocument(); |
| } |
| } |
| |
| public void endDocument() |
| throws SAXException |
| { |
| if (contentHandler != null) |
| { |
| contentHandler.endDocument(); |
| } |
| } |
| |
| public void startPrefixMapping(String prefix, String uri) |
| throws SAXException |
| { |
| namespaceSupport.declarePrefix(prefix, uri); |
| if (contentHandler != null) |
| { |
| contentHandler.startPrefixMapping(prefix, uri); |
| } |
| } |
| |
| public void endPrefixMapping(String prefix) |
| throws SAXException |
| { |
| if (contentHandler != null) |
| { |
| contentHandler.endPrefixMapping(prefix); |
| } |
| } |
| |
| public void startElement(String uri, String localName, String qName, |
| Attributes atts) |
| throws SAXException |
| { |
| namespaceSupport.pushContext(); |
| QName name = new QName(uri, localName); |
| ElementDeclaration decl = |
| (ElementDeclaration) schema.elementDeclarations.get(name); |
| // Validation Rule: Element Locally Valid (Element) |
| String xsiType = |
| atts.getValue(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type"); |
| xsiType = xsiType.trim(); // normalise |
| String xsiNil = |
| atts.getValue(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil"); |
| Type type = decl.datatype; |
| if (xsiType.length() > 0) |
| { |
| try |
| { |
| Type specifiedType = resolveType(xsiType); |
| // TODO 4.3 |
| type = specifiedType; |
| } |
| catch (DatatypeException e) // 4.1, 4.2 |
| { |
| ValidationException e2 = |
| new ValidationException("Can't resolve type " + xsiType, |
| loc); |
| e2.initCause(e); |
| throw e2; |
| } |
| } |
| XMLSchemaElementTypeInfo typeInfo = |
| new XMLSchemaElementTypeInfo(schema, decl, type); |
| if (decl == null) // 1 |
| { |
| throw new ValidationException("No declaration for " + name, loc); |
| } |
| if (decl.isAbstract) // 2 |
| { |
| throw new ValidationException("Declaration for " + name + |
| " is abstract", loc); |
| } |
| if (xsiNil.length() > 0) |
| { |
| if (!decl.nillable) // 3.1 |
| { |
| throw new ValidationException("Declaration for " + name + |
| " is nillable but xsi:nil present", |
| loc); |
| } |
| else if ("true".equals(xsiNil)) // 3.2 |
| { |
| typeInfo.nil = true; |
| if (decl.type == XMLSchema.CONSTRAINT_FIXED) // 3.2.2 |
| { |
| throw new ValidationException("Declaration for " + name + |
| " is fixed but xsi:nil is true", |
| loc); |
| } |
| } |
| } |
| // TODO 5, 6, 7 |
| |
| // parent |
| if (!context.isEmpty()) |
| { |
| XMLSchemaElementTypeInfo parent = |
| (XMLSchemaElementTypeInfo) context.getFirst(); |
| if (parent.nil) // Element Locally Valid (Element) 3.2.1 |
| { |
| throw new ValidationException("Parent of " + qName + |
| " is declared xsi:nil", loc); |
| } |
| // TODO |
| } |
| context.addFirst(typeInfo); |
| // attributes |
| int len = atts.getLength(); |
| Attributes2Impl atts2 = new Attributes2Impl(); |
| int count = 0; |
| for (int i = 0; i < len; i++) |
| { |
| String attUri = atts.getURI(i); |
| String attLocalName = atts.getLocalName(i); |
| String attQName = atts.getQName(i); |
| String attValue = atts.getValue(i); |
| |
| if (XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI.equals(attUri)) |
| { |
| continue; // ? |
| } |
| |
| QName attName = new QName(attUri, attLocalName); |
| AttributeDeclaration attDecl = |
| (AttributeDeclaration) schema.attributeDeclarations.get(attName); |
| boolean declared = (attDecl != null); |
| |
| String attType = (attDecl != null) ? |
| attDecl.datatype.toString() : "CDATA"; |
| XMLSchemaAttributeTypeInfo attTypeInfo = |
| new XMLSchemaAttributeTypeInfo(schema, attDecl, true); |
| attributes.add(attTypeInfo); |
| |
| atts2.addAttribute(attUri, attLocalName, attQName, attType, attValue); |
| atts2.setDeclared(count, declared); |
| atts2.setSpecified(count, true); |
| count++; |
| } |
| // add defaulted attributes to atts2 |
| // TODO |
| // atts2.setSpecified(count, false); |
| if (contentHandler != null) |
| { |
| contentHandler.startElement(uri, localName, qName, atts2); |
| } |
| } |
| |
| public void endElement(String uri, String localName, String qName) |
| throws SAXException |
| { |
| // TODO check all required have been seen |
| context.removeFirst(); |
| attributes.clear(); |
| namespaceSupport.popContext(); |
| if (contentHandler != null) |
| { |
| contentHandler.endElement(uri, localName, qName); |
| } |
| } |
| |
| public void characters(char[] ch, int start, int length) |
| throws SAXException |
| { |
| XMLSchemaElementTypeInfo parent = |
| (XMLSchemaElementTypeInfo) context.getFirst(); |
| if (parent.nil) // Element Locally Valid (Element) 3.2.1 |
| { |
| throw new ValidationException(parent.decl.name.toString() + |
| " is declared xsi:nil", |
| loc); |
| } |
| // TODO |
| if (contentHandler != null) |
| { |
| contentHandler.characters(ch, start, length); |
| } |
| } |
| |
| public void ignorableWhitespace(char[] ch, int start, int length) |
| throws SAXException |
| { |
| if (contentHandler != null) |
| { |
| contentHandler.ignorableWhitespace(ch, start, length); |
| } |
| } |
| |
| public void processingInstruction(String target, String data) |
| throws SAXException |
| { |
| if (contentHandler != null) |
| { |
| contentHandler.processingInstruction(target, data); |
| } |
| } |
| |
| public void skippedEntity(String name) |
| throws SAXException |
| { |
| if (contentHandler != null) |
| { |
| contentHandler.skippedEntity(name); |
| } |
| } |
| |
| Type resolveType(String value) |
| throws DatatypeException |
| { |
| QName name = QName.valueOf(value); |
| String prefix = name.getPrefix(); |
| String localName = name.getLocalPart(); |
| if (prefix != null && prefix.length() > 0) |
| { |
| String uri = namespaceSupport.getURI(prefix); |
| if (!XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(uri)) |
| return null; |
| } |
| if ("anyType".equals(localName)) |
| return Type.ANY_TYPE; |
| return (SimpleType) typeLibrary.createDatatype(localName); |
| } |
| |
| } |
| |