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