]> git.argeo.org Git - lgpl/argeo-commons.git/blob - TypesManager.java
05c7ca638e90884be0a7ba2e5add42c411649704
[lgpl/argeo-commons.git] / TypesManager.java
1 package org.argeo.cms.acr;
2
3 import java.io.IOException;
4 import java.net.URL;
5 import java.util.ArrayList;
6 import java.util.HashMap;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10 import java.util.SortedMap;
11 import java.util.TreeMap;
12
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;
23
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;
52
53 /** Register content types. */
54 class TypesManager {
55 private final static CmsLog log = CmsLog.getLog(TypesManager.class);
56 // private Map<String, String> prefixes = new TreeMap<>();
57
58 // immutable factories
59 private SchemaFactory schemaFactory;
60
61 /** Schema sources. */
62 private List<URL> sources = new ArrayList<>();
63
64 // cached
65 private Schema schema;
66 private DocumentBuilderFactory documentBuilderFactory;
67 // private XSModel xsModel;
68 private SortedMap<QName, Map<QName, CrAttributeType>> types;
69
70 private boolean validating = false;
71 private boolean creatingXsModel = true;
72
73 private final static boolean limited = false;
74
75 public TypesManager() {
76 schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
77
78 // types
79 types = new TreeMap<>(NamespaceUtils.QNAME_COMPARATOR);
80
81 }
82
83 public void init() {
84 registerTypes(CmsContentNamespace.values());
85 }
86
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());
94
95 if (contentNamespace.getSchemaResource() != null) {
96 sources.add(contentNamespace.getSchemaResource());
97 log.debug(() -> "Registered types " + contentNamespace.getNamespaceURI() + " from "
98 + contentNamespace.getSchemaResource().toExternalForm());
99 }
100 }
101 reload();
102 }
103
104 public Set<QName> listTypes() {
105 return types.keySet();
106 }
107
108 public Map<QName, CrAttributeType> getAttributeTypes(QName type) {
109 if (!types.containsKey(type))
110 throw new IllegalArgumentException("Unkown type");
111 return types.get(type);
112 }
113
114 private synchronized void reload() {
115 try {
116 // schema
117 if (validating) {
118 List<StreamSource> sourcesToUse = new ArrayList<>();
119 for (URL sourceUrl : sources) {
120 sourcesToUse.add(new StreamSource(sourceUrl.toExternalForm()));
121 // try {
122 // sourcesToUse.add(new StreamSource(sourceUrl.openStream()));
123 // } catch (IOException e) {
124 // log.error("Cannot open schema source " + sourceUrl);
125 // }
126 }
127 schema = schemaFactory.newSchema(sourcesToUse.toArray(new Source[sourcesToUse.size()]));
128 // for (StreamSource source : sourcesToUse) {
129 // try {
130 // source.getInputStream().close();
131 // } catch (IOException e) {
132 // // TODO Auto-generated catch block
133 // e.printStackTrace();
134 // }
135 // }
136 }
137
138 // document builder factory
139 // we force usage of Xerces for predictability
140 documentBuilderFactory = limited ? DocumentBuilderFactory.newInstance() : new DocumentBuilderFactoryImpl();
141 documentBuilderFactory.setNamespaceAware(true);
142 if (!limited) {
143 documentBuilderFactory.setXIncludeAware(true);
144 if (validating) {
145 documentBuilderFactory.setSchema(getSchema());
146 documentBuilderFactory.setValidating(validating);
147 }
148 }
149
150 if (creatingXsModel) {
151 // XS model
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());
160 }
161 StringList sl = new StringListImpl(systemIds.toArray(new String[systemIds.size()]), systemIds.size());
162 XSModel xsModel = xsLoader.loadURIList(sl);
163
164 // types
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());
169 // types.add(type);
170 // }
171 collectTypes(xsModel);
172
173 log.debug("Created XS model");
174 // printTypes();
175 }
176 } catch (XSException | SAXException e) {
177 throw new IllegalStateException("Cannot reload types", e);
178 }
179 }
180
181 private void collectTypes(XSModel xsModel) {
182 types.clear();
183 // elements
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);
188 }
189
190 // types
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);
195 }
196
197 }
198
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);
205 } else {
206 throw new IllegalArgumentException("Unsupported type " + tDef.getTypeCategory());
207 }
208 }
209 }
210
211 private void collectComplexType(String prefix, QName parent, XSComplexTypeDefinition ctDef) {
212 if (ctDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE) {
213
214 // content with attributes and a string value
215
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);
220 //
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");
225 //
226 // }
227 // }
228
229 } else if (ctDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_ELEMENT
230 || ctDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_MIXED) {
231 XSParticle p = ctDef.getParticle();
232
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");
242 //
243 // }
244 // }
245 // log.debug(prefix + "Empty " + ctDef.getNamespace() + ":" + ctDef.getName() + " - " + attributes);
246 } else {
247 throw new IllegalArgumentException("Unsupported type " + ctDef.getTypeCategory());
248 }
249 }
250
251 private void collectParticle(String prefix, XSParticle particle, boolean multipleFromAbove) {
252 boolean orderable = false;
253
254 XSTerm term = particle.getTerm();
255
256 if (particle.getMaxOccurs() == 0) {
257 return;
258 }
259
260 boolean mandatory = false;
261 if (particle.getMinOccurs() > 0) {
262 mandatory = true;
263 }
264
265 boolean multiple = false;
266 if (particle.getMaxOccurs() > 1 || particle.getMaxOccursUnbounded()) {
267 multiple = true;
268 }
269 if (!multiple && multipleFromAbove)
270 multiple = true;
271
272 if (term.getType() == XSConstants.ELEMENT_DECLARATION) {
273 XSElementDeclaration eDec = (XSElementDeclaration) term;
274
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
280
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;
286
287 if (mg.getCompositor() == XSModelGroup.COMPOSITOR_SEQUENCE) {
288 orderable = true;
289 }
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);
294 }
295 }
296 }
297
298 private void collectElementDeclaration(String prefix, XSElementDeclaration eDec) {
299 QName name = new QName(eDec.getNamespace(), eDec.getName());
300 XSTypeDefinition tDef = eDec.getTypeDefinition();
301
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);
313 } else {
314 if (!types.containsKey(name)) {
315 // System.out.println(prefix + "Element " + name);
316
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));
326 }
327 // REGISTER
328 types.put(name, attributes);
329 if (ctDef != null)
330 collectComplexType(prefix + " ", name, ctDef);
331 }
332 }
333 }
334
335 }
336
337 private CrAttributeType xsToCrType(short kind) {
338 CrAttributeType propertyType;
339 switch (kind) {
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;
350 break;
351 case XSConstants.BOOLEAN_DT:
352 propertyType = CrAttributeType.BOOLEAN;
353 break;
354 case XSConstants.DECIMAL_DT:
355 case XSConstants.FLOAT_DT:
356 case XSConstants.DOUBLE_DT:
357 propertyType = CrAttributeType.DOUBLE;
358 break;
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;
369 break;
370 case XSConstants.HEXBINARY_DT:
371 case XSConstants.BASE64BINARY_DT:
372 case XSConstants.ANYURI_DT:
373 propertyType = CrAttributeType.ANY_URI;
374 break;
375 case XSConstants.QNAME_DT:
376 case XSConstants.NAME_DT:
377 case XSConstants.NCNAME_DT:
378 // TODO support QName?
379 propertyType = CrAttributeType.STRING;
380 break;
381 case XSConstants.IDREF_DT:
382 // TODO support references?
383 propertyType = CrAttributeType.STRING;
384 break;
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;
399 break;
400 case XSConstants.LISTOFUNION_DT:
401 case XSConstants.LIST_DT:
402 case XSConstants.UNAVAILABLE_DT:
403 propertyType = CrAttributeType.STRING;
404 break;
405 default:
406 propertyType = CrAttributeType.STRING;
407 break;
408 }
409 return propertyType;
410 }
411
412 public DocumentBuilder newDocumentBuilder() {
413 try {
414 DocumentBuilder dBuilder = documentBuilderFactory.newDocumentBuilder();
415 dBuilder.setErrorHandler(new ErrorHandler() {
416
417 @Override
418 public void warning(SAXParseException exception) throws SAXException {
419 log.warn(exception);
420 }
421
422 @Override
423 public void fatalError(SAXParseException exception) throws SAXException {
424 log.error(exception);
425 }
426
427 @Override
428 public void error(SAXParseException exception) throws SAXException {
429 log.error(exception);
430 }
431 });
432 return dBuilder;
433 } catch (ParserConfigurationException e) {
434 throw new IllegalStateException("Cannot create document builder", e);
435 }
436 }
437
438 void printTypes() {
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));
444 }
445 }
446 }
447
448 void printTypes(XSModel xsModel) {
449 if (xsModel != null)
450 try {
451
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);
457 log.debug(tDef);
458 }
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);
467 }
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);
474 }
475 } catch (ClassCastException | XSException e) {
476 throw new RuntimeException(e);
477 }
478
479 }
480
481 public void validate(Source source) throws IOException {
482 if (!validating)
483 return;
484 Validator validator;
485 synchronized (this) {
486 validator = schema.newValidator();
487 }
488 try {
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);
493 }
494 }
495
496 // public Map<String, String> getPrefixes() {
497 // return prefixes;
498 // }
499
500 // public List<Source> getSources() {
501 // return sources;
502 // }
503
504 public Schema getSchema() {
505 return schema;
506 }
507
508 }