package org.argeo.cms.acr;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.NavigableSet;
+import java.util.Objects;
+import java.util.Set;
import java.util.TreeMap;
+import java.util.TreeSet;
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
import org.apache.xerces.impl.xs.XSImplementationImpl;
import org.apache.xerces.impl.xs.util.StringListImpl;
import org.apache.xerces.xs.XSTypeDefinition;
import org.argeo.api.acr.CrName;
import org.argeo.api.cms.CmsLog;
+import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
public class ContentTypesManager {
private final static CmsLog log = CmsLog.getLog(ContentTypesManager.class);
private Map<String, String> prefixes = new TreeMap<>();
+ // immutable factories
+ private SchemaFactory schemaFactory;
+
+ /** Schema sources. */
private List<Source> sources = new ArrayList<>();
- private SchemaFactory schemaFactory;
+ // cached
private Schema schema;
+ DocumentBuilderFactory documentBuilderFactory;
+ private XSModel xsModel;
+ private NavigableSet<QName> types;
+
+ private boolean validating = true;
public ContentTypesManager() {
schemaFactory = SchemaFactory.newDefaultInstance();
+ // types
+ types = new TreeSet<>((qn1, qn2) -> {
+ if (Objects.equals(qn1.getNamespaceURI(), qn2.getNamespaceURI())) {// same namespace
+ return qn1.getLocalPart().compareTo(qn2.getLocalPart());
+ } else {
+ return qn1.getNamespaceURI().compareTo(qn2.getNamespaceURI());
+ }
+ });
+
}
public synchronized void init() {
prefixes.put("owner", CrName.CR_NAMESPACE_URI);
prefixes.put("posix", CrName.CR_NAMESPACE_URI);
- try {
- for (CmsContentTypes cs : CmsContentTypes.values()) {
- StreamSource source = new StreamSource(cs.getResource().toExternalForm());
- sources.add(source);
- if (prefixes.containsKey(cs.getDefaultPrefix()))
- throw new IllegalStateException("Prefix " + cs.getDefaultPrefix() + " is already mapped with "
- + prefixes.get(cs.getDefaultPrefix()));
- prefixes.put(cs.getDefaultPrefix(), cs.getNamespace());
- }
-
- schema = schemaFactory.newSchema(sources.toArray(new Source[sources.size()]));
- } catch (SAXException e) {
- throw new IllegalStateException("Cannot initialise types", e);
+ for (CmsContentTypes cs : CmsContentTypes.values()) {
+ StreamSource source = new StreamSource(cs.getResource().toExternalForm());
+ sources.add(source);
+ if (prefixes.containsKey(cs.getDefaultPrefix()))
+ throw new IllegalStateException("Prefix " + cs.getDefaultPrefix() + " is already mapped with "
+ + prefixes.get(cs.getDefaultPrefix()));
+ prefixes.put(cs.getDefaultPrefix(), cs.getNamespace());
}
+ reload();
}
public synchronized void registerTypes(String defaultPrefix, String namespace, String xsdSystemId) {
- try {
- if (prefixes.containsKey(defaultPrefix))
- throw new IllegalStateException(
- "Prefix " + defaultPrefix + " is already mapped with " + prefixes.get(defaultPrefix));
- prefixes.put(defaultPrefix, namespace);
+ if (prefixes.containsKey(defaultPrefix))
+ throw new IllegalStateException(
+ "Prefix " + defaultPrefix + " is already mapped with " + prefixes.get(defaultPrefix));
+ prefixes.put(defaultPrefix, namespace);
- sources.add(new StreamSource(xsdSystemId));
- schema = schemaFactory.newSchema(sources.toArray(new Source[sources.size()]));
- } catch (SAXException e) {
- throw new IllegalStateException("Cannot initialise types " + namespace + " based on " + xsdSystemId, e);
- }
+ sources.add(new StreamSource(xsdSystemId));
+ reload();
+ }
+ public Set<QName> listTypes() {
+// TODO cache it?
+ return types;
}
- public void listTypes() {
+ private synchronized void reload() {
try {
- // Find an XMLSchema loader instance
+ // schema
+ schema = schemaFactory.newSchema(sources.toArray(new Source[sources.size()]));
+
+ // document builder factory
+ documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ documentBuilderFactory.setNamespaceAware(true);
+ documentBuilderFactory.setXIncludeAware(true);
+ documentBuilderFactory.setSchema(getSchema());
+ documentBuilderFactory.setValidating(validating);
+
+ // XS model
+ // TODO use JVM implementation?
// DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
// XSImplementation implementation = (XSImplementation) registry.getDOMImplementation("XS-Loader");
- XSImplementation implementation = new XSImplementationImpl();
- XSLoader loader = implementation.createXSLoader(null);
-
- // Load the XML Schema
+ XSImplementation xsImplementation = new XSImplementationImpl();
+ XSLoader xsLoader = xsImplementation.createXSLoader(null);
List<String> systemIds = new ArrayList<>();
for (Source source : sources) {
systemIds.add(source.getSystemId());
}
StringList sl = new StringListImpl(systemIds.toArray(new String[systemIds.size()]), systemIds.size());
- XSModel xsModel = loader.loadURIList(sl);
+ xsModel = xsLoader.loadURIList(sl);
+
+ // types
+ XSNamedMap map = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);
+ for (int i = 0; i < map.getLength(); i++) {
+ XSElementDeclaration eDec = (XSElementDeclaration) map.item(i);
+ QName type = new QName(eDec.getNamespace(), eDec.getName());
+ types.add(type);
+ }
+ } catch (XSException | SAXException e) {
+ throw new IllegalStateException("Cannot relaod types");
+ }
+ }
+
+ public DocumentBuilder newDocumentBuilder() {
+ try {
+ DocumentBuilder dBuilder = documentBuilderFactory.newDocumentBuilder();
+ dBuilder.setErrorHandler(new ErrorHandler() {
+
+ @Override
+ public void warning(SAXParseException exception) throws SAXException {
+ log.warn(exception);
+ }
+
+ @Override
+ public void fatalError(SAXParseException exception) throws SAXException {
+ log.error(exception);
+ }
+
+ @Override
+ public void error(SAXParseException exception) throws SAXException {
+ log.error(exception);
+ }
+ });
+ return dBuilder;
+ } catch (ParserConfigurationException e) {
+ throw new IllegalStateException("Cannot create document builder", e);
+ }
+ }
+
+ public void printTypes() {
+ try {
// Convert top level complex type definitions to node types
log.debug("\n## TYPES");
}
+ public void validate(Source source) throws IOException {
+ if (!validating)
+ return;
+ Validator validator;
+ synchronized (this) {
+ validator = schema.newValidator();
+ }
+ try {
+ validator.validate(source);
+ } catch (SAXException e) {
+ throw new IllegalArgumentException("Provided source is not valid", e);
+ }
+ }
+
public Map<String, String> getPrefixes() {
return prefixes;
}