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