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
.acr
.NamespaceUtils
;
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
= true;
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
);
80 public synchronized void init() {
81 // prefixes.put(CrName.CR_DEFAULT_PREFIX, CrName.CR_NAMESPACE_URI);
82 // prefixes.put("basic", CrName.CR_NAMESPACE_URI);
83 // prefixes.put("owner", CrName.CR_NAMESPACE_URI);
84 // prefixes.put("posix", CrName.CR_NAMESPACE_URI);
86 for (CmsContentTypes cs
: CmsContentTypes
.values()) {
87 StreamSource source
= new StreamSource(cs
.getResource().toExternalForm());
89 if (prefixes
.containsKey(cs
.getDefaultPrefix()))
90 throw new IllegalStateException("Prefix " + cs
.getDefaultPrefix() + " is already mapped with "
91 + prefixes
.get(cs
.getDefaultPrefix()));
92 prefixes
.put(cs
.getDefaultPrefix(), cs
.getNamespace());
98 public synchronized void registerTypes(String defaultPrefix
, String namespace
, String xsdSystemId
) {
99 if (prefixes
.containsKey(defaultPrefix
))
100 throw new IllegalStateException(
101 "Prefix " + defaultPrefix
+ " is already mapped with " + prefixes
.get(defaultPrefix
));
102 prefixes
.put(defaultPrefix
, namespace
);
104 sources
.add(new StreamSource(xsdSystemId
));
108 public Set
<QName
> listTypes() {
109 return types
.keySet();
112 public Map
<QName
, CrAttributeType
> getAttributeTypes(QName type
) {
113 if (!types
.containsKey(type
))
114 throw new IllegalArgumentException("Unkown type");
115 return types
.get(type
);
118 private synchronized void reload() {
121 schema
= schemaFactory
.newSchema(sources
.toArray(new Source
[sources
.size()]));
123 // document builder factory
124 // we force usage of Xerces for predictability
125 documentBuilderFactory
= limited ? DocumentBuilderFactory
.newInstance() : new DocumentBuilderFactoryImpl();
126 documentBuilderFactory
.setNamespaceAware(true);
128 documentBuilderFactory
.setXIncludeAware(true);
129 documentBuilderFactory
.setSchema(getSchema());
130 documentBuilderFactory
.setValidating(validating
);
134 // TODO use JVM implementation?
135 // DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
136 // XSImplementation implementation = (XSImplementation) registry.getDOMImplementation("XS-Loader");
137 XSImplementation xsImplementation
= new XSImplementationImpl();
138 XSLoader xsLoader
= xsImplementation
.createXSLoader(null);
139 List
<String
> systemIds
= new ArrayList
<>();
140 for (Source source
: sources
) {
141 systemIds
.add(source
.getSystemId());
143 StringList sl
= new StringListImpl(systemIds
.toArray(new String
[systemIds
.size()]), systemIds
.size());
144 xsModel
= xsLoader
.loadURIList(sl
);
147 // XSNamedMap map = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);
148 // for (int i = 0; i < map.getLength(); i++) {
149 // XSElementDeclaration eDec = (XSElementDeclaration) map.item(i);
150 // QName type = new QName(eDec.getNamespace(), eDec.getName());
154 } catch (XSException
| SAXException e
) {
155 throw new IllegalStateException("Cannot reload types", e
);
159 private void collectTypes() {
162 XSNamedMap topLevelElements
= xsModel
.getComponents(XSConstants
.ELEMENT_DECLARATION
);
163 for (int i
= 0; i
< topLevelElements
.getLength(); i
++) {
164 XSElementDeclaration eDec
= (XSElementDeclaration
) topLevelElements
.item(i
);
165 collectElementDeclaration("", eDec
);
169 XSNamedMap topLevelTypes
= xsModel
.getComponents(XSConstants
.TYPE_DEFINITION
);
170 for (int i
= 0; i
< topLevelTypes
.getLength(); i
++) {
171 XSTypeDefinition tDef
= (XSTypeDefinition
) topLevelTypes
.item(i
);
172 collectType(tDef
, null, null);
177 private void collectType(XSTypeDefinition tDef
, String namespace
, String nameHint
) {
178 if (tDef
.getTypeCategory() == XSTypeDefinition
.COMPLEX_TYPE
) {
179 XSComplexTypeDefinition ctDef
= (XSComplexTypeDefinition
) tDef
;
180 if (ctDef
.getContentType() != XSComplexTypeDefinition
.CONTENTTYPE_SIMPLE
181 || ctDef
.getAttributeUses().getLength() > 0 || ctDef
.getAttributeWildcard() != null) {
182 collectComplexType("", null, ctDef
);
184 throw new IllegalArgumentException("Unsupported type " + tDef
.getTypeCategory());
189 private void collectComplexType(String prefix
, QName parent
, XSComplexTypeDefinition ctDef
) {
190 if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_SIMPLE
) {
192 // content with attributes and a string value
194 XSSimpleTypeDefinition stDef
= ctDef
.getSimpleType();
195 // QName name = new QName(stDef.getNamespace(), stDef.getName());
196 // log.warn(prefix + "Simple " + ctDef + " - " + attributes);
197 // System.err.println(prefix + "Simple from " + parent + " - " + attributes);
199 // if (parentAttributes != null) {
200 // for (QName attr : attributes.keySet()) {
201 // if (!parentAttributes.containsKey(attr))
202 // System.err.println(prefix + " - " + attr + " not available in parent");
207 } else if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_ELEMENT
208 || ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_MIXED
) {
209 XSParticle p
= ctDef
.getParticle();
211 collectParticle(prefix
, p
, false);
212 } else if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_EMPTY
) {
213 // Parent only contains attributes
214 // if (parent != null)
215 // System.err.println(prefix + "Empty from " + parent + " - " + attributes);
216 // if (parentAttributes != null) {
217 // for (QName attr : attributes.keySet()) {
218 // if (!parentAttributes.containsKey(attr))
219 // System.err.println(prefix + " - " + attr + " not available in parent");
223 // log.debug(prefix + "Empty " + ctDef.getNamespace() + ":" + ctDef.getName() + " - " + attributes);
225 throw new IllegalArgumentException("Unsupported type " + ctDef
.getTypeCategory());
229 private void collectParticle(String prefix
, XSParticle particle
, boolean multipleFromAbove
) {
230 boolean orderable
= false;
232 XSTerm term
= particle
.getTerm();
234 if (particle
.getMaxOccurs() == 0) {
238 boolean mandatory
= false;
239 if (particle
.getMinOccurs() > 0) {
243 boolean multiple
= false;
244 if (particle
.getMaxOccurs() > 1 || particle
.getMaxOccursUnbounded()) {
247 if (!multiple
&& multipleFromAbove
)
250 if (term
.getType() == XSConstants
.ELEMENT_DECLARATION
) {
251 XSElementDeclaration eDec
= (XSElementDeclaration
) term
;
253 collectElementDeclaration(prefix
, eDec
);
254 // If this particle is a wildcard (an <xs:any> )then it
255 // is converted into a node def.
256 } else if (term
.getType() == XSConstants
.WILDCARD
) {
257 // TODO can be anything
259 // If this particle is a model group (one of
260 // <xs:sequence>, <xs:choice> or <xs:all>) then
261 // it subparticles must be processed.
262 } else if (term
.getType() == XSConstants
.MODEL_GROUP
) {
263 XSModelGroup mg
= (XSModelGroup
) term
;
265 if (mg
.getCompositor() == XSModelGroup
.COMPOSITOR_SEQUENCE
) {
268 XSObjectList list
= mg
.getParticles();
269 for (int i
= 0; i
< list
.getLength(); i
++) {
270 XSParticle pp
= (XSParticle
) list
.item(i
);
271 collectParticle(prefix
+ " ", pp
, multiple
);
276 private void collectElementDeclaration(String prefix
, XSElementDeclaration eDec
) {
277 QName name
= new QName(eDec
.getNamespace(), eDec
.getName());
278 XSTypeDefinition tDef
= eDec
.getTypeDefinition();
280 XSComplexTypeDefinition ctDef
= null;
281 Map
<QName
, CrAttributeType
> attributes
= new HashMap
<>();
282 if (tDef
.getTypeCategory() == XSTypeDefinition
.SIMPLE_TYPE
) {
283 XSSimpleTypeDefinition stDef
= (XSSimpleTypeDefinition
) tDef
;
284 // System.err.println(prefix + "Simple element " + name);
285 } else if (tDef
.getTypeCategory() == XSTypeDefinition
.COMPLEX_TYPE
) {
286 ctDef
= (XSComplexTypeDefinition
) tDef
;
287 if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_SIMPLE
288 && ctDef
.getAttributeUses().getLength() == 0 && ctDef
.getAttributeWildcard() == null) {
289 XSSimpleTypeDefinition stDef
= ctDef
.getSimpleType();
290 // System.err.println(prefix + "Simplified element " + name);
292 if (!types
.containsKey(name
)) {
293 // System.out.println(prefix + "Element " + name);
295 XSObjectList list
= ctDef
.getAttributeUses();
296 for (int i
= 0; i
< list
.getLength(); i
++) {
297 XSAttributeUse au
= (XSAttributeUse
) list
.item(i
);
298 XSAttributeDeclaration ad
= au
.getAttrDeclaration();
299 QName attrName
= new QName(ad
.getNamespace(), ad
.getName());
300 // Get the simple type def for this attribute
301 XSSimpleTypeDefinition std
= ad
.getTypeDefinition();
302 attributes
.put(attrName
, xsToCrType(std
.getBuiltInKind()));
303 // System.out.println(prefix + " - " + attrName + " = " + attributes.get(attrName));
306 types
.put(name
, attributes
);
308 collectComplexType(prefix
+ " ", name
, ctDef
);
315 private CrAttributeType
xsToCrType(short kind
) {
316 CrAttributeType propertyType
;
318 case XSConstants
.ANYSIMPLETYPE_DT
:
319 case XSConstants
.STRING_DT
:
320 case XSConstants
.ID_DT
:
321 case XSConstants
.ENTITY_DT
:
322 case XSConstants
.NOTATION_DT
:
323 case XSConstants
.NORMALIZEDSTRING_DT
:
324 case XSConstants
.TOKEN_DT
:
325 case XSConstants
.LANGUAGE_DT
:
326 case XSConstants
.NMTOKEN_DT
:
327 propertyType
= CrAttributeType
.STRING
;
329 case XSConstants
.BOOLEAN_DT
:
330 propertyType
= CrAttributeType
.BOOLEAN
;
332 case XSConstants
.DECIMAL_DT
:
333 case XSConstants
.FLOAT_DT
:
334 case XSConstants
.DOUBLE_DT
:
335 propertyType
= CrAttributeType
.DOUBLE
;
337 case XSConstants
.DURATION_DT
:
338 case XSConstants
.DATETIME_DT
:
339 case XSConstants
.TIME_DT
:
340 case XSConstants
.DATE_DT
:
341 case XSConstants
.GYEARMONTH_DT
:
342 case XSConstants
.GYEAR_DT
:
343 case XSConstants
.GMONTHDAY_DT
:
344 case XSConstants
.GDAY_DT
:
345 case XSConstants
.GMONTH_DT
:
346 propertyType
= CrAttributeType
.DATE_TIME
;
348 case XSConstants
.HEXBINARY_DT
:
349 case XSConstants
.BASE64BINARY_DT
:
350 case XSConstants
.ANYURI_DT
:
351 propertyType
= CrAttributeType
.ANY_URI
;
353 case XSConstants
.QNAME_DT
:
354 case XSConstants
.NAME_DT
:
355 case XSConstants
.NCNAME_DT
:
356 // TODO support QName?
357 propertyType
= CrAttributeType
.STRING
;
359 case XSConstants
.IDREF_DT
:
360 // TODO support references?
361 propertyType
= CrAttributeType
.STRING
;
363 case XSConstants
.INTEGER_DT
:
364 case XSConstants
.NONPOSITIVEINTEGER_DT
:
365 case XSConstants
.NEGATIVEINTEGER_DT
:
366 case XSConstants
.LONG_DT
:
367 case XSConstants
.INT_DT
:
368 case XSConstants
.SHORT_DT
:
369 case XSConstants
.BYTE_DT
:
370 case XSConstants
.NONNEGATIVEINTEGER_DT
:
371 case XSConstants
.UNSIGNEDLONG_DT
:
372 case XSConstants
.UNSIGNEDINT_DT
:
373 case XSConstants
.UNSIGNEDSHORT_DT
:
374 case XSConstants
.UNSIGNEDBYTE_DT
:
375 case XSConstants
.POSITIVEINTEGER_DT
:
376 propertyType
= CrAttributeType
.LONG
;
378 case XSConstants
.LISTOFUNION_DT
:
379 case XSConstants
.LIST_DT
:
380 case XSConstants
.UNAVAILABLE_DT
:
381 propertyType
= CrAttributeType
.STRING
;
384 propertyType
= CrAttributeType
.STRING
;
390 public DocumentBuilder
newDocumentBuilder() {
392 DocumentBuilder dBuilder
= documentBuilderFactory
.newDocumentBuilder();
393 dBuilder
.setErrorHandler(new ErrorHandler() {
396 public void warning(SAXParseException exception
) throws SAXException
{
401 public void fatalError(SAXParseException exception
) throws SAXException
{
402 log
.error(exception
);
406 public void error(SAXParseException exception
) throws SAXException
{
407 log
.error(exception
);
411 } catch (ParserConfigurationException e
) {
412 throw new IllegalStateException("Cannot create document builder", e
);
416 public void printTypes() {
419 // Convert top level complex type definitions to node types
420 log
.debug("\n## TYPES");
421 XSNamedMap map
= xsModel
.getComponents(XSConstants
.TYPE_DEFINITION
);
422 for (int i
= 0; i
< map
.getLength(); i
++) {
423 XSTypeDefinition tDef
= (XSTypeDefinition
) map
.item(i
);
426 // Convert local (anonymous) complex type defs found in top level
427 // element declarations
428 log
.debug("\n## ELEMENTS");
429 map
= xsModel
.getComponents(XSConstants
.ELEMENT_DECLARATION
);
430 for (int i
= 0; i
< map
.getLength(); i
++) {
431 XSElementDeclaration eDec
= (XSElementDeclaration
) map
.item(i
);
432 XSTypeDefinition tDef
= eDec
.getTypeDefinition();
433 log
.debug(eDec
+ ", " + tDef
);
435 log
.debug("\n## ATTRIBUTES");
436 map
= xsModel
.getComponents(XSConstants
.ATTRIBUTE_DECLARATION
);
437 for (int i
= 0; i
< map
.getLength(); i
++) {
438 XSAttributeDeclaration eDec
= (XSAttributeDeclaration
) map
.item(i
);
439 XSTypeDefinition tDef
= eDec
.getTypeDefinition();
440 log
.debug(eDec
.getNamespace() + ":" + eDec
.getName() + ", " + tDef
);
442 } catch (ClassCastException
| XSException e
) {
443 throw new RuntimeException(e
);
448 public void validate(Source source
) throws IOException
{
452 synchronized (this) {
453 validator
= schema
.newValidator();
456 validator
.validate(source
);
457 } catch (SAXException e
) {
458 throw new IllegalArgumentException("Provided source is not valid", e
);
462 public Map
<String
, String
> getPrefixes() {
466 public List
<Source
> getSources() {
470 public Schema
getSchema() {