View Javadoc
1   /*
2    * Copyright 2020-2022  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.bremersee.xml;
18  
19  import static org.springframework.util.ObjectUtils.isEmpty;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.net.URL;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.Collections;
29  import java.util.LinkedHashMap;
30  import java.util.LinkedHashSet;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Set;
34  import javax.xml.XMLConstants;
35  import javax.xml.transform.Source;
36  import javax.xml.transform.stream.StreamSource;
37  import javax.xml.validation.Schema;
38  import javax.xml.validation.SchemaFactory;
39  import lombok.AccessLevel;
40  import lombok.NoArgsConstructor;
41  import lombok.ToString;
42  import org.reflections.util.ClasspathHelper;
43  import org.springframework.core.io.DefaultResourceLoader;
44  import org.springframework.core.io.ResourceLoader;
45  import org.springframework.util.FileCopyUtils;
46  import org.w3c.dom.ls.LSResourceResolver;
47  import org.xml.sax.ErrorHandler;
48  import org.xml.sax.SAXException;
49  import org.xml.sax.SAXNotRecognizedException;
50  import org.xml.sax.SAXNotSupportedException;
51  
52  /**
53   * The schema builder implementation.
54   *
55   * @author Christian Bremer
56   */
57  @NoArgsConstructor(access = AccessLevel.PACKAGE)
58  @ToString
59  class SchemaBuilderImpl implements SchemaBuilder {
60  
61    /**
62     * The schema language.
63     */
64    String schemaLanguage = XMLConstants.W3C_XML_SCHEMA_NS_URI;
65  
66    /**
67     * The factory class name.
68     */
69    String factoryClassName;
70  
71    /**
72     * The class loader.
73     */
74    ClassLoader classLoader;
75  
76    /**
77     * The resource loader.
78     */
79    ResourceLoader resourceLoader = new DefaultResourceLoader();
80  
81    /**
82     * The resource resolver.
83     */
84    LSResourceResolver resourceResolver;
85  
86    /**
87     * The error handler.
88     */
89    ErrorHandler errorHandler;
90  
91    /**
92     * The features.
93     */
94    final Map<String, Boolean> features = new LinkedHashMap<>();
95  
96    /**
97     * The properties.
98     */
99    final Map<String, Object> properties = new LinkedHashMap<>();
100 
101   /**
102    * Creates a new schema factory.
103    *
104    * @return the schema factory
105    */
106   SchemaFactory createSchemaFactory() {
107     SchemaFactory schemaFactory;
108     if (!isEmpty(factoryClassName)) {
109       if (isEmpty(classLoader)) {
110         schemaFactory = SchemaFactory
111             .newInstance(schemaLanguage, factoryClassName, ClasspathHelper.contextClassLoader());
112       } else {
113         schemaFactory = SchemaFactory.newInstance(schemaLanguage, factoryClassName, classLoader);
114       }
115     } else {
116       schemaFactory = SchemaFactory.newInstance(schemaLanguage);
117     }
118     if (!isEmpty(resourceResolver)) {
119       schemaFactory.setResourceResolver(resourceResolver);
120     }
121     if (!isEmpty(errorHandler)) {
122       schemaFactory.setErrorHandler(errorHandler);
123     }
124     try {
125       for (Map.Entry<String, Boolean> feature : features.entrySet()) {
126         schemaFactory.setFeature(feature.getKey(), feature.getValue());
127       }
128       for (Map.Entry<String, Object> property : properties.entrySet()) {
129         schemaFactory.setProperty(property.getKey(), property.getValue());
130       }
131 
132     } catch (SAXNotSupportedException | SAXNotRecognizedException e) {
133       throw new XmlRuntimeException(e);
134     }
135     return schemaFactory;
136   }
137 
138   @Override
139   public SchemaBuilderImpl copy() {
140     SchemaBuilderImpl copy = new SchemaBuilderImpl();
141     copy.schemaLanguage = schemaLanguage;
142     copy.factoryClassName = factoryClassName;
143     copy.classLoader = classLoader;
144     copy.resourceLoader = resourceLoader;
145     copy.resourceResolver = resourceResolver;
146     copy.errorHandler = errorHandler;
147     copy.features.putAll(features);
148     copy.properties.putAll(properties);
149     return copy;
150   }
151 
152   @Override
153   public SchemaBuilder withSchemaLanguage(String schemaLanguage) {
154     if (isEmpty(schemaLanguage)) {
155       this.schemaLanguage = schemaLanguage;
156     }
157     return this;
158   }
159 
160   @Override
161   public SchemaBuilder withFactory(String factoryClassName) {
162     this.factoryClassName = factoryClassName;
163     return this;
164   }
165 
166   @Override
167   public SchemaBuilder withClassLoader(ClassLoader classLoader) {
168     this.classLoader = classLoader;
169     return this;
170   }
171 
172   @Override
173   public SchemaBuilder withResourceLoader(ResourceLoader resourceLoader) {
174     if (!isEmpty(resourceLoader)) {
175       this.resourceLoader = resourceLoader;
176     }
177     return this;
178   }
179 
180   @Override
181   public SchemaBuilder withResourceResolver(LSResourceResolver resourceResolver) {
182     this.resourceResolver = resourceResolver;
183     return this;
184   }
185 
186   @Override
187   public SchemaBuilder withErrorHandler(ErrorHandler errorHandler) {
188     this.errorHandler = errorHandler;
189     return this;
190   }
191 
192   @Override
193   public SchemaBuilder withFeature(String name, Boolean value) {
194     if (!isEmpty(name)) {
195       features.put(name, value);
196     }
197     return this;
198   }
199 
200   @Override
201   public SchemaBuilder withProperty(String name, Object value) {
202     if (!isEmpty(name)) {
203       properties.put(name, value);
204     }
205     return this;
206   }
207 
208   @Override
209   public List<Source> fetchSchemaSources(Collection<String> locations) {
210     List<Source> sources = new ArrayList<>();
211     if (!isEmpty(locations)) {
212       Set<String> locationSet = new LinkedHashSet<>(locations);
213       for (String location : locationSet) {
214         try (InputStream is = resourceLoader.getResource(location).getInputStream()) {
215           byte[] bytes = FileCopyUtils.copyToByteArray(is);
216           sources.add(new StreamSource(new ByteArrayInputStream(bytes)));
217         } catch (IOException e) {
218           throw new XmlRuntimeException(e);
219         }
220       }
221     }
222     return Collections.unmodifiableList(sources);
223   }
224 
225   @Override
226   public Schema buildSchema(URL url) {
227     try {
228       if (isEmpty(url)) {
229         return createSchemaFactory().newSchema();
230       }
231       return createSchemaFactory().newSchema(url);
232     } catch (SAXException e) {
233       throw new XmlRuntimeException(e);
234     }
235   }
236 
237   @Override
238   public Schema buildSchema(File file) {
239     try {
240       if (isEmpty(file)) {
241         return createSchemaFactory().newSchema();
242       }
243       return createSchemaFactory().newSchema(file);
244     } catch (SAXException e) {
245       throw new XmlRuntimeException(e);
246     }
247   }
248 
249   @Override
250   public Schema buildSchema(Source[] sources) {
251     try {
252       if (isEmpty(sources)) {
253         return createSchemaFactory().newSchema();
254       }
255       return createSchemaFactory().newSchema(sources);
256     } catch (SAXException e) {
257       throw new XmlRuntimeException(e);
258     }
259   }
260 
261 }