]> git.argeo.org Git - lgpl/argeo-commons.git/blob - TypesManager.java
6cfb2e5e9cbc2a74736c9a231e488ad99e164ac0
[lgpl/argeo-commons.git] / TypesManager.java
1 package org.argeo.cms.acr;
2
3 import java.io.IOException;
4 import java.util.ArrayList;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Set;
9 import java.util.SortedMap;
10 import java.util.TreeMap;
11
12 import javax.xml.XMLConstants;
13 import javax.xml.namespace.QName;
14 import javax.xml.parsers.DocumentBuilder;
15 import javax.xml.parsers.DocumentBuilderFactory;
16 import javax.xml.parsers.ParserConfigurationException;
17 import javax.xml.transform.Source;
18 import javax.xml.transform.stream.StreamSource;
19 import javax.xml.validation.Schema;
20 import javax.xml.validation.SchemaFactory;
21 import javax.xml.validation.Validator;
22
23 import org.apache.xerces.impl.xs.XSImplementationImpl;
24 import org.apache.xerces.impl.xs.util.StringListImpl;
25 import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl;
26 import org.apache.xerces.xs.StringList;
27 import org.apache.xerces.xs.XSAttributeDeclaration;
28 import org.apache.xerces.xs.XSAttributeUse;
29 import org.apache.xerces.xs.XSComplexTypeDefinition;
30 import org.apache.xerces.xs.XSConstants;
31 import org.apache.xerces.xs.XSElementDeclaration;
32 import org.apache.xerces.xs.XSException;
33 import org.apache.xerces.xs.XSImplementation;
34 import org.apache.xerces.xs.XSLoader;
35 import org.apache.xerces.xs.XSModel;
36 import org.apache.xerces.xs.XSModelGroup;
37 import org.apache.xerces.xs.XSNamedMap;
38 import org.apache.xerces.xs.XSObjectList;
39 import org.apache.xerces.xs.XSParticle;
40 import org.apache.xerces.xs.XSSimpleTypeDefinition;
41 import org.apache.xerces.xs.XSTerm;
42 import org.apache.xerces.xs.XSTypeDefinition;
43 import org.argeo.api.acr.CrAttributeType;
44 import org.argeo.api.acr.NamespaceUtils;
45 import org.argeo.api.acr.RuntimeNamespaceContext;
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;
50
51 /** Register content types. */
52 class TypesManager {
53 private final static CmsLog log = CmsLog.getLog(TypesManager.class);
54 // private Map<String, String> prefixes = new TreeMap<>();
55
56 // immutable factories
57 private SchemaFactory schemaFactory;
58
59 /** Schema sources. */
60 private List<Source> sources = new ArrayList<>();
61
62 // cached
63 private Schema schema;
64 private DocumentBuilderFactory documentBuilderFactory;
65 private XSModel xsModel;
66 private SortedMap<QName, Map<QName, CrAttributeType>> types;
67
68 private boolean validating = true;
69
70 private final static boolean limited = false;
71
72 public TypesManager() {
73 schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
74
75 // types
76 types = new TreeMap<>(NamespaceUtils.QNAME_COMPARATOR);
77
78 }
79
80 public void init() {
81 for (CmsContentTypes cs : CmsContentTypes.values()) {
82 if (cs.getResource() != null) {
83 StreamSource source = new StreamSource(cs.getResource().toExternalForm());
84 sources.add(source);
85 }
86 RuntimeNamespaceContext.register(cs.getNamespace(), cs.getDefaultPrefix());
87 }
88
89 reload();
90 }
91
92 public void registerTypes(String defaultPrefix, String namespace, String xsdSystemId) {
93 // if (prefixes.containsKey(defaultPrefix))
94 // throw new IllegalStateException(
95 // "Prefix " + defaultPrefix + " is already mapped with " + prefixes.get(defaultPrefix));
96 // prefixes.put(defaultPrefix, namespace);
97 RuntimeNamespaceContext.register(namespace, defaultPrefix);
98
99 if (xsdSystemId != null) {
100 sources.add(new StreamSource(xsdSystemId));
101 reload();
102 }
103 }
104
105 public Set<QName> listTypes() {
106 return types.keySet();
107 }
108
109 public Map<QName, CrAttributeType> getAttributeTypes(QName type) {
110 if (!types.containsKey(type))
111 throw new IllegalArgumentException("Unkown type");
112 return types.get(type);
113 }
114
115 private synchronized void reload() {
116 try {
117 // schema
118 schema = schemaFactory.newSchema(sources.toArray(new Source[sources.size()]));
119
120 // document builder factory
121 // we force usage of Xerces for predictability
122 documentBuilderFactory = limited ? DocumentBuilderFactory.newInstance() : new DocumentBuilderFactoryImpl();
123 documentBuilderFactory.setNamespaceAware(true);
124 if (!limited) {
125 documentBuilderFactory.setXIncludeAware(true);
126 documentBuilderFactory.setSchema(getSchema());
127 documentBuilderFactory.setValidating(validating);
128 }
129
130 // XS model
131 // TODO use JVM implementation?
132 // DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
133 // XSImplementation implementation = (XSImplementation) registry.getDOMImplementation("XS-Loader");
134 XSImplementation xsImplementation = new XSImplementationImpl();
135 XSLoader xsLoader = xsImplementation.createXSLoader(null);
136 List<String> systemIds = new ArrayList<>();
137 for (Source source : sources) {
138 systemIds.add(source.getSystemId());
139 }
140 StringList sl = new StringListImpl(systemIds.toArray(new String[systemIds.size()]), systemIds.size());
141 xsModel = xsLoader.loadURIList(sl);
142
143 // types
144 // XSNamedMap map = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);
145 // for (int i = 0; i < map.getLength(); i++) {
146 // XSElementDeclaration eDec = (XSElementDeclaration) map.item(i);
147 // QName type = new QName(eDec.getNamespace(), eDec.getName());
148 // types.add(type);
149 // }
150 collectTypes();
151 } catch (XSException | SAXException e) {
152 throw new IllegalStateException("Cannot reload types", e);
153 }
154 }
155
156 private void collectTypes() {
157 types.clear();
158 // elements
159 XSNamedMap topLevelElements = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);
160 for (int i = 0; i < topLevelElements.getLength(); i++) {
161 XSElementDeclaration eDec = (XSElementDeclaration) topLevelElements.item(i);
162 collectElementDeclaration("", eDec);
163 }
164
165 // types
166 XSNamedMap topLevelTypes = xsModel.getComponents(XSConstants.TYPE_DEFINITION);
167 for (int i = 0; i < topLevelTypes.getLength(); i++) {
168 XSTypeDefinition tDef = (XSTypeDefinition) topLevelTypes.item(i);
169 collectType(tDef, null, null);
170 }
171
172 }
173
174 private void collectType(XSTypeDefinition tDef, String namespace, String nameHint) {
175 if (tDef.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
176 XSComplexTypeDefinition ctDef = (XSComplexTypeDefinition) tDef;
177 if (ctDef.getContentType() != XSComplexTypeDefinition.CONTENTTYPE_SIMPLE
178 || ctDef.getAttributeUses().getLength() > 0 || ctDef.getAttributeWildcard() != null) {
179 collectComplexType("", null, ctDef);
180 } else {
181 throw new IllegalArgumentException("Unsupported type " + tDef.getTypeCategory());
182 }
183 }
184 }
185
186 private void collectComplexType(String prefix, QName parent, XSComplexTypeDefinition ctDef) {
187 if (ctDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE) {
188
189 // content with attributes and a string value
190
191 XSSimpleTypeDefinition stDef = ctDef.getSimpleType();
192 // QName name = new QName(stDef.getNamespace(), stDef.getName());
193 // log.warn(prefix + "Simple " + ctDef + " - " + attributes);
194 // System.err.println(prefix + "Simple from " + parent + " - " + attributes);
195 //
196 // if (parentAttributes != null) {
197 // for (QName attr : attributes.keySet()) {
198 // if (!parentAttributes.containsKey(attr))
199 // System.err.println(prefix + " - " + attr + " not available in parent");
200 //
201 // }
202 // }
203
204 } else if (ctDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_ELEMENT
205 || ctDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_MIXED) {
206 XSParticle p = ctDef.getParticle();
207
208 collectParticle(prefix, p, false);
209 } else if (ctDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_EMPTY) {
210 // Parent only contains attributes
211 // if (parent != null)
212 // System.err.println(prefix + "Empty from " + parent + " - " + attributes);
213 // if (parentAttributes != null) {
214 // for (QName attr : attributes.keySet()) {
215 // if (!parentAttributes.containsKey(attr))
216 // System.err.println(prefix + " - " + attr + " not available in parent");
217 //
218 // }
219 // }
220 // log.debug(prefix + "Empty " + ctDef.getNamespace() + ":" + ctDef.getName() + " - " + attributes);
221 } else {
222 throw new IllegalArgumentException("Unsupported type " + ctDef.getTypeCategory());
223 }
224 }
225
226 private void collectParticle(String prefix, XSParticle particle, boolean multipleFromAbove) {
227 boolean orderable = false;
228
229 XSTerm term = particle.getTerm();
230
231 if (particle.getMaxOccurs() == 0) {
232 return;
233 }
234
235 boolean mandatory = false;
236 if (particle.getMinOccurs() > 0) {
237 mandatory = true;
238 }
239
240 boolean multiple = false;
241 if (particle.getMaxOccurs() > 1 || particle.getMaxOccursUnbounded()) {
242 multiple = true;
243 }
244 if (!multiple && multipleFromAbove)
245 multiple = true;
246
247 if (term.getType() == XSConstants.ELEMENT_DECLARATION) {
248 XSElementDeclaration eDec = (XSElementDeclaration) term;
249
250 collectElementDeclaration(prefix, eDec);
251 // If this particle is a wildcard (an <xs:any> )then it
252 // is converted into a node def.
253 } else if (term.getType() == XSConstants.WILDCARD) {
254 // TODO can be anything
255
256 // If this particle is a model group (one of
257 // <xs:sequence>, <xs:choice> or <xs:all>) then
258 // it subparticles must be processed.
259 } else if (term.getType() == XSConstants.MODEL_GROUP) {
260 XSModelGroup mg = (XSModelGroup) term;
261
262 if (mg.getCompositor() == XSModelGroup.COMPOSITOR_SEQUENCE) {
263 orderable = true;
264 }
265 XSObjectList list = mg.getParticles();
266 for (int i = 0; i < list.getLength(); i++) {
267 XSParticle pp = (XSParticle) list.item(i);
268 collectParticle(prefix + " ", pp, multiple);
269 }
270 }
271 }
272
273 private void collectElementDeclaration(String prefix, XSElementDeclaration eDec) {
274 QName name = new QName(eDec.getNamespace(), eDec.getName());
275 XSTypeDefinition tDef = eDec.getTypeDefinition();
276
277 XSComplexTypeDefinition ctDef = null;
278 Map<QName, CrAttributeType> attributes = new HashMap<>();
279 if (tDef.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) {
280 XSSimpleTypeDefinition stDef = (XSSimpleTypeDefinition) tDef;
281 // System.err.println(prefix + "Simple element " + name);
282 } else if (tDef.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
283 ctDef = (XSComplexTypeDefinition) tDef;
284 if (ctDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE
285 && ctDef.getAttributeUses().getLength() == 0 && ctDef.getAttributeWildcard() == null) {
286 XSSimpleTypeDefinition stDef = ctDef.getSimpleType();
287 // System.err.println(prefix + "Simplified element " + name);
288 } else {
289 if (!types.containsKey(name)) {
290 // System.out.println(prefix + "Element " + name);
291
292 XSObjectList list = ctDef.getAttributeUses();
293 for (int i = 0; i < list.getLength(); i++) {
294 XSAttributeUse au = (XSAttributeUse) list.item(i);
295 XSAttributeDeclaration ad = au.getAttrDeclaration();
296 QName attrName = new QName(ad.getNamespace(), ad.getName());
297 // Get the simple type def for this attribute
298 XSSimpleTypeDefinition std = ad.getTypeDefinition();
299 attributes.put(attrName, xsToCrType(std.getBuiltInKind()));
300 // System.out.println(prefix + " - " + attrName + " = " + attributes.get(attrName));
301 }
302 // REGISTER
303 types.put(name, attributes);
304 if (ctDef != null)
305 collectComplexType(prefix + " ", name, ctDef);
306 }
307 }
308 }
309
310 }
311
312 private CrAttributeType xsToCrType(short kind) {
313 CrAttributeType propertyType;
314 switch (kind) {
315 case XSConstants.ANYSIMPLETYPE_DT:
316 case XSConstants.STRING_DT:
317 case XSConstants.ID_DT:
318 case XSConstants.ENTITY_DT:
319 case XSConstants.NOTATION_DT:
320 case XSConstants.NORMALIZEDSTRING_DT:
321 case XSConstants.TOKEN_DT:
322 case XSConstants.LANGUAGE_DT:
323 case XSConstants.NMTOKEN_DT:
324 propertyType = CrAttributeType.STRING;
325 break;
326 case XSConstants.BOOLEAN_DT:
327 propertyType = CrAttributeType.BOOLEAN;
328 break;
329 case XSConstants.DECIMAL_DT:
330 case XSConstants.FLOAT_DT:
331 case XSConstants.DOUBLE_DT:
332 propertyType = CrAttributeType.DOUBLE;
333 break;
334 case XSConstants.DURATION_DT:
335 case XSConstants.DATETIME_DT:
336 case XSConstants.TIME_DT:
337 case XSConstants.DATE_DT:
338 case XSConstants.GYEARMONTH_DT:
339 case XSConstants.GYEAR_DT:
340 case XSConstants.GMONTHDAY_DT:
341 case XSConstants.GDAY_DT:
342 case XSConstants.GMONTH_DT:
343 propertyType = CrAttributeType.DATE_TIME;
344 break;
345 case XSConstants.HEXBINARY_DT:
346 case XSConstants.BASE64BINARY_DT:
347 case XSConstants.ANYURI_DT:
348 propertyType = CrAttributeType.ANY_URI;
349 break;
350 case XSConstants.QNAME_DT:
351 case XSConstants.NAME_DT:
352 case XSConstants.NCNAME_DT:
353 // TODO support QName?
354 propertyType = CrAttributeType.STRING;
355 break;
356 case XSConstants.IDREF_DT:
357 // TODO support references?
358 propertyType = CrAttributeType.STRING;
359 break;
360 case XSConstants.INTEGER_DT:
361 case XSConstants.NONPOSITIVEINTEGER_DT:
362 case XSConstants.NEGATIVEINTEGER_DT:
363 case XSConstants.LONG_DT:
364 case XSConstants.INT_DT:
365 case XSConstants.SHORT_DT:
366 case XSConstants.BYTE_DT:
367 case XSConstants.NONNEGATIVEINTEGER_DT:
368 case XSConstants.UNSIGNEDLONG_DT:
369 case XSConstants.UNSIGNEDINT_DT:
370 case XSConstants.UNSIGNEDSHORT_DT:
371 case XSConstants.UNSIGNEDBYTE_DT:
372 case XSConstants.POSITIVEINTEGER_DT:
373 propertyType = CrAttributeType.LONG;
374 break;
375 case XSConstants.LISTOFUNION_DT:
376 case XSConstants.LIST_DT:
377 case XSConstants.UNAVAILABLE_DT:
378 propertyType = CrAttributeType.STRING;
379 break;
380 default:
381 propertyType = CrAttributeType.STRING;
382 break;
383 }
384 return propertyType;
385 }
386
387 public DocumentBuilder newDocumentBuilder() {
388 try {
389 DocumentBuilder dBuilder = documentBuilderFactory.newDocumentBuilder();
390 dBuilder.setErrorHandler(new ErrorHandler() {
391
392 @Override
393 public void warning(SAXParseException exception) throws SAXException {
394 log.warn(exception);
395 }
396
397 @Override
398 public void fatalError(SAXParseException exception) throws SAXException {
399 log.error(exception);
400 }
401
402 @Override
403 public void error(SAXParseException exception) throws SAXException {
404 log.error(exception);
405 }
406 });
407 return dBuilder;
408 } catch (ParserConfigurationException e) {
409 throw new IllegalStateException("Cannot create document builder", e);
410 }
411 }
412
413 public void printTypes() {
414 try {
415
416 // Convert top level complex type definitions to node types
417 log.debug("\n## TYPES");
418 XSNamedMap map = xsModel.getComponents(XSConstants.TYPE_DEFINITION);
419 for (int i = 0; i < map.getLength(); i++) {
420 XSTypeDefinition tDef = (XSTypeDefinition) map.item(i);
421 log.debug(tDef);
422 }
423 // Convert local (anonymous) complex type defs found in top level
424 // element declarations
425 log.debug("\n## ELEMENTS");
426 map = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);
427 for (int i = 0; i < map.getLength(); i++) {
428 XSElementDeclaration eDec = (XSElementDeclaration) map.item(i);
429 XSTypeDefinition tDef = eDec.getTypeDefinition();
430 log.debug(eDec + ", " + tDef);
431 }
432 log.debug("\n## ATTRIBUTES");
433 map = xsModel.getComponents(XSConstants.ATTRIBUTE_DECLARATION);
434 for (int i = 0; i < map.getLength(); i++) {
435 XSAttributeDeclaration eDec = (XSAttributeDeclaration) map.item(i);
436 XSTypeDefinition tDef = eDec.getTypeDefinition();
437 log.debug(eDec.getNamespace() + ":" + eDec.getName() + ", " + tDef);
438 }
439 } catch (ClassCastException | XSException e) {
440 throw new RuntimeException(e);
441 }
442
443 }
444
445 public void validate(Source source) throws IOException {
446 if (!validating)
447 return;
448 Validator validator;
449 synchronized (this) {
450 validator = schema.newValidator();
451 }
452 try {
453 validator.validate(source);
454 } catch (SAXException e) {
455 throw new IllegalArgumentException("Provided source is not valid", e);
456 }
457 }
458
459 // public Map<String, String> getPrefixes() {
460 // return prefixes;
461 // }
462
463 public List<Source> getSources() {
464 return sources;
465 }
466
467 public Schema getSchema() {
468 return schema;
469 }
470
471 }