1 package org
.argeo
.cms
.acr
;
3 import java
.io
.IOException
;
4 import java
.util
.ArrayList
;
5 import java
.util
.HashMap
;
9 import java
.util
.SortedMap
;
10 import java
.util
.TreeMap
;
12 import javax
.xml
.XMLConstants
;
13 import javax
.xml
.namespace
.QName
;
14 import javax
.xml
.parsers
.DocumentBuilder
;
15 import javax
.xml
.parsers
.DocumentBuilderFactory
;
16 import javax
.xml
.parsers
.ParserConfigurationException
;
17 import javax
.xml
.transform
.Source
;
18 import javax
.xml
.transform
.stream
.StreamSource
;
19 import javax
.xml
.validation
.Schema
;
20 import javax
.xml
.validation
.SchemaFactory
;
21 import javax
.xml
.validation
.Validator
;
23 import org
.apache
.xerces
.impl
.xs
.XSImplementationImpl
;
24 import org
.apache
.xerces
.impl
.xs
.util
.StringListImpl
;
25 import org
.apache
.xerces
.jaxp
.DocumentBuilderFactoryImpl
;
26 import org
.apache
.xerces
.xs
.StringList
;
27 import org
.apache
.xerces
.xs
.XSAttributeDeclaration
;
28 import org
.apache
.xerces
.xs
.XSAttributeUse
;
29 import org
.apache
.xerces
.xs
.XSComplexTypeDefinition
;
30 import org
.apache
.xerces
.xs
.XSConstants
;
31 import org
.apache
.xerces
.xs
.XSElementDeclaration
;
32 import org
.apache
.xerces
.xs
.XSException
;
33 import org
.apache
.xerces
.xs
.XSImplementation
;
34 import org
.apache
.xerces
.xs
.XSLoader
;
35 import org
.apache
.xerces
.xs
.XSModel
;
36 import org
.apache
.xerces
.xs
.XSModelGroup
;
37 import org
.apache
.xerces
.xs
.XSNamedMap
;
38 import org
.apache
.xerces
.xs
.XSObjectList
;
39 import org
.apache
.xerces
.xs
.XSParticle
;
40 import org
.apache
.xerces
.xs
.XSSimpleTypeDefinition
;
41 import org
.apache
.xerces
.xs
.XSTerm
;
42 import org
.apache
.xerces
.xs
.XSTypeDefinition
;
43 import org
.argeo
.api
.acr
.CrAttributeType
;
44 import org
.argeo
.api
.acr
.NamespaceUtils
;
45 import org
.argeo
.api
.acr
.RuntimeNamespaceContext
;
46 import org
.argeo
.api
.cms
.CmsLog
;
47 import org
.xml
.sax
.ErrorHandler
;
48 import org
.xml
.sax
.SAXException
;
49 import org
.xml
.sax
.SAXParseException
;
51 /** Register content types. */
53 private final static CmsLog log
= CmsLog
.getLog(TypesManager
.class);
54 // private Map<String, String> prefixes = new TreeMap<>();
56 // immutable factories
57 private SchemaFactory schemaFactory
;
59 /** Schema sources. */
60 private List
<Source
> sources
= new ArrayList
<>();
63 private Schema schema
;
64 private DocumentBuilderFactory documentBuilderFactory
;
65 private XSModel xsModel
;
66 private SortedMap
<QName
, Map
<QName
, CrAttributeType
>> types
;
68 private boolean validating
= false;
70 private final static boolean limited
= false;
72 public TypesManager() {
73 schemaFactory
= SchemaFactory
.newInstance(XMLConstants
.W3C_XML_SCHEMA_NS_URI
);
76 types
= new TreeMap
<>(NamespaceUtils
.QNAME_COMPARATOR
);
81 for (CmsContentTypes cs
: CmsContentTypes
.values()) {
82 if (cs
.getResource() != null) {
83 StreamSource source
= new StreamSource(cs
.getResource().toExternalForm());
86 RuntimeNamespaceContext
.register(cs
.getNamespace(), cs
.getDefaultPrefix());
92 public void registerTypes(String defaultPrefix
, String namespace
, String xsdSystemId
) {
93 // if (prefixes.containsKey(defaultPrefix))
94 // throw new IllegalStateException(
95 // "Prefix " + defaultPrefix + " is already mapped with " + prefixes.get(defaultPrefix));
96 // prefixes.put(defaultPrefix, namespace);
97 RuntimeNamespaceContext
.register(namespace
, defaultPrefix
);
99 if (xsdSystemId
!= null) {
100 sources
.add(new StreamSource(xsdSystemId
));
102 log
.debug(() -> "Registered types " + namespace
+ " from " + xsdSystemId
);
106 public Set
<QName
> listTypes() {
107 return types
.keySet();
110 public Map
<QName
, CrAttributeType
> getAttributeTypes(QName type
) {
111 if (!types
.containsKey(type
))
112 throw new IllegalArgumentException("Unkown type");
113 return types
.get(type
);
116 private synchronized void reload() {
119 schema
= schemaFactory
.newSchema(sources
.toArray(new Source
[sources
.size()]));
121 // document builder factory
122 // we force usage of Xerces for predictability
123 documentBuilderFactory
= limited ? DocumentBuilderFactory
.newInstance() : new DocumentBuilderFactoryImpl();
124 documentBuilderFactory
.setNamespaceAware(true);
126 documentBuilderFactory
.setXIncludeAware(true);
127 documentBuilderFactory
.setSchema(getSchema());
128 documentBuilderFactory
.setValidating(validating
);
132 // TODO use JVM implementation?
133 // DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
134 // XSImplementation implementation = (XSImplementation) registry.getDOMImplementation("XS-Loader");
135 XSImplementation xsImplementation
= new XSImplementationImpl();
136 XSLoader xsLoader
= xsImplementation
.createXSLoader(null);
137 List
<String
> systemIds
= new ArrayList
<>();
138 for (Source source
: sources
) {
139 systemIds
.add(source
.getSystemId());
141 StringList sl
= new StringListImpl(systemIds
.toArray(new String
[systemIds
.size()]), systemIds
.size());
142 xsModel
= xsLoader
.loadURIList(sl
);
145 // XSNamedMap map = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);
146 // for (int i = 0; i < map.getLength(); i++) {
147 // XSElementDeclaration eDec = (XSElementDeclaration) map.item(i);
148 // QName type = new QName(eDec.getNamespace(), eDec.getName());
152 } catch (XSException
| SAXException e
) {
153 throw new IllegalStateException("Cannot reload types", e
);
157 private void collectTypes() {
160 XSNamedMap topLevelElements
= xsModel
.getComponents(XSConstants
.ELEMENT_DECLARATION
);
161 for (int i
= 0; i
< topLevelElements
.getLength(); i
++) {
162 XSElementDeclaration eDec
= (XSElementDeclaration
) topLevelElements
.item(i
);
163 collectElementDeclaration("", eDec
);
167 XSNamedMap topLevelTypes
= xsModel
.getComponents(XSConstants
.TYPE_DEFINITION
);
168 for (int i
= 0; i
< topLevelTypes
.getLength(); i
++) {
169 XSTypeDefinition tDef
= (XSTypeDefinition
) topLevelTypes
.item(i
);
170 collectType(tDef
, null, null);
175 private void collectType(XSTypeDefinition tDef
, String namespace
, String nameHint
) {
176 if (tDef
.getTypeCategory() == XSTypeDefinition
.COMPLEX_TYPE
) {
177 XSComplexTypeDefinition ctDef
= (XSComplexTypeDefinition
) tDef
;
178 if (ctDef
.getContentType() != XSComplexTypeDefinition
.CONTENTTYPE_SIMPLE
179 || ctDef
.getAttributeUses().getLength() > 0 || ctDef
.getAttributeWildcard() != null) {
180 collectComplexType("", null, ctDef
);
182 throw new IllegalArgumentException("Unsupported type " + tDef
.getTypeCategory());
187 private void collectComplexType(String prefix
, QName parent
, XSComplexTypeDefinition ctDef
) {
188 if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_SIMPLE
) {
190 // content with attributes and a string value
192 XSSimpleTypeDefinition stDef
= ctDef
.getSimpleType();
193 // QName name = new QName(stDef.getNamespace(), stDef.getName());
194 // log.warn(prefix + "Simple " + ctDef + " - " + attributes);
195 // System.err.println(prefix + "Simple from " + parent + " - " + attributes);
197 // if (parentAttributes != null) {
198 // for (QName attr : attributes.keySet()) {
199 // if (!parentAttributes.containsKey(attr))
200 // System.err.println(prefix + " - " + attr + " not available in parent");
205 } else if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_ELEMENT
206 || ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_MIXED
) {
207 XSParticle p
= ctDef
.getParticle();
209 collectParticle(prefix
, p
, false);
210 } else if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_EMPTY
) {
211 // Parent only contains attributes
212 // if (parent != null)
213 // System.err.println(prefix + "Empty from " + parent + " - " + attributes);
214 // if (parentAttributes != null) {
215 // for (QName attr : attributes.keySet()) {
216 // if (!parentAttributes.containsKey(attr))
217 // System.err.println(prefix + " - " + attr + " not available in parent");
221 // log.debug(prefix + "Empty " + ctDef.getNamespace() + ":" + ctDef.getName() + " - " + attributes);
223 throw new IllegalArgumentException("Unsupported type " + ctDef
.getTypeCategory());
227 private void collectParticle(String prefix
, XSParticle particle
, boolean multipleFromAbove
) {
228 boolean orderable
= false;
230 XSTerm term
= particle
.getTerm();
232 if (particle
.getMaxOccurs() == 0) {
236 boolean mandatory
= false;
237 if (particle
.getMinOccurs() > 0) {
241 boolean multiple
= false;
242 if (particle
.getMaxOccurs() > 1 || particle
.getMaxOccursUnbounded()) {
245 if (!multiple
&& multipleFromAbove
)
248 if (term
.getType() == XSConstants
.ELEMENT_DECLARATION
) {
249 XSElementDeclaration eDec
= (XSElementDeclaration
) term
;
251 collectElementDeclaration(prefix
, eDec
);
252 // If this particle is a wildcard (an <xs:any> )then it
253 // is converted into a node def.
254 } else if (term
.getType() == XSConstants
.WILDCARD
) {
255 // TODO can be anything
257 // If this particle is a model group (one of
258 // <xs:sequence>, <xs:choice> or <xs:all>) then
259 // it subparticles must be processed.
260 } else if (term
.getType() == XSConstants
.MODEL_GROUP
) {
261 XSModelGroup mg
= (XSModelGroup
) term
;
263 if (mg
.getCompositor() == XSModelGroup
.COMPOSITOR_SEQUENCE
) {
266 XSObjectList list
= mg
.getParticles();
267 for (int i
= 0; i
< list
.getLength(); i
++) {
268 XSParticle pp
= (XSParticle
) list
.item(i
);
269 collectParticle(prefix
+ " ", pp
, multiple
);
274 private void collectElementDeclaration(String prefix
, XSElementDeclaration eDec
) {
275 QName name
= new QName(eDec
.getNamespace(), eDec
.getName());
276 XSTypeDefinition tDef
= eDec
.getTypeDefinition();
278 XSComplexTypeDefinition ctDef
= null;
279 Map
<QName
, CrAttributeType
> attributes
= new HashMap
<>();
280 if (tDef
.getTypeCategory() == XSTypeDefinition
.SIMPLE_TYPE
) {
281 XSSimpleTypeDefinition stDef
= (XSSimpleTypeDefinition
) tDef
;
282 // System.err.println(prefix + "Simple element " + name);
283 } else if (tDef
.getTypeCategory() == XSTypeDefinition
.COMPLEX_TYPE
) {
284 ctDef
= (XSComplexTypeDefinition
) tDef
;
285 if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_SIMPLE
286 && ctDef
.getAttributeUses().getLength() == 0 && ctDef
.getAttributeWildcard() == null) {
287 XSSimpleTypeDefinition stDef
= ctDef
.getSimpleType();
288 // System.err.println(prefix + "Simplified element " + name);
290 if (!types
.containsKey(name
)) {
291 // System.out.println(prefix + "Element " + name);
293 XSObjectList list
= ctDef
.getAttributeUses();
294 for (int i
= 0; i
< list
.getLength(); i
++) {
295 XSAttributeUse au
= (XSAttributeUse
) list
.item(i
);
296 XSAttributeDeclaration ad
= au
.getAttrDeclaration();
297 QName attrName
= new QName(ad
.getNamespace(), ad
.getName());
298 // Get the simple type def for this attribute
299 XSSimpleTypeDefinition std
= ad
.getTypeDefinition();
300 attributes
.put(attrName
, xsToCrType(std
.getBuiltInKind()));
301 // System.out.println(prefix + " - " + attrName + " = " + attributes.get(attrName));
304 types
.put(name
, attributes
);
306 collectComplexType(prefix
+ " ", name
, ctDef
);
313 private CrAttributeType
xsToCrType(short kind
) {
314 CrAttributeType propertyType
;
316 case XSConstants
.ANYSIMPLETYPE_DT
:
317 case XSConstants
.STRING_DT
:
318 case XSConstants
.ID_DT
:
319 case XSConstants
.ENTITY_DT
:
320 case XSConstants
.NOTATION_DT
:
321 case XSConstants
.NORMALIZEDSTRING_DT
:
322 case XSConstants
.TOKEN_DT
:
323 case XSConstants
.LANGUAGE_DT
:
324 case XSConstants
.NMTOKEN_DT
:
325 propertyType
= CrAttributeType
.STRING
;
327 case XSConstants
.BOOLEAN_DT
:
328 propertyType
= CrAttributeType
.BOOLEAN
;
330 case XSConstants
.DECIMAL_DT
:
331 case XSConstants
.FLOAT_DT
:
332 case XSConstants
.DOUBLE_DT
:
333 propertyType
= CrAttributeType
.DOUBLE
;
335 case XSConstants
.DURATION_DT
:
336 case XSConstants
.DATETIME_DT
:
337 case XSConstants
.TIME_DT
:
338 case XSConstants
.DATE_DT
:
339 case XSConstants
.GYEARMONTH_DT
:
340 case XSConstants
.GYEAR_DT
:
341 case XSConstants
.GMONTHDAY_DT
:
342 case XSConstants
.GDAY_DT
:
343 case XSConstants
.GMONTH_DT
:
344 propertyType
= CrAttributeType
.DATE_TIME
;
346 case XSConstants
.HEXBINARY_DT
:
347 case XSConstants
.BASE64BINARY_DT
:
348 case XSConstants
.ANYURI_DT
:
349 propertyType
= CrAttributeType
.ANY_URI
;
351 case XSConstants
.QNAME_DT
:
352 case XSConstants
.NAME_DT
:
353 case XSConstants
.NCNAME_DT
:
354 // TODO support QName?
355 propertyType
= CrAttributeType
.STRING
;
357 case XSConstants
.IDREF_DT
:
358 // TODO support references?
359 propertyType
= CrAttributeType
.STRING
;
361 case XSConstants
.INTEGER_DT
:
362 case XSConstants
.NONPOSITIVEINTEGER_DT
:
363 case XSConstants
.NEGATIVEINTEGER_DT
:
364 case XSConstants
.LONG_DT
:
365 case XSConstants
.INT_DT
:
366 case XSConstants
.SHORT_DT
:
367 case XSConstants
.BYTE_DT
:
368 case XSConstants
.NONNEGATIVEINTEGER_DT
:
369 case XSConstants
.UNSIGNEDLONG_DT
:
370 case XSConstants
.UNSIGNEDINT_DT
:
371 case XSConstants
.UNSIGNEDSHORT_DT
:
372 case XSConstants
.UNSIGNEDBYTE_DT
:
373 case XSConstants
.POSITIVEINTEGER_DT
:
374 propertyType
= CrAttributeType
.LONG
;
376 case XSConstants
.LISTOFUNION_DT
:
377 case XSConstants
.LIST_DT
:
378 case XSConstants
.UNAVAILABLE_DT
:
379 propertyType
= CrAttributeType
.STRING
;
382 propertyType
= CrAttributeType
.STRING
;
388 public DocumentBuilder
newDocumentBuilder() {
390 DocumentBuilder dBuilder
= documentBuilderFactory
.newDocumentBuilder();
391 dBuilder
.setErrorHandler(new ErrorHandler() {
394 public void warning(SAXParseException exception
) throws SAXException
{
399 public void fatalError(SAXParseException exception
) throws SAXException
{
400 log
.error(exception
);
404 public void error(SAXParseException exception
) throws SAXException
{
405 log
.error(exception
);
409 } catch (ParserConfigurationException e
) {
410 throw new IllegalStateException("Cannot create document builder", e
);
414 public void printTypes() {
417 // Convert top level complex type definitions to node types
418 log
.debug("\n## TYPES");
419 XSNamedMap map
= xsModel
.getComponents(XSConstants
.TYPE_DEFINITION
);
420 for (int i
= 0; i
< map
.getLength(); i
++) {
421 XSTypeDefinition tDef
= (XSTypeDefinition
) map
.item(i
);
424 // Convert local (anonymous) complex type defs found in top level
425 // element declarations
426 log
.debug("\n## ELEMENTS");
427 map
= xsModel
.getComponents(XSConstants
.ELEMENT_DECLARATION
);
428 for (int i
= 0; i
< map
.getLength(); i
++) {
429 XSElementDeclaration eDec
= (XSElementDeclaration
) map
.item(i
);
430 XSTypeDefinition tDef
= eDec
.getTypeDefinition();
431 log
.debug(eDec
+ ", " + tDef
);
433 log
.debug("\n## ATTRIBUTES");
434 map
= xsModel
.getComponents(XSConstants
.ATTRIBUTE_DECLARATION
);
435 for (int i
= 0; i
< map
.getLength(); i
++) {
436 XSAttributeDeclaration eDec
= (XSAttributeDeclaration
) map
.item(i
);
437 XSTypeDefinition tDef
= eDec
.getTypeDefinition();
438 log
.debug(eDec
.getNamespace() + ":" + eDec
.getName() + ", " + tDef
);
440 } catch (ClassCastException
| XSException e
) {
441 throw new RuntimeException(e
);
446 public void validate(Source source
) throws IOException
{
450 synchronized (this) {
451 validator
= schema
.newValidator();
454 validator
.validate(source
);
455 } catch (SAXException e
) {
456 log
.error(source
+ " is not valid ", e
);
457 // throw new IllegalArgumentException("Provided source is not valid", e);
461 // public Map<String, String> getPrefixes() {
465 public List
<Source
> getSources() {
469 public Schema
getSchema() {