SchemaBuilderImpl.java
/*
* Copyright 2020-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bremersee.xml;
import static org.springframework.util.ObjectUtils.isEmpty;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.reflections.util.ClasspathHelper;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.FileCopyUtils;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
/**
* The schema builder implementation.
*
* @author Christian Bremer
*/
@NoArgsConstructor(access = AccessLevel.PACKAGE)
@ToString
class SchemaBuilderImpl implements SchemaBuilder {
/**
* The schema language.
*/
String schemaLanguage = XMLConstants.W3C_XML_SCHEMA_NS_URI;
/**
* The factory class name.
*/
String factoryClassName;
/**
* The class loader.
*/
ClassLoader classLoader;
/**
* The resource loader.
*/
ResourceLoader resourceLoader = new DefaultResourceLoader();
/**
* The resource resolver.
*/
LSResourceResolver resourceResolver;
/**
* The error handler.
*/
ErrorHandler errorHandler;
/**
* The features.
*/
final Map<String, Boolean> features = new LinkedHashMap<>();
/**
* The properties.
*/
final Map<String, Object> properties = new LinkedHashMap<>();
/**
* Creates a new schema factory.
*
* @return the schema factory
*/
SchemaFactory createSchemaFactory() {
SchemaFactory schemaFactory;
if (!isEmpty(factoryClassName)) {
if (isEmpty(classLoader)) {
schemaFactory = SchemaFactory
.newInstance(schemaLanguage, factoryClassName, ClasspathHelper.contextClassLoader());
} else {
schemaFactory = SchemaFactory.newInstance(schemaLanguage, factoryClassName, classLoader);
}
} else {
schemaFactory = SchemaFactory.newInstance(schemaLanguage);
}
if (!isEmpty(resourceResolver)) {
schemaFactory.setResourceResolver(resourceResolver);
}
if (!isEmpty(errorHandler)) {
schemaFactory.setErrorHandler(errorHandler);
}
try {
for (Map.Entry<String, Boolean> feature : features.entrySet()) {
schemaFactory.setFeature(feature.getKey(), feature.getValue());
}
for (Map.Entry<String, Object> property : properties.entrySet()) {
schemaFactory.setProperty(property.getKey(), property.getValue());
}
} catch (SAXNotSupportedException | SAXNotRecognizedException e) {
throw new XmlRuntimeException(e);
}
return schemaFactory;
}
@Override
public SchemaBuilderImpl copy() {
SchemaBuilderImpl copy = new SchemaBuilderImpl();
copy.schemaLanguage = schemaLanguage;
copy.factoryClassName = factoryClassName;
copy.classLoader = classLoader;
copy.resourceLoader = resourceLoader;
copy.resourceResolver = resourceResolver;
copy.errorHandler = errorHandler;
copy.features.putAll(features);
copy.properties.putAll(properties);
return copy;
}
@Override
public SchemaBuilder withSchemaLanguage(String schemaLanguage) {
if (isEmpty(schemaLanguage)) {
this.schemaLanguage = schemaLanguage;
}
return this;
}
@Override
public SchemaBuilder withFactory(String factoryClassName) {
this.factoryClassName = factoryClassName;
return this;
}
@Override
public SchemaBuilder withClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
return this;
}
@Override
public SchemaBuilder withResourceLoader(ResourceLoader resourceLoader) {
if (!isEmpty(resourceLoader)) {
this.resourceLoader = resourceLoader;
}
return this;
}
@Override
public SchemaBuilder withResourceResolver(LSResourceResolver resourceResolver) {
this.resourceResolver = resourceResolver;
return this;
}
@Override
public SchemaBuilder withErrorHandler(ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
return this;
}
@Override
public SchemaBuilder withFeature(String name, Boolean value) {
if (!isEmpty(name)) {
features.put(name, value);
}
return this;
}
@Override
public SchemaBuilder withProperty(String name, Object value) {
if (!isEmpty(name)) {
properties.put(name, value);
}
return this;
}
@Override
public List<Source> fetchSchemaSources(Collection<String> locations) {
List<Source> sources = new ArrayList<>();
if (!isEmpty(locations)) {
Set<String> locationSet = new LinkedHashSet<>(locations);
for (String location : locationSet) {
try (InputStream is = resourceLoader.getResource(location).getInputStream()) {
byte[] bytes = FileCopyUtils.copyToByteArray(is);
sources.add(new StreamSource(new ByteArrayInputStream(bytes)));
} catch (IOException e) {
throw new XmlRuntimeException(e);
}
}
}
return Collections.unmodifiableList(sources);
}
@Override
public Schema buildSchema(URL url) {
try {
if (isEmpty(url)) {
return createSchemaFactory().newSchema();
}
return createSchemaFactory().newSchema(url);
} catch (SAXException e) {
throw new XmlRuntimeException(e);
}
}
@Override
public Schema buildSchema(File file) {
try {
if (isEmpty(file)) {
return createSchemaFactory().newSchema();
}
return createSchemaFactory().newSchema(file);
} catch (SAXException e) {
throw new XmlRuntimeException(e);
}
}
@Override
public Schema buildSchema(Source[] sources) {
try {
if (isEmpty(sources)) {
return createSchemaFactory().newSchema();
}
return createSchemaFactory().newSchema(sources);
} catch (SAXException e) {
throw new XmlRuntimeException(e);
}
}
}