1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.xml.xsd.commons;
18
19 import java.io.IOException;
20 import java.util.ArrayList;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Set;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.apache.ws.commons.schema.ValidationEventHandler;
28 import org.apache.ws.commons.schema.XmlSchema;
29 import org.apache.ws.commons.schema.XmlSchemaCollection;
30 import org.apache.ws.commons.schema.XmlSchemaExternal;
31 import org.apache.ws.commons.schema.XmlSchemaImport;
32 import org.apache.ws.commons.schema.XmlSchemaInclude;
33 import org.apache.ws.commons.schema.XmlSchemaObject;
34 import org.apache.ws.commons.schema.XmlSchemaObjectCollection;
35 import org.apache.ws.commons.schema.resolver.DefaultURIResolver;
36 import org.apache.ws.commons.schema.resolver.URIResolver;
37 import org.xml.sax.InputSource;
38
39 import org.springframework.beans.factory.InitializingBean;
40 import org.springframework.context.ResourceLoaderAware;
41 import org.springframework.core.io.Resource;
42 import org.springframework.core.io.ResourceLoader;
43 import org.springframework.core.io.UrlResource;
44 import org.springframework.util.Assert;
45 import org.springframework.util.StringUtils;
46 import org.springframework.xml.sax.SaxUtils;
47 import org.springframework.xml.validation.XmlValidator;
48 import org.springframework.xml.validation.XmlValidatorFactory;
49 import org.springframework.xml.xsd.XsdSchema;
50 import org.springframework.xml.xsd.XsdSchemaCollection;
51
52
53
54
55
56
57
58
59
60
61
62
63 public class CommonsXsdSchemaCollection implements XsdSchemaCollection, InitializingBean, ResourceLoaderAware {
64
65 private static final Log logger = LogFactory.getLog(CommonsXsdSchemaCollection.class);
66
67 private final XmlSchemaCollection schemaCollection = new XmlSchemaCollection();
68
69 private final List xmlSchemas = new ArrayList();
70
71 private Resource[] xsdResources;
72
73 private boolean inline = false;
74
75 private ValidationEventHandler validationEventHandler;
76
77 private URIResolver uriResolver = new ClasspathUriResolver();
78
79 private ResourceLoader resourceLoader;
80
81
82
83
84
85
86 public CommonsXsdSchemaCollection() {
87 }
88
89
90
91
92
93
94 public CommonsXsdSchemaCollection(Resource[] resources) {
95 this.xsdResources = resources;
96 }
97
98
99
100
101
102
103 public void setXsds(Resource[] xsdResources) {
104 this.xsdResources = xsdResources;
105 }
106
107
108
109
110
111
112 public void setInline(boolean inline) {
113 this.inline = inline;
114 }
115
116
117 public void setValidationEventHandler(ValidationEventHandler validationEventHandler) {
118 this.validationEventHandler = validationEventHandler;
119 }
120
121
122
123
124
125
126 public void setUriResolver(URIResolver uriResolver) {
127 Assert.notNull(uriResolver, "'uriResolver' must not be null");
128 this.uriResolver = uriResolver;
129 }
130
131 public void setResourceLoader(ResourceLoader resourceLoader) {
132 this.resourceLoader = resourceLoader;
133 }
134
135 public void afterPropertiesSet() throws IOException {
136 Assert.notEmpty(xsdResources, "'xsds' must not be empty");
137
138 schemaCollection.setSchemaResolver(uriResolver);
139
140 Set processedIncludes = new HashSet();
141 Set processedImports = new HashSet();
142
143 for (int i = 0; i < xsdResources.length; i++) {
144 Resource xsdResource = xsdResources[i];
145 Assert.isTrue(xsdResource.exists(), xsdResource + " does not exit");
146 try {
147 XmlSchema xmlSchema =
148 schemaCollection.read(SaxUtils.createInputSource(xsdResource), validationEventHandler);
149 xmlSchemas.add(xmlSchema);
150
151 if (inline) {
152 inlineIncludes(xmlSchema, processedIncludes, processedImports);
153 findImports(xmlSchema, processedImports, processedIncludes);
154 }
155 }
156 catch (Exception ex) {
157 throw new CommonsXsdSchemaException("Schema [" + xsdResource + "] could not be loaded", ex);
158 }
159 }
160 if (logger.isInfoEnabled()) {
161 logger.info("Loaded " + StringUtils.arrayToCommaDelimitedString(xsdResources));
162 }
163
164 }
165
166 public XsdSchema[] getXsdSchemas() {
167 XsdSchema[] result = new XsdSchema[xmlSchemas.size()];
168 for (int i = 0; i < xmlSchemas.size(); i++) {
169 XmlSchema xmlSchema = (XmlSchema) xmlSchemas.get(i);
170 result[i] = new CommonsXsdSchema(xmlSchema, schemaCollection);
171 }
172 return result;
173 }
174
175 public XmlValidator createValidator() throws IOException {
176 Resource[] resources = new Resource[xmlSchemas.size()];
177 for (int i = xmlSchemas.size() - 1; i >= 0; i--) {
178 XmlSchema xmlSchema = (XmlSchema) xmlSchemas.get(i);
179 resources[i] = new UrlResource(xmlSchema.getSourceURI());
180 }
181 return XmlValidatorFactory.createValidator(resources, XmlValidatorFactory.SCHEMA_W3C_XML);
182 }
183
184 private void inlineIncludes(XmlSchema schema, Set processedIncludes, Set processedImports) {
185 processedIncludes.add(schema);
186
187 XmlSchemaObjectCollection schemaItems = schema.getItems();
188 for (int i = 0; i < schemaItems.getCount(); i++) {
189 XmlSchemaObject schemaObject = schemaItems.getItem(i);
190 if (schemaObject instanceof XmlSchemaInclude) {
191 XmlSchema includedSchema = ((XmlSchemaInclude) schemaObject).getSchema();
192 if (!processedIncludes.contains(includedSchema)) {
193 inlineIncludes(includedSchema, processedIncludes, processedImports);
194 findImports(includedSchema, processedImports, processedIncludes);
195 XmlSchemaObjectCollection includeItems = includedSchema.getItems();
196 for (int j = 0; j < includeItems.getCount(); j++) {
197 XmlSchemaObject includedItem = includeItems.getItem(j);
198 schemaItems.add(includedItem);
199 }
200 }
201
202 schemaItems.removeAt(i);
203 i--;
204 }
205 }
206 }
207
208 private void findImports(XmlSchema schema, Set processedImports, Set processedIncludes) {
209 processedImports.add(schema);
210 XmlSchemaObjectCollection includes = schema.getIncludes();
211 for (int i = 0; i < includes.getCount(); i++) {
212 XmlSchemaExternal external = (XmlSchemaExternal) includes.getItem(i);
213 if (external instanceof XmlSchemaImport) {
214 XmlSchemaImport schemaImport = (XmlSchemaImport) external;
215 XmlSchema importedSchema = schemaImport.getSchema();
216 if (!"http://www.w3.org/XML/1998/namespace".equals(schemaImport.getNamespace()) &&
217 importedSchema != null && !processedImports.contains(importedSchema)) {
218 inlineIncludes(importedSchema, processedIncludes, processedImports);
219 findImports(importedSchema, processedImports, processedIncludes);
220 xmlSchemas.add(importedSchema);
221 }
222
223 external.setSchemaLocation(null);
224 }
225 }
226 }
227
228 public String toString() {
229 StringBuffer buffer = new StringBuffer("CommonsXsdSchemaCollection");
230 buffer.append('{');
231 for (int i = 0; i < xmlSchemas.size(); i++) {
232 XmlSchema schema = (XmlSchema) xmlSchemas.get(i);
233 buffer.append(schema.getTargetNamespace());
234 if (i < xmlSchemas.size() - 1) {
235 buffer.append(',');
236 }
237 }
238 buffer.append('}');
239 return buffer.toString();
240 }
241
242 private class ClasspathUriResolver extends DefaultURIResolver {
243
244 public InputSource resolveEntity(String namespace, String schemaLocation, String baseUri) {
245 if (resourceLoader != null) {
246 Resource resource = resourceLoader.getResource(schemaLocation);
247 if (resource.exists()) {
248 return createInputSource(resource);
249 }
250 else if (StringUtils.hasLength(baseUri)) {
251
252 try {
253 Resource baseUriResource = new UrlResource(baseUri);
254 resource = baseUriResource.createRelative(schemaLocation);
255 if (resource.exists()) {
256 return createInputSource(resource);
257 }
258 }
259 catch (IOException e) {
260
261 }
262 }
263
264 String classpathLocation = ResourceLoader.CLASSPATH_URL_PREFIX + "/" + schemaLocation;
265 resource = resourceLoader.getResource(classpathLocation);
266 if (resource.exists()) {
267 return createInputSource(resource);
268 }
269 }
270 return super.resolveEntity(namespace, schemaLocation, baseUri);
271 }
272
273 private InputSource createInputSource(Resource resource) {
274 try {
275 return SaxUtils.createInputSource(resource);
276 }
277 catch (IOException ex) {
278 throw new CommonsXsdSchemaException("Could not resolve location", ex);
279 }
280 }
281 }
282
283 }