1 /* XMLSchemaValidatorHandler.java -- 2 Copyright (C) 2006 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 package gnu.xml.validation.xmlschema; 39 40 import java.util.ArrayList; 41 import java.util.LinkedList; 42 import javax.xml.XMLConstants; 43 import javax.xml.namespace.QName; 44 import javax.xml.validation.TypeInfoProvider; 45 import javax.xml.validation.ValidatorHandler; 46 import org.relaxng.datatype.DatatypeException; 47 import org.relaxng.datatype.DatatypeLibrary; 48 import org.relaxng.datatype.helpers.DatatypeLibraryLoader; 49 import org.w3c.dom.TypeInfo; 50 import org.w3c.dom.ls.LSResourceResolver; 51 import org.xml.sax.Attributes; 52 import org.xml.sax.ContentHandler; 53 import org.xml.sax.ErrorHandler; 54 import org.xml.sax.Locator; 55 import org.xml.sax.SAXException; 56 import org.xml.sax.ext.Attributes2Impl; 57 import org.xml.sax.helpers.NamespaceSupport; 58 import gnu.xml.validation.datatype.SimpleType; 59 import gnu.xml.validation.datatype.Type; 60 61 /** 62 * Streaming validator. 63 * 64 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> 65 */ 66 final class XMLSchemaValidatorHandler 67 extends ValidatorHandler 68 { 69 70 final XMLSchema schema; 71 final TypeInfoProvider typeInfoProvider; 72 final NamespaceSupport namespaceSupport; 73 final DatatypeLibrary typeLibrary; 74 Locator loc; 75 ContentHandler contentHandler; 76 ErrorHandler errorHandler; 77 LSResourceResolver resourceResolver; 78 final LinkedList context; // element context 79 final ArrayList attributes; // attribute context; 80 XMLSchemaValidatorHandler(XMLSchema schema)81 XMLSchemaValidatorHandler(XMLSchema schema) 82 { 83 this.schema = schema; 84 typeInfoProvider = new XMLSchemaTypeInfoProvider(this); 85 namespaceSupport = new NamespaceSupport(); 86 context = new LinkedList(); 87 attributes = new ArrayList(); 88 final String ns = XMLConstants.W3C_XML_SCHEMA_NS_URI; 89 typeLibrary = new DatatypeLibraryLoader().createDatatypeLibrary(ns); 90 } 91 getContentHandler()92 public ContentHandler getContentHandler() 93 { 94 return contentHandler; 95 } 96 setContentHandler(ContentHandler contentHandler)97 public void setContentHandler(ContentHandler contentHandler) 98 { 99 this.contentHandler = contentHandler; 100 } 101 getErrorHandler()102 public ErrorHandler getErrorHandler() 103 { 104 return errorHandler; 105 } 106 setErrorHandler(ErrorHandler errorHandler)107 public void setErrorHandler(ErrorHandler errorHandler) 108 { 109 this.errorHandler = errorHandler; 110 } 111 getResourceResolver()112 public LSResourceResolver getResourceResolver() 113 { 114 return resourceResolver; 115 } 116 setResourceResolver(LSResourceResolver resourceResolver)117 public void setResourceResolver(LSResourceResolver resourceResolver) 118 { 119 this.resourceResolver = resourceResolver; 120 } 121 getTypeInfoProvider()122 public TypeInfoProvider getTypeInfoProvider() 123 { 124 return typeInfoProvider; 125 } 126 getElementTypeInfo()127 TypeInfo getElementTypeInfo() 128 { 129 return (XMLSchemaElementTypeInfo) context.getFirst(); 130 } 131 getAttributeTypeInfo(int index)132 TypeInfo getAttributeTypeInfo(int index) 133 { 134 return (XMLSchemaAttributeTypeInfo) attributes.get(index); 135 } 136 isIdAttribute(int index)137 boolean isIdAttribute(int index) 138 { 139 XMLSchemaAttributeTypeInfo typeInfo = 140 (XMLSchemaAttributeTypeInfo) attributes.get(index); 141 return typeInfo.id; 142 } 143 isSpecified(int index)144 boolean isSpecified(int index) 145 { 146 XMLSchemaAttributeTypeInfo typeInfo = 147 (XMLSchemaAttributeTypeInfo) attributes.get(index); 148 return typeInfo.specified; 149 } 150 setDocumentLocator(Locator locator)151 public void setDocumentLocator(Locator locator) 152 { 153 loc = locator; 154 if (contentHandler != null) 155 { 156 contentHandler.setDocumentLocator(locator); 157 } 158 } 159 startDocument()160 public void startDocument() 161 throws SAXException 162 { 163 namespaceSupport.reset(); 164 context.clear(); 165 attributes.clear(); 166 if (contentHandler != null) 167 { 168 contentHandler.startDocument(); 169 } 170 } 171 endDocument()172 public void endDocument() 173 throws SAXException 174 { 175 if (contentHandler != null) 176 { 177 contentHandler.endDocument(); 178 } 179 } 180 startPrefixMapping(String prefix, String uri)181 public void startPrefixMapping(String prefix, String uri) 182 throws SAXException 183 { 184 namespaceSupport.declarePrefix(prefix, uri); 185 if (contentHandler != null) 186 { 187 contentHandler.startPrefixMapping(prefix, uri); 188 } 189 } 190 endPrefixMapping(String prefix)191 public void endPrefixMapping(String prefix) 192 throws SAXException 193 { 194 if (contentHandler != null) 195 { 196 contentHandler.endPrefixMapping(prefix); 197 } 198 } 199 startElement(String uri, String localName, String qName, Attributes atts)200 public void startElement(String uri, String localName, String qName, 201 Attributes atts) 202 throws SAXException 203 { 204 namespaceSupport.pushContext(); 205 QName name = new QName(uri, localName); 206 ElementDeclaration decl = 207 (ElementDeclaration) schema.elementDeclarations.get(name); 208 // Validation Rule: Element Locally Valid (Element) 209 String xsiType = 210 atts.getValue(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type"); 211 xsiType = xsiType.trim(); // normalise 212 String xsiNil = 213 atts.getValue(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil"); 214 Type type = decl.datatype; 215 if (xsiType.length() > 0) 216 { 217 try 218 { 219 Type specifiedType = resolveType(xsiType); 220 // TODO 4.3 221 type = specifiedType; 222 } 223 catch (DatatypeException e) // 4.1, 4.2 224 { 225 ValidationException e2 = 226 new ValidationException("Can't resolve type " + xsiType, 227 loc); 228 e2.initCause(e); 229 throw e2; 230 } 231 } 232 XMLSchemaElementTypeInfo typeInfo = 233 new XMLSchemaElementTypeInfo(schema, decl, type); 234 if (decl == null) // 1 235 { 236 throw new ValidationException("No declaration for " + name, loc); 237 } 238 if (decl.isAbstract) // 2 239 { 240 throw new ValidationException("Declaration for " + name + 241 " is abstract", loc); 242 } 243 if (xsiNil.length() > 0) 244 { 245 if (!decl.nillable) // 3.1 246 { 247 throw new ValidationException("Declaration for " + name + 248 " is nillable but xsi:nil present", 249 loc); 250 } 251 else if ("true".equals(xsiNil)) // 3.2 252 { 253 typeInfo.nil = true; 254 if (decl.type == XMLSchema.CONSTRAINT_FIXED) // 3.2.2 255 { 256 throw new ValidationException("Declaration for " + name + 257 " is fixed but xsi:nil is true", 258 loc); 259 } 260 } 261 } 262 // TODO 5, 6, 7 263 264 // parent 265 if (!context.isEmpty()) 266 { 267 XMLSchemaElementTypeInfo parent = 268 (XMLSchemaElementTypeInfo) context.getFirst(); 269 if (parent.nil) // Element Locally Valid (Element) 3.2.1 270 { 271 throw new ValidationException("Parent of " + qName + 272 " is declared xsi:nil", loc); 273 } 274 // TODO 275 } 276 context.addFirst(typeInfo); 277 // attributes 278 int len = atts.getLength(); 279 Attributes2Impl atts2 = new Attributes2Impl(); 280 int count = 0; 281 for (int i = 0; i < len; i++) 282 { 283 String attUri = atts.getURI(i); 284 String attLocalName = atts.getLocalName(i); 285 String attQName = atts.getQName(i); 286 String attValue = atts.getValue(i); 287 288 if (XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI.equals(attUri)) 289 { 290 continue; // ? 291 } 292 293 QName attName = new QName(attUri, attLocalName); 294 AttributeDeclaration attDecl = 295 (AttributeDeclaration) schema.attributeDeclarations.get(attName); 296 boolean declared = (attDecl != null); 297 298 String attType = (attDecl != null) ? 299 attDecl.datatype.toString() : "CDATA"; 300 XMLSchemaAttributeTypeInfo attTypeInfo = 301 new XMLSchemaAttributeTypeInfo(schema, attDecl, true); 302 attributes.add(attTypeInfo); 303 304 atts2.addAttribute(attUri, attLocalName, attQName, attType, attValue); 305 atts2.setDeclared(count, declared); 306 atts2.setSpecified(count, true); 307 count++; 308 } 309 // add defaulted attributes to atts2 310 // TODO 311 // atts2.setSpecified(count, false); 312 if (contentHandler != null) 313 { 314 contentHandler.startElement(uri, localName, qName, atts2); 315 } 316 } 317 endElement(String uri, String localName, String qName)318 public void endElement(String uri, String localName, String qName) 319 throws SAXException 320 { 321 // TODO check all required have been seen 322 context.removeFirst(); 323 attributes.clear(); 324 namespaceSupport.popContext(); 325 if (contentHandler != null) 326 { 327 contentHandler.endElement(uri, localName, qName); 328 } 329 } 330 characters(char[] ch, int start, int length)331 public void characters(char[] ch, int start, int length) 332 throws SAXException 333 { 334 XMLSchemaElementTypeInfo parent = 335 (XMLSchemaElementTypeInfo) context.getFirst(); 336 if (parent.nil) // Element Locally Valid (Element) 3.2.1 337 { 338 throw new ValidationException(parent.decl.name.toString() + 339 " is declared xsi:nil", 340 loc); 341 } 342 // TODO 343 if (contentHandler != null) 344 { 345 contentHandler.characters(ch, start, length); 346 } 347 } 348 ignorableWhitespace(char[] ch, int start, int length)349 public void ignorableWhitespace(char[] ch, int start, int length) 350 throws SAXException 351 { 352 if (contentHandler != null) 353 { 354 contentHandler.ignorableWhitespace(ch, start, length); 355 } 356 } 357 processingInstruction(String target, String data)358 public void processingInstruction(String target, String data) 359 throws SAXException 360 { 361 if (contentHandler != null) 362 { 363 contentHandler.processingInstruction(target, data); 364 } 365 } 366 skippedEntity(String name)367 public void skippedEntity(String name) 368 throws SAXException 369 { 370 if (contentHandler != null) 371 { 372 contentHandler.skippedEntity(name); 373 } 374 } 375 resolveType(String value)376 Type resolveType(String value) 377 throws DatatypeException 378 { 379 QName name = QName.valueOf(value); 380 String prefix = name.getPrefix(); 381 String localName = name.getLocalPart(); 382 if (prefix != null && prefix.length() > 0) 383 { 384 String uri = namespaceSupport.getURI(prefix); 385 if (!XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(uri)) 386 return null; 387 } 388 if ("anyType".equals(localName)) 389 return Type.ANY_TYPE; 390 return (SimpleType) typeLibrary.createDatatype(localName); 391 } 392 393 } 394