View Javadoc

1   /*
2    * Copyright 2006 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.xml.validation;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.ByteArrayOutputStream;
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.List;
24  import javax.xml.parsers.ParserConfigurationException;
25  import javax.xml.parsers.SAXParser;
26  import javax.xml.parsers.SAXParserFactory;
27  import javax.xml.transform.Source;
28  import javax.xml.transform.Transformer;
29  import javax.xml.transform.TransformerException;
30  import javax.xml.transform.TransformerFactory;
31  import javax.xml.transform.dom.DOMSource;
32  import javax.xml.transform.sax.SAXSource;
33  import javax.xml.transform.stream.StreamResult;
34  import javax.xml.transform.stream.StreamSource;
35  
36  import org.springframework.core.io.Resource;
37  import org.springframework.xml.sax.SaxUtils;
38  import org.xml.sax.InputSource;
39  import org.xml.sax.SAXException;
40  import org.xml.sax.SAXParseException;
41  import org.xml.sax.helpers.DefaultHandler;
42  
43  /**
44   * Internal class that uses JAXP 1.0 features to create <code>XmlValidator</code> instances.
45   *
46   * @author Arjen Poutsma
47   * @since 1.0.0
48   */
49  abstract class Jaxp10ValidatorFactory {
50  
51      private static final String SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
52  
53      private static final String SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
54  
55      static XmlValidator createValidator(Resource[] schemaResources, String schemaLanguage) throws IOException {
56          InputSource[] inputSources = new InputSource[schemaResources.length];
57          for (int i = 0; i < schemaResources.length; i++) {
58              inputSources[i] = SaxUtils.createInputSource(schemaResources[i]);
59          }
60          return new Jaxp10Validator(inputSources, schemaLanguage);
61      }
62  
63      private static class Jaxp10Validator implements XmlValidator {
64  
65          private SAXParserFactory parserFactory;
66  
67          private TransformerFactory transformerFactory;
68  
69          private InputSource[] schemaInputSources;
70  
71          private String schemaLanguage;
72  
73          private Jaxp10Validator(InputSource[] schemaInputSources, String schemaLanguage) {
74              this.schemaInputSources = schemaInputSources;
75              this.schemaLanguage = schemaLanguage;
76              transformerFactory = TransformerFactory.newInstance();
77              parserFactory = SAXParserFactory.newInstance();
78              parserFactory.setNamespaceAware(true);
79              parserFactory.setValidating(true);
80          }
81  
82          public SAXParseException[] validate(Source source) throws IOException {
83              SAXParser parser = createSAXParser();
84              ValidationErrorHandler errorHandler = new ValidationErrorHandler();
85              try {
86                  if (source instanceof SAXSource) {
87                      validateSAXSource((SAXSource) source, parser, errorHandler);
88                  }
89                  else if (source instanceof StreamSource) {
90                      validateStreamSource((StreamSource) source, parser, errorHandler);
91                  }
92                  else if (source instanceof DOMSource) {
93                      validateDOMSource((DOMSource) source, parser, errorHandler);
94                  }
95                  else {
96                      throw new IllegalArgumentException("Source [" + source.getClass().getName() +
97                              "] is neither SAXSource, DOMSource, nor StreamSource");
98                  }
99                  return errorHandler.getErrors();
100             }
101             catch (SAXException ex) {
102                 throw new XmlValidationException("Could not validate source: " + ex.getMessage(), ex);
103             }
104         }
105 
106         private void validateDOMSource(DOMSource domSource, SAXParser parser, ValidationErrorHandler errorHandler)
107                 throws IOException, SAXException {
108             try {
109                 // Sadly, JAXP 1.0 DOM doesn't implement DOM level 3, so we cannot use Document.normalizeDocument()
110                 // Instead, we write the Document to a Stream, and validate that
111                 Transformer transformer = transformerFactory.newTransformer();
112                 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
113                 transformer.transform(domSource, new StreamResult(outputStream));
114                 ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
115                 validateStreamSource(new StreamSource(inputStream), parser, errorHandler);
116             }
117             catch (TransformerException ex) {
118                 throw new XmlValidationException("Could not validate DOM source: " + ex.getMessage(), ex);
119             }
120 
121         }
122 
123         private void validateStreamSource(StreamSource streamSource,
124                                           SAXParser parser,
125                                           ValidationErrorHandler errorHandler) throws SAXException, IOException {
126             if (streamSource.getInputStream() != null) {
127                 parser.parse(streamSource.getInputStream(), errorHandler);
128             }
129             else if (streamSource.getReader() != null) {
130                 parser.parse(new InputSource(streamSource.getReader()), errorHandler);
131             }
132             else {
133                 throw new IllegalArgumentException("StreamSource contains neither InputStream nor Reader");
134             }
135         }
136 
137         private void validateSAXSource(SAXSource source, SAXParser parser, ValidationErrorHandler errorHandler)
138                 throws SAXException, IOException {
139             parser.parse(source.getInputSource(), errorHandler);
140         }
141 
142         private SAXParser createSAXParser() {
143             try {
144                 SAXParser parser = parserFactory.newSAXParser();
145                 parser.setProperty(SCHEMA_LANGUAGE, schemaLanguage);
146                 parser.setProperty(SCHEMA_SOURCE, schemaInputSources);
147                 return parser;
148             }
149             catch (ParserConfigurationException ex) {
150                 throw new XmlValidationException("Could not create SAXParser: " + ex.getMessage(), ex);
151             }
152             catch (SAXException ex) {
153                 throw new XmlValidationException("Could not create SAXParser: " + ex.getMessage(), ex);
154             }
155         }
156     }
157 
158     /** <code>DefaultHandler</code> extension that stores errors and fatal errors in a list. */
159     private static class ValidationErrorHandler extends DefaultHandler {
160 
161         private List errors = new ArrayList();
162 
163         private SAXParseException[] getErrors() {
164             return (SAXParseException[]) errors.toArray(new SAXParseException[errors.size()]);
165         }
166 
167         public void warning(SAXParseException ex) throws SAXException {
168         }
169 
170         public void error(SAXParseException ex) throws SAXException {
171             errors.add(ex);
172         }
173 
174         public void fatalError(SAXParseException ex) throws SAXException {
175             errors.add(ex);
176         }
177     }
178 }