1 package org
.argeo
.cms
.acr
;
3 import java
.io
.IOException
;
5 import java
.util
.ArrayList
;
6 import java
.util
.HashMap
;
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
.acr
.RuntimeNamespaceContext
;
47 import org
.argeo
.api
.acr
.spi
.ContentNamespace
;
48 import org
.argeo
.api
.cms
.CmsLog
;
49 import org
.xml
.sax
.ErrorHandler
;
50 import org
.xml
.sax
.SAXException
;
51 import org
.xml
.sax
.SAXParseException
;
53 /** Register content types. */
55 private final static CmsLog log
= CmsLog
.getLog(TypesManager
.class);
56 // private Map<String, String> prefixes = new TreeMap<>();
58 // immutable factories
59 private SchemaFactory schemaFactory
;
61 /** Schema sources. */
62 private List
<URL
> sources
= new ArrayList
<>();
65 private Schema schema
;
66 private DocumentBuilderFactory documentBuilderFactory
;
67 // private XSModel xsModel;
68 private SortedMap
<QName
, Map
<QName
, CrAttributeType
>> types
;
70 private boolean validating
= false;
71 private boolean creatingXsModel
= true;
73 private final static boolean limited
= false;
75 public TypesManager() {
76 schemaFactory
= SchemaFactory
.newInstance(XMLConstants
.W3C_XML_SCHEMA_NS_URI
);
79 types
= new TreeMap
<>(NamespaceUtils
.QNAME_COMPARATOR
);
84 registerTypes(CmsContentNamespace
.values());
87 public void registerTypes(ContentNamespace
... namespaces
) {
88 // if (prefixes.containsKey(defaultPrefix))
89 // throw new IllegalStateException(
90 // "Prefix " + defaultPrefix + " is already mapped with " + prefixes.get(defaultPrefix));
91 // prefixes.put(defaultPrefix, namespace);
92 for (ContentNamespace contentNamespace
: namespaces
) {
93 RuntimeNamespaceContext
.register(contentNamespace
.getNamespaceURI(), contentNamespace
.getDefaultPrefix());
95 if (contentNamespace
.getSchemaResource() != null) {
96 sources
.add(contentNamespace
.getSchemaResource());
97 log
.debug(() -> "Registered types " + contentNamespace
.getNamespaceURI() + " from "
98 + contentNamespace
.getSchemaResource().toExternalForm());
104 public Set
<QName
> listTypes() {
105 return types
.keySet();
108 public Map
<QName
, CrAttributeType
> getAttributeTypes(QName type
) {
109 if (!types
.containsKey(type
))
110 throw new IllegalArgumentException("Unkown type");
111 return types
.get(type
);
114 private synchronized void reload() {
118 List
<StreamSource
> sourcesToUse
= new ArrayList
<>();
119 for (URL sourceUrl
: sources
) {
120 sourcesToUse
.add(new StreamSource(sourceUrl
.toExternalForm()));
122 // sourcesToUse.add(new StreamSource(sourceUrl.openStream()));
123 // } catch (IOException e) {
124 // log.error("Cannot open schema source " + sourceUrl);
127 schema
= schemaFactory
.newSchema(sourcesToUse
.toArray(new Source
[sourcesToUse
.size()]));
128 // for (StreamSource source : sourcesToUse) {
130 // source.getInputStream().close();
131 // } catch (IOException e) {
132 // // TODO Auto-generated catch block
133 // e.printStackTrace();
138 // document builder factory
139 // we force usage of Xerces for predictability
140 documentBuilderFactory
= limited ? DocumentBuilderFactory
.newInstance() : new DocumentBuilderFactoryImpl();
141 documentBuilderFactory
.setNamespaceAware(true);
143 documentBuilderFactory
.setXIncludeAware(true);
145 documentBuilderFactory
.setSchema(getSchema());
146 documentBuilderFactory
.setValidating(validating
);
150 if (creatingXsModel
) {
152 // TODO use JVM implementation?
153 // DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
154 // XSImplementation implementation = (XSImplementation) registry.getDOMImplementation("XS-Loader");
155 XSImplementation xsImplementation
= new XSImplementationImpl();
156 XSLoader xsLoader
= xsImplementation
.createXSLoader(null);
157 List
<String
> systemIds
= new ArrayList
<>();
158 for (URL sourceUrl
: sources
) {
159 systemIds
.add(sourceUrl
.toExternalForm());
161 StringList sl
= new StringListImpl(systemIds
.toArray(new String
[systemIds
.size()]), systemIds
.size());
162 XSModel xsModel
= xsLoader
.loadURIList(sl
);
165 // XSNamedMap map = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);
166 // for (int i = 0; i < map.getLength(); i++) {
167 // XSElementDeclaration eDec = (XSElementDeclaration) map.item(i);
168 // QName type = new QName(eDec.getNamespace(), eDec.getName());
171 collectTypes(xsModel
);
173 log
.debug("Created XS model");
176 } catch (XSException
| SAXException e
) {
177 throw new IllegalStateException("Cannot reload types", e
);
181 private void collectTypes(XSModel xsModel
) {
184 XSNamedMap topLevelElements
= xsModel
.getComponents(XSConstants
.ELEMENT_DECLARATION
);
185 for (int i
= 0; i
< topLevelElements
.getLength(); i
++) {
186 XSElementDeclaration eDec
= (XSElementDeclaration
) topLevelElements
.item(i
);
187 collectElementDeclaration("", eDec
);
191 XSNamedMap topLevelTypes
= xsModel
.getComponents(XSConstants
.TYPE_DEFINITION
);
192 for (int i
= 0; i
< topLevelTypes
.getLength(); i
++) {
193 XSTypeDefinition tDef
= (XSTypeDefinition
) topLevelTypes
.item(i
);
194 collectType(tDef
, null, null);
199 private void collectType(XSTypeDefinition tDef
, String namespace
, String nameHint
) {
200 if (tDef
.getTypeCategory() == XSTypeDefinition
.COMPLEX_TYPE
) {
201 XSComplexTypeDefinition ctDef
= (XSComplexTypeDefinition
) tDef
;
202 if (ctDef
.getContentType() != XSComplexTypeDefinition
.CONTENTTYPE_SIMPLE
203 || ctDef
.getAttributeUses().getLength() > 0 || ctDef
.getAttributeWildcard() != null) {
204 collectComplexType("", null, ctDef
);
206 throw new IllegalArgumentException("Unsupported type " + tDef
.getTypeCategory());
211 private void collectComplexType(String prefix
, QName parent
, XSComplexTypeDefinition ctDef
) {
212 if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_SIMPLE
) {
214 // content with attributes and a string value
216 XSSimpleTypeDefinition stDef
= ctDef
.getSimpleType();
217 // QName name = new QName(stDef.getNamespace(), stDef.getName());
218 // log.warn(prefix + "Simple " + ctDef + " - " + attributes);
219 // System.err.println(prefix + "Simple 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");
229 } else if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_ELEMENT
230 || ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_MIXED
) {
231 XSParticle p
= ctDef
.getParticle();
233 collectParticle(prefix
, p
, false);
234 } else if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_EMPTY
) {
235 // Parent only contains attributes
236 // if (parent != null)
237 // System.err.println(prefix + "Empty from " + parent + " - " + attributes);
238 // if (parentAttributes != null) {
239 // for (QName attr : attributes.keySet()) {
240 // if (!parentAttributes.containsKey(attr))
241 // System.err.println(prefix + " - " + attr + " not available in parent");
245 // log.debug(prefix + "Empty " + ctDef.getNamespace() + ":" + ctDef.getName() + " - " + attributes);
247 throw new IllegalArgumentException("Unsupported type " + ctDef
.getTypeCategory());
251 private void collectParticle(String prefix
, XSParticle particle
, boolean multipleFromAbove
) {
252 boolean orderable
= false;
254 XSTerm term
= particle
.getTerm();
256 if (particle
.getMaxOccurs() == 0) {
260 boolean mandatory
= false;
261 if (particle
.getMinOccurs() > 0) {
265 boolean multiple
= false;
266 if (particle
.getMaxOccurs() > 1 || particle
.getMaxOccursUnbounded()) {
269 if (!multiple
&& multipleFromAbove
)
272 if (term
.getType() == XSConstants
.ELEMENT_DECLARATION
) {
273 XSElementDeclaration eDec
= (XSElementDeclaration
) term
;
275 collectElementDeclaration(prefix
, eDec
);
276 // If this particle is a wildcard (an <xs:any> )then it
277 // is converted into a node def.
278 } else if (term
.getType() == XSConstants
.WILDCARD
) {
279 // TODO can be anything
281 // If this particle is a model group (one of
282 // <xs:sequence>, <xs:choice> or <xs:all>) then
283 // it subparticles must be processed.
284 } else if (term
.getType() == XSConstants
.MODEL_GROUP
) {
285 XSModelGroup mg
= (XSModelGroup
) term
;
287 if (mg
.getCompositor() == XSModelGroup
.COMPOSITOR_SEQUENCE
) {
290 XSObjectList list
= mg
.getParticles();
291 for (int i
= 0; i
< list
.getLength(); i
++) {
292 XSParticle pp
= (XSParticle
) list
.item(i
);
293 collectParticle(prefix
+ " ", pp
, multiple
);
298 private void collectElementDeclaration(String prefix
, XSElementDeclaration eDec
) {
299 QName name
= new QName(eDec
.getNamespace(), eDec
.getName());
300 XSTypeDefinition tDef
= eDec
.getTypeDefinition();
302 XSComplexTypeDefinition ctDef
= null;
303 Map
<QName
, CrAttributeType
> attributes
= new HashMap
<>();
304 if (tDef
.getTypeCategory() == XSTypeDefinition
.SIMPLE_TYPE
) {
305 XSSimpleTypeDefinition stDef
= (XSSimpleTypeDefinition
) tDef
;
306 // System.err.println(prefix + "Simple element " + name);
307 } else if (tDef
.getTypeCategory() == XSTypeDefinition
.COMPLEX_TYPE
) {
308 ctDef
= (XSComplexTypeDefinition
) tDef
;
309 if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_SIMPLE
310 && ctDef
.getAttributeUses().getLength() == 0 && ctDef
.getAttributeWildcard() == null) {
311 XSSimpleTypeDefinition stDef
= ctDef
.getSimpleType();
312 // System.err.println(prefix + "Simplified element " + name);
314 if (!types
.containsKey(name
)) {
315 // System.out.println(prefix + "Element " + name);
317 XSObjectList list
= ctDef
.getAttributeUses();
318 for (int i
= 0; i
< list
.getLength(); i
++) {
319 XSAttributeUse au
= (XSAttributeUse
) list
.item(i
);
320 XSAttributeDeclaration ad
= au
.getAttrDeclaration();
321 QName attrName
= new QName(ad
.getNamespace(), ad
.getName());
322 // Get the simple type def for this attribute
323 XSSimpleTypeDefinition std
= ad
.getTypeDefinition();
324 attributes
.put(attrName
, xsToCrType(std
.getBuiltInKind()));
325 // System.out.println(prefix + " - " + attrName + " = " + attributes.get(attrName));
328 types
.put(name
, attributes
);
330 collectComplexType(prefix
+ " ", name
, ctDef
);
337 private CrAttributeType
xsToCrType(short kind
) {
338 CrAttributeType propertyType
;
340 case XSConstants
.ANYSIMPLETYPE_DT
:
341 case XSConstants
.STRING_DT
:
342 case XSConstants
.ID_DT
:
343 case XSConstants
.ENTITY_DT
:
344 case XSConstants
.NOTATION_DT
:
345 case XSConstants
.NORMALIZEDSTRING_DT
:
346 case XSConstants
.TOKEN_DT
:
347 case XSConstants
.LANGUAGE_DT
:
348 case XSConstants
.NMTOKEN_DT
:
349 propertyType
= CrAttributeType
.STRING
;
351 case XSConstants
.BOOLEAN_DT
:
352 propertyType
= CrAttributeType
.BOOLEAN
;
354 case XSConstants
.DECIMAL_DT
:
355 case XSConstants
.FLOAT_DT
:
356 case XSConstants
.DOUBLE_DT
:
357 propertyType
= CrAttributeType
.DOUBLE
;
359 case XSConstants
.DURATION_DT
:
360 case XSConstants
.DATETIME_DT
:
361 case XSConstants
.TIME_DT
:
362 case XSConstants
.DATE_DT
:
363 case XSConstants
.GYEARMONTH_DT
:
364 case XSConstants
.GYEAR_DT
:
365 case XSConstants
.GMONTHDAY_DT
:
366 case XSConstants
.GDAY_DT
:
367 case XSConstants
.GMONTH_DT
:
368 propertyType
= CrAttributeType
.DATE_TIME
;
370 case XSConstants
.HEXBINARY_DT
:
371 case XSConstants
.BASE64BINARY_DT
:
372 case XSConstants
.ANYURI_DT
:
373 propertyType
= CrAttributeType
.ANY_URI
;
375 case XSConstants
.QNAME_DT
:
376 case XSConstants
.NAME_DT
:
377 case XSConstants
.NCNAME_DT
:
378 // TODO support QName?
379 propertyType
= CrAttributeType
.STRING
;
381 case XSConstants
.IDREF_DT
:
382 // TODO support references?
383 propertyType
= CrAttributeType
.STRING
;
385 case XSConstants
.INTEGER_DT
:
386 case XSConstants
.NONPOSITIVEINTEGER_DT
:
387 case XSConstants
.NEGATIVEINTEGER_DT
:
388 case XSConstants
.LONG_DT
:
389 case XSConstants
.INT_DT
:
390 case XSConstants
.SHORT_DT
:
391 case XSConstants
.BYTE_DT
:
392 case XSConstants
.NONNEGATIVEINTEGER_DT
:
393 case XSConstants
.UNSIGNEDLONG_DT
:
394 case XSConstants
.UNSIGNEDINT_DT
:
395 case XSConstants
.UNSIGNEDSHORT_DT
:
396 case XSConstants
.UNSIGNEDBYTE_DT
:
397 case XSConstants
.POSITIVEINTEGER_DT
:
398 propertyType
= CrAttributeType
.LONG
;
400 case XSConstants
.LISTOFUNION_DT
:
401 case XSConstants
.LIST_DT
:
402 case XSConstants
.UNAVAILABLE_DT
:
403 propertyType
= CrAttributeType
.STRING
;
406 propertyType
= CrAttributeType
.STRING
;
412 public DocumentBuilder
newDocumentBuilder() {
414 DocumentBuilder dBuilder
= documentBuilderFactory
.newDocumentBuilder();
415 dBuilder
.setErrorHandler(new ErrorHandler() {
418 public void warning(SAXParseException exception
) throws SAXException
{
423 public void fatalError(SAXParseException exception
) throws SAXException
{
424 log
.error(exception
);
428 public void error(SAXParseException exception
) throws SAXException
{
429 log
.error(exception
);
433 } catch (ParserConfigurationException e
) {
434 throw new IllegalStateException("Cannot create document builder", e
);
439 for (QName type
: types
.keySet()) {
440 Map
<QName
, CrAttributeType
> attrs
= types
.get(type
);
441 System
.out
.println("## " + type
);
442 for (QName attr
: attrs
.keySet()) {
443 System
.out
.println(" " + attr
+ " : " + attrs
.get(attr
));
448 void printTypes(XSModel xsModel
) {
452 // Convert top level complex type definitions to node types
453 log
.debug("\n## TYPES");
454 XSNamedMap map
= xsModel
.getComponents(XSConstants
.TYPE_DEFINITION
);
455 for (int i
= 0; i
< map
.getLength(); i
++) {
456 XSTypeDefinition tDef
= (XSTypeDefinition
) map
.item(i
);
459 // Convert local (anonymous) complex type defs found in top level
460 // element declarations
461 log
.debug("\n## ELEMENTS");
462 map
= xsModel
.getComponents(XSConstants
.ELEMENT_DECLARATION
);
463 for (int i
= 0; i
< map
.getLength(); i
++) {
464 XSElementDeclaration eDec
= (XSElementDeclaration
) map
.item(i
);
465 XSTypeDefinition tDef
= eDec
.getTypeDefinition();
466 log
.debug(eDec
+ ", " + tDef
);
468 log
.debug("\n## ATTRIBUTES");
469 map
= xsModel
.getComponents(XSConstants
.ATTRIBUTE_DECLARATION
);
470 for (int i
= 0; i
< map
.getLength(); i
++) {
471 XSAttributeDeclaration eDec
= (XSAttributeDeclaration
) map
.item(i
);
472 XSTypeDefinition tDef
= eDec
.getTypeDefinition();
473 log
.debug(eDec
.getNamespace() + ":" + eDec
.getName() + ", " + tDef
);
475 } catch (ClassCastException
| XSException e
) {
476 throw new RuntimeException(e
);
481 public void validate(Source source
) throws IOException
{
485 synchronized (this) {
486 validator
= schema
.newValidator();
489 validator
.validate(source
);
490 } catch (SAXException e
) {
491 log
.error(source
+ " is not valid " + e
);
492 // throw new IllegalArgumentException("Provided source is not valid", e);
496 // public Map<String, String> getPrefixes() {
500 // public List<Source> getSources() {
504 public Schema
getSchema() {