001    package com.thaiopensource.validate.xerces;
002    
003    import com.thaiopensource.util.PropertyMap;
004    import com.thaiopensource.validate.ValidateProperty;
005    import com.thaiopensource.validate.Validator;
006    
007    import org.apache.xerces.impl.XMLEntityManager;
008    import org.apache.xerces.impl.XMLErrorReporter;
009    import org.apache.xerces.impl.validation.EntityState;
010    import org.apache.xerces.impl.validation.ValidationManager;
011    import org.apache.xerces.impl.xs.XMLSchemaValidator;
012    import org.apache.xerces.util.NamespaceSupport;
013    import org.apache.xerces.util.ParserConfigurationSettings;
014    import org.apache.xerces.util.SymbolTable;
015    import org.apache.xerces.util.XMLAttributesImpl;
016    import org.apache.xerces.util.XMLSymbols;
017    import org.apache.xerces.util.ErrorHandlerWrapper;
018    import org.apache.xerces.xni.NamespaceContext;
019    import org.apache.xerces.xni.QName;
020    import org.apache.xerces.xni.XMLAttributes;
021    import org.apache.xerces.xni.XMLLocator;
022    import org.apache.xerces.xni.XMLResourceIdentifier;
023    import org.apache.xerces.xni.XMLString;
024    import org.apache.xerces.xni.XNIException;
025    import org.apache.xerces.xni.grammars.XMLGrammarPool;
026    import org.apache.xerces.xni.parser.XMLComponent;
027    import org.apache.xerces.xni.parser.XMLEntityResolver;
028    import org.apache.xerces.xni.parser.XMLInputSource;
029    import org.apache.xerces.xni.parser.XMLParseException;
030    import org.apache.xerces.xni.parser.XMLErrorHandler;
031    import org.xml.sax.Attributes;
032    import org.xml.sax.Locator;
033    import org.xml.sax.SAXException;
034    import org.xml.sax.SAXParseException;
035    import org.xml.sax.DTDHandler;
036    import org.xml.sax.ContentHandler;
037    
038    import java.io.IOException;
039    import java.util.Hashtable;
040    
041    class ValidatorImpl extends ParserConfigurationSettings implements Validator, ContentHandler, DTDHandler, XMLLocator, XMLEntityResolver, EntityState {
042    
043      private final XMLSchemaValidator schemaValidator = new XMLSchemaValidator();
044      private final XMLErrorReporter errorReporter = new XMLErrorReporter();
045      private final XMLEntityManager entityManager = new XMLEntityManager();
046      private final ValidationManager validationManager = new ValidationManager();
047      private final NamespaceContext namespaceContext = new NamespaceSupport();
048      private final XMLAttributes attributes = new XMLAttributesImpl();
049      private final SymbolTable symbolTable;
050      private final XMLComponent[] components;
051      private Locator locator;
052      private final Hashtable entityTable = new Hashtable();
053      private boolean pushedContext = false;
054    
055      // JC deal with baseURI
056    
057      static private final String[] recognizedFeatures = {
058        Features.SCHEMA_AUGMENT_PSVI,
059        Features.SCHEMA_FULL_CHECKING,
060        Features.VALIDATION,
061        Features.SCHEMA_VALIDATION,
062      };
063    
064      static private final String[] recognizedProperties = {
065        Properties.XMLGRAMMAR_POOL,
066        Properties.SYMBOL_TABLE,
067        Properties.ERROR_REPORTER,
068        Properties.ERROR_HANDLER,
069        Properties.VALIDATION_MANAGER,
070        Properties.ENTITY_MANAGER,
071        Properties.ENTITY_RESOLVER,
072      };
073    
074      ValidatorImpl(SymbolTable symbolTable, XMLGrammarPool grammarPool, PropertyMap properties) {
075        this.symbolTable = symbolTable;
076        XMLErrorHandler errorHandlerWrapper = new ErrorHandlerWrapper(ValidateProperty.ERROR_HANDLER.get(properties));
077        components = new XMLComponent[] { errorReporter, schemaValidator, entityManager };
078        for (int i = 0; i < components.length; i++) {
079          addRecognizedFeatures(components[i].getRecognizedFeatures());
080          addRecognizedProperties(components[i].getRecognizedProperties());
081        }
082        addRecognizedFeatures(recognizedFeatures);
083        addRecognizedProperties(recognizedProperties);
084        setFeature(Features.SCHEMA_AUGMENT_PSVI, false);
085        setFeature(Features.SCHEMA_FULL_CHECKING, true);
086        setFeature(Features.VALIDATION, true);
087        setFeature(Features.SCHEMA_VALIDATION, true);
088        setProperty(Properties.XMLGRAMMAR_POOL, grammarPool);
089        setProperty(Properties.SYMBOL_TABLE, symbolTable);
090        errorReporter.setDocumentLocator(this);
091        setProperty(Properties.ERROR_REPORTER, errorReporter);
092        setProperty(Properties.ERROR_HANDLER, errorHandlerWrapper);
093        setProperty(Properties.VALIDATION_MANAGER, validationManager);
094        // In Xerces 2.4.0, XMLSchemaValidator uses ENTITY_MANAGER when
095        // it should use ENTITY_RESOLVER
096    
097        // George: comment out the next line, otherwise
098        // with newwe versions of Xerces we got a CCE if I remember right...
099        
100        setProperty(Properties.ENTITY_MANAGER, entityManager);
101        
102        setProperty(Properties.ENTITY_RESOLVER, this);
103        reset();
104      }
105    
106      public void reset() {
107        validationManager.reset();
108        namespaceContext.reset();
109        for (int i = 0; i < components.length; i++)
110          components[i].reset(this);
111        validationManager.setEntityState(this);
112      }
113    
114      public ContentHandler getContentHandler() {
115        return this;
116      }
117    
118      public DTDHandler getDTDHandler() {
119        return this;
120      }
121    
122      public void setDocumentLocator(Locator locator) {
123        this.locator = locator;
124      }
125    
126      public void notationDecl(String name,
127                               String publicId,
128                               String systemId) {
129        // nothing needed
130      }
131    
132      public void unparsedEntityDecl(String name,
133                                     String publicId,
134                                     String systemId,
135                                     String notationName) {
136        entityTable.put(name, name);
137      }
138    
139      public boolean isEntityDeclared(String name) {
140        return entityTable.get(name) != null;
141      }
142    
143      public boolean isEntityUnparsed(String name) {
144        return entityTable.get(name) != null;
145      }
146    
147      public void startDocument()
148              throws SAXException {
149        try {
150          schemaValidator.startDocument(locator == null ? null : this, null, namespaceContext, null);
151        }
152        catch (XNIException e) {
153          throw toSAXException(e);
154        }
155      }
156    
157      public void endDocument()
158              throws SAXException {
159        try {
160          schemaValidator.endDocument(null);
161        }
162        catch (XNIException e) {
163          throw toSAXException(e);
164        }
165      }
166    
167      public void startElement(String namespaceURI, String localName,
168                               String qName, Attributes atts)
169              throws SAXException {
170        try {
171          if (!pushedContext)
172            namespaceContext.pushContext();
173          else
174            pushedContext = false;
175          for (int i = 0, len = atts.getLength(); i < len; i++)
176            attributes.addAttribute(makeQName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)),
177                                    symbolTable.addSymbol(atts.getType(i)),
178                                    atts.getValue(i));
179          schemaValidator.startElement(makeQName(namespaceURI, localName, qName), attributes, null);
180          attributes.removeAllAttributes();
181        }
182        catch (XNIException e) {
183          throw toSAXException(e);
184        }
185      }
186    
187      public void endElement(String namespaceURI, String localName,
188                             String qName)
189              throws SAXException {
190        try {
191          schemaValidator.endElement(makeQName(namespaceURI, localName, qName), null);
192          namespaceContext.popContext();
193        }
194        catch (XNIException e) {
195          throw toSAXException(e);
196        }
197      }
198    
199      public void startPrefixMapping(String prefix, String uri)
200              throws SAXException {
201        try {
202          if (!pushedContext) {
203            namespaceContext.pushContext();
204            pushedContext = true;
205          }
206          if (prefix == null)
207            prefix = XMLSymbols.EMPTY_STRING;
208          else
209            prefix = symbolTable.addSymbol(prefix);
210          if (uri != null) {
211            if (uri.equals(""))
212              uri = null;
213            else
214              uri = symbolTable.addSymbol(uri);
215          }
216          namespaceContext.declarePrefix(prefix, uri);
217        }
218        catch (XNIException e) {
219          throw toSAXException(e);
220        }
221      }
222    
223      public void endPrefixMapping(String prefix)
224              throws SAXException {
225        // do nothing
226      }
227    
228      public void characters(char ch[], int start, int length)
229              throws SAXException {
230        try {
231          schemaValidator.characters(new XMLString(ch, start, length), null);
232        }
233        catch (XNIException e) {
234          throw toSAXException(e);
235        }
236      }
237    
238      public void ignorableWhitespace(char ch[], int start, int length)
239              throws SAXException {
240        try {
241          schemaValidator.ignorableWhitespace(new XMLString(ch, start, length), null);
242        }
243        catch (XNIException e) {
244          throw toSAXException(e);
245        }
246      }
247    
248      public void processingInstruction(String target, String data)
249              throws SAXException {
250        // do nothing
251      }
252    
253      public void skippedEntity(String name)
254              throws SAXException {
255        // do nothing
256      }
257    
258      private QName makeQName(String namespaceURI, String localName, String qName) {
259        localName = symbolTable.addSymbol(localName);
260        String prefix;
261        if (namespaceURI.equals("")) {
262          namespaceURI = null;
263          prefix = XMLSymbols.EMPTY_STRING;
264          qName = localName;
265        }
266        else {
267          namespaceURI = symbolTable.addSymbol(namespaceURI);
268          if (qName.equals("")) {
269            prefix = namespaceContext.getPrefix(namespaceURI);
270            if (prefix == XMLSymbols.EMPTY_STRING)
271              qName = localName;
272            else if (prefix == null)
273              qName = localName; // JC what to do?
274            else
275              qName = symbolTable.addSymbol(prefix + ":" + localName);
276          }
277          else {
278            qName = symbolTable.addSymbol(qName);
279            int colon = qName.indexOf(':');
280            if (colon > 0)
281              prefix = symbolTable.addSymbol(qName.substring(0, colon));
282            else
283              prefix = XMLSymbols.EMPTY_STRING;
284          }
285        }
286        return new QName(prefix, localName, qName, namespaceURI);
287      }
288    
289      public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier)
290              throws XNIException, IOException {
291        return null;
292      }
293    
294      public String getPublicId() {
295        return locator.getPublicId();
296      }
297    
298      public String getEncoding() {
299        return null;
300      }
301    
302      public String getBaseSystemId() {
303        return null;
304      }
305    
306      public String getLiteralSystemId() {
307        return null;
308      }
309    
310      public String getExpandedSystemId() {
311        return locator.getSystemId();
312      }
313    
314      public int getLineNumber() {
315        return locator.getLineNumber();
316      }
317    
318      public int getColumnNumber() {
319        return locator.getColumnNumber();
320      }
321    
322      static SAXException toSAXException(XNIException e) {
323        if (e instanceof XMLParseException) {
324          XMLParseException pe = (XMLParseException)e;
325          return new SAXParseException(pe.getMessage(),
326                                       pe.getPublicId(),
327                                       pe.getExpandedSystemId(),
328                                       pe.getLineNumber(),
329                                       pe.getColumnNumber(),
330                                       pe.getException());
331        }
332        Exception nested = e.getException();
333        if (nested == null)
334          return new SAXException(e.getMessage());
335        if (nested instanceof SAXException)
336          return (SAXException)nested;
337        if (nested instanceof RuntimeException)
338          throw (RuntimeException)nested;
339        return new SAXException(nested);
340      }
341    
342      /**
343       * This is needed by the Xerces 2.7.0 - we do not include it yet
344       * as it is not yet released but made an integration from CVS some
345       * time ago. When reverting to 2.6.2 the method remained here as we 
346       * will neet it when we will get back to 2.7.0.
347       *
348       * @see org.apache.xerces.xni.XMLLocator#getCharacterOffset()
349       */
350      public int getCharacterOffset() {
351        return 0;
352      }
353    
354      /**
355       * This is needed by the Xerces 2.7.0 - we do not include it yet
356       * as it is not yet released but made an integration from CVS some
357       * time ago. When reverting to 2.6.2 the method remained here as we 
358       * will neet it when we will get back to 2.7.0.
359       *
360       * @see org.apache.xerces.xni.XMLLocator#getXMLVersion()
361       */
362      public String getXMLVersion() {    
363        return "1.0";
364      }
365    }