1 package org
.argeo
.cms
.acr
;
3 import java
.io
.IOException
;
4 import java
.util
.ArrayList
;
5 import java
.util
.HashMap
;
8 import java
.util
.Objects
;
10 import java
.util
.SortedMap
;
11 import java
.util
.TreeMap
;
13 import javax
.xml
.XMLConstants
;
14 import javax
.xml
.namespace
.QName
;
15 import javax
.xml
.parsers
.DocumentBuilder
;
16 import javax
.xml
.parsers
.DocumentBuilderFactory
;
17 import javax
.xml
.parsers
.ParserConfigurationException
;
18 import javax
.xml
.transform
.Source
;
19 import javax
.xml
.transform
.stream
.StreamSource
;
20 import javax
.xml
.validation
.Schema
;
21 import javax
.xml
.validation
.SchemaFactory
;
22 import javax
.xml
.validation
.Validator
;
24 import org
.apache
.xerces
.impl
.xs
.XSImplementationImpl
;
25 import org
.apache
.xerces
.impl
.xs
.util
.StringListImpl
;
26 import org
.apache
.xerces
.jaxp
.DocumentBuilderFactoryImpl
;
27 import org
.apache
.xerces
.xs
.StringList
;
28 import org
.apache
.xerces
.xs
.XSAttributeDeclaration
;
29 import org
.apache
.xerces
.xs
.XSAttributeUse
;
30 import org
.apache
.xerces
.xs
.XSComplexTypeDefinition
;
31 import org
.apache
.xerces
.xs
.XSConstants
;
32 import org
.apache
.xerces
.xs
.XSElementDeclaration
;
33 import org
.apache
.xerces
.xs
.XSException
;
34 import org
.apache
.xerces
.xs
.XSImplementation
;
35 import org
.apache
.xerces
.xs
.XSLoader
;
36 import org
.apache
.xerces
.xs
.XSModel
;
37 import org
.apache
.xerces
.xs
.XSModelGroup
;
38 import org
.apache
.xerces
.xs
.XSNamedMap
;
39 import org
.apache
.xerces
.xs
.XSObjectList
;
40 import org
.apache
.xerces
.xs
.XSParticle
;
41 import org
.apache
.xerces
.xs
.XSSimpleTypeDefinition
;
42 import org
.apache
.xerces
.xs
.XSTerm
;
43 import org
.apache
.xerces
.xs
.XSTypeDefinition
;
44 import org
.argeo
.api
.acr
.CrAttributeType
;
45 import org
.argeo
.api
.cms
.CmsLog
;
46 import org
.xml
.sax
.ErrorHandler
;
47 import org
.xml
.sax
.SAXException
;
48 import org
.xml
.sax
.SAXParseException
;
50 /** Register content types. */
52 private final static CmsLog log
= CmsLog
.getLog(TypesManager
.class);
53 private Map
<String
, String
> prefixes
= new TreeMap
<>();
55 // immutable factories
56 private SchemaFactory schemaFactory
;
58 /** Schema sources. */
59 private List
<Source
> sources
= new ArrayList
<>();
62 private Schema schema
;
63 private DocumentBuilderFactory documentBuilderFactory
;
64 private XSModel xsModel
;
65 private SortedMap
<QName
, Map
<QName
, CrAttributeType
>> types
;
67 private boolean validating
= true;
69 private final static boolean limited
= true;
71 public TypesManager() {
72 schemaFactory
= SchemaFactory
.newInstance(XMLConstants
.W3C_XML_SCHEMA_NS_URI
);
75 types
= new TreeMap
<>((qn1
, qn2
) -> {
76 if (Objects
.equals(qn1
.getNamespaceURI(), qn2
.getNamespaceURI())) {// same namespace
77 return qn1
.getLocalPart().compareTo(qn2
.getLocalPart());
79 return qn1
.getNamespaceURI().compareTo(qn2
.getNamespaceURI());
85 public synchronized void init() {
86 // prefixes.put(CrName.CR_DEFAULT_PREFIX, CrName.CR_NAMESPACE_URI);
87 // prefixes.put("basic", CrName.CR_NAMESPACE_URI);
88 // prefixes.put("owner", CrName.CR_NAMESPACE_URI);
89 // prefixes.put("posix", CrName.CR_NAMESPACE_URI);
91 for (CmsContentTypes cs
: CmsContentTypes
.values()) {
92 StreamSource source
= new StreamSource(cs
.getResource().toExternalForm());
94 if (prefixes
.containsKey(cs
.getDefaultPrefix()))
95 throw new IllegalStateException("Prefix " + cs
.getDefaultPrefix() + " is already mapped with "
96 + prefixes
.get(cs
.getDefaultPrefix()));
97 prefixes
.put(cs
.getDefaultPrefix(), cs
.getNamespace());
103 public synchronized void registerTypes(String defaultPrefix
, String namespace
, String xsdSystemId
) {
104 if (prefixes
.containsKey(defaultPrefix
))
105 throw new IllegalStateException(
106 "Prefix " + defaultPrefix
+ " is already mapped with " + prefixes
.get(defaultPrefix
));
107 prefixes
.put(defaultPrefix
, namespace
);
109 sources
.add(new StreamSource(xsdSystemId
));
113 public Set
<QName
> listTypes() {
114 return types
.keySet();
117 public Map
<QName
, CrAttributeType
> getAttributeTypes(QName type
) {
118 if (!types
.containsKey(type
))
119 throw new IllegalArgumentException("Unkown type");
120 return types
.get(type
);
123 private synchronized void reload() {
126 schema
= schemaFactory
.newSchema(sources
.toArray(new Source
[sources
.size()]));
128 // document builder factory
129 // we force usage of Xerces for predictability
130 documentBuilderFactory
= limited ? DocumentBuilderFactory
.newInstance() : new DocumentBuilderFactoryImpl();
131 documentBuilderFactory
.setNamespaceAware(true);
133 documentBuilderFactory
.setXIncludeAware(true);
134 documentBuilderFactory
.setSchema(getSchema());
135 documentBuilderFactory
.setValidating(validating
);
139 // TODO use JVM implementation?
140 // DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
141 // XSImplementation implementation = (XSImplementation) registry.getDOMImplementation("XS-Loader");
142 XSImplementation xsImplementation
= new XSImplementationImpl();
143 XSLoader xsLoader
= xsImplementation
.createXSLoader(null);
144 List
<String
> systemIds
= new ArrayList
<>();
145 for (Source source
: sources
) {
146 systemIds
.add(source
.getSystemId());
148 StringList sl
= new StringListImpl(systemIds
.toArray(new String
[systemIds
.size()]), systemIds
.size());
149 xsModel
= xsLoader
.loadURIList(sl
);
152 // XSNamedMap map = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);
153 // for (int i = 0; i < map.getLength(); i++) {
154 // XSElementDeclaration eDec = (XSElementDeclaration) map.item(i);
155 // QName type = new QName(eDec.getNamespace(), eDec.getName());
159 } catch (XSException
| SAXException e
) {
160 throw new IllegalStateException("Cannot relaod types");
164 private void collectTypes() {
167 XSNamedMap topLevelElements
= xsModel
.getComponents(XSConstants
.ELEMENT_DECLARATION
);
168 for (int i
= 0; i
< topLevelElements
.getLength(); i
++) {
169 XSElementDeclaration eDec
= (XSElementDeclaration
) topLevelElements
.item(i
);
170 collectElementDeclaration("", eDec
);
174 XSNamedMap topLevelTypes
= xsModel
.getComponents(XSConstants
.TYPE_DEFINITION
);
175 for (int i
= 0; i
< topLevelTypes
.getLength(); i
++) {
176 XSTypeDefinition tDef
= (XSTypeDefinition
) topLevelTypes
.item(i
);
177 collectType(tDef
, null, null);
182 private void collectType(XSTypeDefinition tDef
, String namespace
, String nameHint
) {
183 if (tDef
.getTypeCategory() == XSTypeDefinition
.COMPLEX_TYPE
) {
184 XSComplexTypeDefinition ctDef
= (XSComplexTypeDefinition
) tDef
;
185 if (ctDef
.getContentType() != XSComplexTypeDefinition
.CONTENTTYPE_SIMPLE
186 || ctDef
.getAttributeUses().getLength() > 0 || ctDef
.getAttributeWildcard() != null) {
187 collectComplexType("", null, ctDef
);
189 throw new IllegalArgumentException("Unsupported type " + tDef
.getTypeCategory());
194 private void collectComplexType(String prefix
, QName parent
, XSComplexTypeDefinition ctDef
) {
195 if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_SIMPLE
) {
197 // content with attributes and a string value
199 XSSimpleTypeDefinition stDef
= ctDef
.getSimpleType();
200 // QName name = new QName(stDef.getNamespace(), stDef.getName());
201 // log.warn(prefix + "Simple " + ctDef + " - " + attributes);
202 // System.err.println(prefix + "Simple from " + parent + " - " + attributes);
204 // if (parentAttributes != null) {
205 // for (QName attr : attributes.keySet()) {
206 // if (!parentAttributes.containsKey(attr))
207 // System.err.println(prefix + " - " + attr + " not available in parent");
212 } else if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_ELEMENT
213 || ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_MIXED
) {
214 XSParticle p
= ctDef
.getParticle();
216 collectParticle(prefix
, p
, false);
217 } else if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_EMPTY
) {
218 // Parent only contains attributes
219 // if (parent != null)
220 // System.err.println(prefix + "Empty from " + parent + " - " + attributes);
221 // if (parentAttributes != null) {
222 // for (QName attr : attributes.keySet()) {
223 // if (!parentAttributes.containsKey(attr))
224 // System.err.println(prefix + " - " + attr + " not available in parent");
228 // log.debug(prefix + "Empty " + ctDef.getNamespace() + ":" + ctDef.getName() + " - " + attributes);
230 throw new IllegalArgumentException("Unsupported type " + ctDef
.getTypeCategory());
234 private void collectParticle(String prefix
, XSParticle particle
, boolean multipleFromAbove
) {
235 boolean orderable
= false;
237 XSTerm term
= particle
.getTerm();
239 if (particle
.getMaxOccurs() == 0) {
243 boolean mandatory
= false;
244 if (particle
.getMinOccurs() > 0) {
248 boolean multiple
= false;
249 if (particle
.getMaxOccurs() > 1 || particle
.getMaxOccursUnbounded()) {
252 if (!multiple
&& multipleFromAbove
)
255 if (term
.getType() == XSConstants
.ELEMENT_DECLARATION
) {
256 XSElementDeclaration eDec
= (XSElementDeclaration
) term
;
258 collectElementDeclaration(prefix
, eDec
);
259 // If this particle is a wildcard (an <xs:any> )then it
260 // is converted into a node def.
261 } else if (term
.getType() == XSConstants
.WILDCARD
) {
262 // TODO can be anything
264 // If this particle is a model group (one of
265 // <xs:sequence>, <xs:choice> or <xs:all>) then
266 // it subparticles must be processed.
267 } else if (term
.getType() == XSConstants
.MODEL_GROUP
) {
268 XSModelGroup mg
= (XSModelGroup
) term
;
270 if (mg
.getCompositor() == XSModelGroup
.COMPOSITOR_SEQUENCE
) {
273 XSObjectList list
= mg
.getParticles();
274 for (int i
= 0; i
< list
.getLength(); i
++) {
275 XSParticle pp
= (XSParticle
) list
.item(i
);
276 collectParticle(prefix
+ " ", pp
, multiple
);
281 private void collectElementDeclaration(String prefix
, XSElementDeclaration eDec
) {
282 QName name
= new QName(eDec
.getNamespace(), eDec
.getName());
283 XSTypeDefinition tDef
= eDec
.getTypeDefinition();
285 XSComplexTypeDefinition ctDef
= null;
286 Map
<QName
, CrAttributeType
> attributes
= new HashMap
<>();
287 if (tDef
.getTypeCategory() == XSTypeDefinition
.SIMPLE_TYPE
) {
288 XSSimpleTypeDefinition stDef
= (XSSimpleTypeDefinition
) tDef
;
289 // System.err.println(prefix + "Simple element " + name);
290 } else if (tDef
.getTypeCategory() == XSTypeDefinition
.COMPLEX_TYPE
) {
291 ctDef
= (XSComplexTypeDefinition
) tDef
;
292 if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_SIMPLE
293 && ctDef
.getAttributeUses().getLength() == 0 && ctDef
.getAttributeWildcard() == null) {
294 XSSimpleTypeDefinition stDef
= ctDef
.getSimpleType();
295 // System.err.println(prefix + "Simplified element " + name);
297 if (!types
.containsKey(name
)) {
298 // System.out.println(prefix + "Element " + name);
300 XSObjectList list
= ctDef
.getAttributeUses();
301 for (int i
= 0; i
< list
.getLength(); i
++) {
302 XSAttributeUse au
= (XSAttributeUse
) list
.item(i
);
303 XSAttributeDeclaration ad
= au
.getAttrDeclaration();
304 QName attrName
= new QName(ad
.getNamespace(), ad
.getName());
305 // Get the simple type def for this attribute
306 XSSimpleTypeDefinition std
= ad
.getTypeDefinition();
307 attributes
.put(attrName
, xsToCrType(std
.getBuiltInKind()));
308 // System.out.println(prefix + " - " + attrName + " = " + attributes.get(attrName));
311 types
.put(name
, attributes
);
313 collectComplexType(prefix
+ " ", name
, ctDef
);
320 private CrAttributeType
xsToCrType(short kind
) {
321 CrAttributeType propertyType
;
323 case XSConstants
.ANYSIMPLETYPE_DT
:
324 case XSConstants
.STRING_DT
:
325 case XSConstants
.ID_DT
:
326 case XSConstants
.ENTITY_DT
:
327 case XSConstants
.NOTATION_DT
:
328 case XSConstants
.NORMALIZEDSTRING_DT
:
329 case XSConstants
.TOKEN_DT
:
330 case XSConstants
.LANGUAGE_DT
:
331 case XSConstants
.NMTOKEN_DT
:
332 propertyType
= CrAttributeType
.STRING
;
334 case XSConstants
.BOOLEAN_DT
:
335 propertyType
= CrAttributeType
.BOOLEAN
;
337 case XSConstants
.DECIMAL_DT
:
338 case XSConstants
.FLOAT_DT
:
339 case XSConstants
.DOUBLE_DT
:
340 propertyType
= CrAttributeType
.DOUBLE
;
342 case XSConstants
.DURATION_DT
:
343 case XSConstants
.DATETIME_DT
:
344 case XSConstants
.TIME_DT
:
345 case XSConstants
.DATE_DT
:
346 case XSConstants
.GYEARMONTH_DT
:
347 case XSConstants
.GYEAR_DT
:
348 case XSConstants
.GMONTHDAY_DT
:
349 case XSConstants
.GDAY_DT
:
350 case XSConstants
.GMONTH_DT
:
351 propertyType
= CrAttributeType
.DATE_TIME
;
353 case XSConstants
.HEXBINARY_DT
:
354 case XSConstants
.BASE64BINARY_DT
:
355 case XSConstants
.ANYURI_DT
:
356 propertyType
= CrAttributeType
.ANY_URI
;
358 case XSConstants
.QNAME_DT
:
359 case XSConstants
.NAME_DT
:
360 case XSConstants
.NCNAME_DT
:
361 // TODO support QName?
362 propertyType
= CrAttributeType
.STRING
;
364 case XSConstants
.IDREF_DT
:
365 // TODO support references?
366 propertyType
= CrAttributeType
.STRING
;
368 case XSConstants
.INTEGER_DT
:
369 case XSConstants
.NONPOSITIVEINTEGER_DT
:
370 case XSConstants
.NEGATIVEINTEGER_DT
:
371 case XSConstants
.LONG_DT
:
372 case XSConstants
.INT_DT
:
373 case XSConstants
.SHORT_DT
:
374 case XSConstants
.BYTE_DT
:
375 case XSConstants
.NONNEGATIVEINTEGER_DT
:
376 case XSConstants
.UNSIGNEDLONG_DT
:
377 case XSConstants
.UNSIGNEDINT_DT
:
378 case XSConstants
.UNSIGNEDSHORT_DT
:
379 case XSConstants
.UNSIGNEDBYTE_DT
:
380 case XSConstants
.POSITIVEINTEGER_DT
:
381 propertyType
= CrAttributeType
.LONG
;
383 case XSConstants
.LISTOFUNION_DT
:
384 case XSConstants
.LIST_DT
:
385 case XSConstants
.UNAVAILABLE_DT
:
386 propertyType
= CrAttributeType
.STRING
;
389 propertyType
= CrAttributeType
.STRING
;
395 public DocumentBuilder
newDocumentBuilder() {
397 DocumentBuilder dBuilder
= documentBuilderFactory
.newDocumentBuilder();
398 dBuilder
.setErrorHandler(new ErrorHandler() {
401 public void warning(SAXParseException exception
) throws SAXException
{
406 public void fatalError(SAXParseException exception
) throws SAXException
{
407 log
.error(exception
);
411 public void error(SAXParseException exception
) throws SAXException
{
412 log
.error(exception
);
416 } catch (ParserConfigurationException e
) {
417 throw new IllegalStateException("Cannot create document builder", e
);
421 public void printTypes() {
424 // Convert top level complex type definitions to node types
425 log
.debug("\n## TYPES");
426 XSNamedMap map
= xsModel
.getComponents(XSConstants
.TYPE_DEFINITION
);
427 for (int i
= 0; i
< map
.getLength(); i
++) {
428 XSTypeDefinition tDef
= (XSTypeDefinition
) map
.item(i
);
431 // Convert local (anonymous) complex type defs found in top level
432 // element declarations
433 log
.debug("\n## ELEMENTS");
434 map
= xsModel
.getComponents(XSConstants
.ELEMENT_DECLARATION
);
435 for (int i
= 0; i
< map
.getLength(); i
++) {
436 XSElementDeclaration eDec
= (XSElementDeclaration
) map
.item(i
);
437 XSTypeDefinition tDef
= eDec
.getTypeDefinition();
438 log
.debug(eDec
+ ", " + tDef
);
440 log
.debug("\n## ATTRIBUTES");
441 map
= xsModel
.getComponents(XSConstants
.ATTRIBUTE_DECLARATION
);
442 for (int i
= 0; i
< map
.getLength(); i
++) {
443 XSAttributeDeclaration eDec
= (XSAttributeDeclaration
) map
.item(i
);
444 XSTypeDefinition tDef
= eDec
.getTypeDefinition();
445 log
.debug(eDec
.getNamespace() + ":" + eDec
.getName() + ", " + tDef
);
447 } catch (ClassCastException
| XSException e
) {
448 throw new RuntimeException(e
);
453 public void validate(Source source
) throws IOException
{
457 synchronized (this) {
458 validator
= schema
.newValidator();
461 validator
.validate(source
);
462 } catch (SAXException e
) {
463 throw new IllegalArgumentException("Provided source is not valid", e
);
467 public Map
<String
, String
> getPrefixes() {
471 public List
<Source
> getSources() {
475 public Schema
getSchema() {