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
= false;
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 schema
= schemaFactory
.newSchema(sourcesToUse
.toArray(new Source
[sourcesToUse
.size()]));
123 // for (StreamSource source : sourcesToUse) {
125 // source.getInputStream().close();
126 // } catch (IOException e) {
127 // // TODO Auto-generated catch block
128 // e.printStackTrace();
133 // document builder factory
134 // we force usage of Xerces for predictability
135 documentBuilderFactory
= limited ? DocumentBuilderFactory
.newInstance() : new DocumentBuilderFactoryImpl();
136 documentBuilderFactory
.setNamespaceAware(true);
138 documentBuilderFactory
.setXIncludeAware(true);
140 documentBuilderFactory
.setSchema(getSchema());
141 documentBuilderFactory
.setValidating(validating
);
145 if (creatingXsModel
) {
147 // TODO use JVM implementation?
148 // DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
149 // XSImplementation implementation = (XSImplementation) registry.getDOMImplementation("XS-Loader");
150 XSImplementation xsImplementation
= new XSImplementationImpl();
151 XSLoader xsLoader
= xsImplementation
.createXSLoader(null);
152 List
<String
> systemIds
= new ArrayList
<>();
153 for (URL sourceUrl
: sources
) {
154 systemIds
.add(sourceUrl
.toExternalForm());
156 StringList sl
= new StringListImpl(systemIds
.toArray(new String
[systemIds
.size()]), systemIds
.size());
157 xsModel
= xsLoader
.loadURIList(sl
);
160 // XSNamedMap map = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);
161 // for (int i = 0; i < map.getLength(); i++) {
162 // XSElementDeclaration eDec = (XSElementDeclaration) map.item(i);
163 // QName type = new QName(eDec.getNamespace(), eDec.getName());
168 log
.debug("Created XS model");
170 } catch (XSException
| SAXException e
) {
171 throw new IllegalStateException("Cannot reload types", e
);
175 private void collectTypes() {
178 XSNamedMap topLevelElements
= xsModel
.getComponents(XSConstants
.ELEMENT_DECLARATION
);
179 for (int i
= 0; i
< topLevelElements
.getLength(); i
++) {
180 XSElementDeclaration eDec
= (XSElementDeclaration
) topLevelElements
.item(i
);
181 collectElementDeclaration("", eDec
);
185 XSNamedMap topLevelTypes
= xsModel
.getComponents(XSConstants
.TYPE_DEFINITION
);
186 for (int i
= 0; i
< topLevelTypes
.getLength(); i
++) {
187 XSTypeDefinition tDef
= (XSTypeDefinition
) topLevelTypes
.item(i
);
188 collectType(tDef
, null, null);
193 private void collectType(XSTypeDefinition tDef
, String namespace
, String nameHint
) {
194 if (tDef
.getTypeCategory() == XSTypeDefinition
.COMPLEX_TYPE
) {
195 XSComplexTypeDefinition ctDef
= (XSComplexTypeDefinition
) tDef
;
196 if (ctDef
.getContentType() != XSComplexTypeDefinition
.CONTENTTYPE_SIMPLE
197 || ctDef
.getAttributeUses().getLength() > 0 || ctDef
.getAttributeWildcard() != null) {
198 collectComplexType("", null, ctDef
);
200 throw new IllegalArgumentException("Unsupported type " + tDef
.getTypeCategory());
205 private void collectComplexType(String prefix
, QName parent
, XSComplexTypeDefinition ctDef
) {
206 if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_SIMPLE
) {
208 // content with attributes and a string value
210 XSSimpleTypeDefinition stDef
= ctDef
.getSimpleType();
211 // QName name = new QName(stDef.getNamespace(), stDef.getName());
212 // log.warn(prefix + "Simple " + ctDef + " - " + attributes);
213 // System.err.println(prefix + "Simple from " + parent + " - " + attributes);
215 // if (parentAttributes != null) {
216 // for (QName attr : attributes.keySet()) {
217 // if (!parentAttributes.containsKey(attr))
218 // System.err.println(prefix + " - " + attr + " not available in parent");
223 } else if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_ELEMENT
224 || ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_MIXED
) {
225 XSParticle p
= ctDef
.getParticle();
227 collectParticle(prefix
, p
, false);
228 } else if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_EMPTY
) {
229 // Parent only contains attributes
230 // if (parent != null)
231 // System.err.println(prefix + "Empty from " + parent + " - " + attributes);
232 // if (parentAttributes != null) {
233 // for (QName attr : attributes.keySet()) {
234 // if (!parentAttributes.containsKey(attr))
235 // System.err.println(prefix + " - " + attr + " not available in parent");
239 // log.debug(prefix + "Empty " + ctDef.getNamespace() + ":" + ctDef.getName() + " - " + attributes);
241 throw new IllegalArgumentException("Unsupported type " + ctDef
.getTypeCategory());
245 private void collectParticle(String prefix
, XSParticle particle
, boolean multipleFromAbove
) {
246 boolean orderable
= false;
248 XSTerm term
= particle
.getTerm();
250 if (particle
.getMaxOccurs() == 0) {
254 boolean mandatory
= false;
255 if (particle
.getMinOccurs() > 0) {
259 boolean multiple
= false;
260 if (particle
.getMaxOccurs() > 1 || particle
.getMaxOccursUnbounded()) {
263 if (!multiple
&& multipleFromAbove
)
266 if (term
.getType() == XSConstants
.ELEMENT_DECLARATION
) {
267 XSElementDeclaration eDec
= (XSElementDeclaration
) term
;
269 collectElementDeclaration(prefix
, eDec
);
270 // If this particle is a wildcard (an <xs:any> )then it
271 // is converted into a node def.
272 } else if (term
.getType() == XSConstants
.WILDCARD
) {
273 // TODO can be anything
275 // If this particle is a model group (one of
276 // <xs:sequence>, <xs:choice> or <xs:all>) then
277 // it subparticles must be processed.
278 } else if (term
.getType() == XSConstants
.MODEL_GROUP
) {
279 XSModelGroup mg
= (XSModelGroup
) term
;
281 if (mg
.getCompositor() == XSModelGroup
.COMPOSITOR_SEQUENCE
) {
284 XSObjectList list
= mg
.getParticles();
285 for (int i
= 0; i
< list
.getLength(); i
++) {
286 XSParticle pp
= (XSParticle
) list
.item(i
);
287 collectParticle(prefix
+ " ", pp
, multiple
);
292 private void collectElementDeclaration(String prefix
, XSElementDeclaration eDec
) {
293 QName name
= new QName(eDec
.getNamespace(), eDec
.getName());
294 XSTypeDefinition tDef
= eDec
.getTypeDefinition();
296 XSComplexTypeDefinition ctDef
= null;
297 Map
<QName
, CrAttributeType
> attributes
= new HashMap
<>();
298 if (tDef
.getTypeCategory() == XSTypeDefinition
.SIMPLE_TYPE
) {
299 XSSimpleTypeDefinition stDef
= (XSSimpleTypeDefinition
) tDef
;
300 // System.err.println(prefix + "Simple element " + name);
301 } else if (tDef
.getTypeCategory() == XSTypeDefinition
.COMPLEX_TYPE
) {
302 ctDef
= (XSComplexTypeDefinition
) tDef
;
303 if (ctDef
.getContentType() == XSComplexTypeDefinition
.CONTENTTYPE_SIMPLE
304 && ctDef
.getAttributeUses().getLength() == 0 && ctDef
.getAttributeWildcard() == null) {
305 XSSimpleTypeDefinition stDef
= ctDef
.getSimpleType();
306 // System.err.println(prefix + "Simplified element " + name);
308 if (!types
.containsKey(name
)) {
309 // System.out.println(prefix + "Element " + name);
311 XSObjectList list
= ctDef
.getAttributeUses();
312 for (int i
= 0; i
< list
.getLength(); i
++) {
313 XSAttributeUse au
= (XSAttributeUse
) list
.item(i
);
314 XSAttributeDeclaration ad
= au
.getAttrDeclaration();
315 QName attrName
= new QName(ad
.getNamespace(), ad
.getName());
316 // Get the simple type def for this attribute
317 XSSimpleTypeDefinition std
= ad
.getTypeDefinition();
318 attributes
.put(attrName
, xsToCrType(std
.getBuiltInKind()));
319 // System.out.println(prefix + " - " + attrName + " = " + attributes.get(attrName));
322 types
.put(name
, attributes
);
324 collectComplexType(prefix
+ " ", name
, ctDef
);
331 private CrAttributeType
xsToCrType(short kind
) {
332 CrAttributeType propertyType
;
334 case XSConstants
.ANYSIMPLETYPE_DT
:
335 case XSConstants
.STRING_DT
:
336 case XSConstants
.ID_DT
:
337 case XSConstants
.ENTITY_DT
:
338 case XSConstants
.NOTATION_DT
:
339 case XSConstants
.NORMALIZEDSTRING_DT
:
340 case XSConstants
.TOKEN_DT
:
341 case XSConstants
.LANGUAGE_DT
:
342 case XSConstants
.NMTOKEN_DT
:
343 propertyType
= CrAttributeType
.STRING
;
345 case XSConstants
.BOOLEAN_DT
:
346 propertyType
= CrAttributeType
.BOOLEAN
;
348 case XSConstants
.DECIMAL_DT
:
349 case XSConstants
.FLOAT_DT
:
350 case XSConstants
.DOUBLE_DT
:
351 propertyType
= CrAttributeType
.DOUBLE
;
353 case XSConstants
.DURATION_DT
:
354 case XSConstants
.DATETIME_DT
:
355 case XSConstants
.TIME_DT
:
356 case XSConstants
.DATE_DT
:
357 case XSConstants
.GYEARMONTH_DT
:
358 case XSConstants
.GYEAR_DT
:
359 case XSConstants
.GMONTHDAY_DT
:
360 case XSConstants
.GDAY_DT
:
361 case XSConstants
.GMONTH_DT
:
362 propertyType
= CrAttributeType
.DATE_TIME
;
364 case XSConstants
.HEXBINARY_DT
:
365 case XSConstants
.BASE64BINARY_DT
:
366 case XSConstants
.ANYURI_DT
:
367 propertyType
= CrAttributeType
.ANY_URI
;
369 case XSConstants
.QNAME_DT
:
370 case XSConstants
.NAME_DT
:
371 case XSConstants
.NCNAME_DT
:
372 // TODO support QName?
373 propertyType
= CrAttributeType
.STRING
;
375 case XSConstants
.IDREF_DT
:
376 // TODO support references?
377 propertyType
= CrAttributeType
.STRING
;
379 case XSConstants
.INTEGER_DT
:
380 case XSConstants
.NONPOSITIVEINTEGER_DT
:
381 case XSConstants
.NEGATIVEINTEGER_DT
:
382 case XSConstants
.LONG_DT
:
383 case XSConstants
.INT_DT
:
384 case XSConstants
.SHORT_DT
:
385 case XSConstants
.BYTE_DT
:
386 case XSConstants
.NONNEGATIVEINTEGER_DT
:
387 case XSConstants
.UNSIGNEDLONG_DT
:
388 case XSConstants
.UNSIGNEDINT_DT
:
389 case XSConstants
.UNSIGNEDSHORT_DT
:
390 case XSConstants
.UNSIGNEDBYTE_DT
:
391 case XSConstants
.POSITIVEINTEGER_DT
:
392 propertyType
= CrAttributeType
.LONG
;
394 case XSConstants
.LISTOFUNION_DT
:
395 case XSConstants
.LIST_DT
:
396 case XSConstants
.UNAVAILABLE_DT
:
397 propertyType
= CrAttributeType
.STRING
;
400 propertyType
= CrAttributeType
.STRING
;
406 public DocumentBuilder
newDocumentBuilder() {
408 DocumentBuilder dBuilder
= documentBuilderFactory
.newDocumentBuilder();
409 dBuilder
.setErrorHandler(new ErrorHandler() {
412 public void warning(SAXParseException exception
) throws SAXException
{
417 public void fatalError(SAXParseException exception
) throws SAXException
{
418 log
.error(exception
);
422 public void error(SAXParseException exception
) throws SAXException
{
423 log
.error(exception
);
427 } catch (ParserConfigurationException e
) {
428 throw new IllegalStateException("Cannot create document builder", e
);
432 public void printTypes() {
436 // Convert top level complex type definitions to node types
437 log
.debug("\n## TYPES");
438 XSNamedMap map
= xsModel
.getComponents(XSConstants
.TYPE_DEFINITION
);
439 for (int i
= 0; i
< map
.getLength(); i
++) {
440 XSTypeDefinition tDef
= (XSTypeDefinition
) map
.item(i
);
443 // Convert local (anonymous) complex type defs found in top level
444 // element declarations
445 log
.debug("\n## ELEMENTS");
446 map
= xsModel
.getComponents(XSConstants
.ELEMENT_DECLARATION
);
447 for (int i
= 0; i
< map
.getLength(); i
++) {
448 XSElementDeclaration eDec
= (XSElementDeclaration
) map
.item(i
);
449 XSTypeDefinition tDef
= eDec
.getTypeDefinition();
450 log
.debug(eDec
+ ", " + tDef
);
452 log
.debug("\n## ATTRIBUTES");
453 map
= xsModel
.getComponents(XSConstants
.ATTRIBUTE_DECLARATION
);
454 for (int i
= 0; i
< map
.getLength(); i
++) {
455 XSAttributeDeclaration eDec
= (XSAttributeDeclaration
) map
.item(i
);
456 XSTypeDefinition tDef
= eDec
.getTypeDefinition();
457 log
.debug(eDec
.getNamespace() + ":" + eDec
.getName() + ", " + tDef
);
459 } catch (ClassCastException
| XSException e
) {
460 throw new RuntimeException(e
);
465 public void validate(Source source
) throws IOException
{
469 synchronized (this) {
470 validator
= schema
.newValidator();
473 validator
.validate(source
);
474 } catch (SAXException e
) {
475 log
.error(source
+ " is not valid " + e
);
476 // throw new IllegalArgumentException("Provided source is not valid", e);
480 // public Map<String, String> getPrefixes() {
484 // public List<Source> getSources() {
488 public Schema
getSchema() {