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