1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.student.contract.model.impl;
17
18 import com.sun.xml.xsom.*;
19 import com.sun.xml.xsom.parser.AnnotationContext;
20 import com.sun.xml.xsom.parser.AnnotationParser;
21 import com.sun.xml.xsom.parser.AnnotationParserFactory;
22 import com.sun.xml.xsom.parser.SchemaDocument;
23 import com.sun.xml.xsom.parser.XSOMParser;
24 import com.sun.xml.xsom.util.DomAnnotationParserFactory;
25
26 import org.kuali.student.contract.model.MessageStructure;
27 import org.kuali.student.contract.model.Service;
28 import org.kuali.student.contract.model.ServiceContractModel;
29 import org.kuali.student.contract.model.ServiceMethod;
30 import org.kuali.student.contract.model.ServiceMethodReturnValue;
31 import org.kuali.student.contract.model.XmlType;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34 import org.xml.sax.Attributes;
35 import org.xml.sax.ContentHandler;
36 import org.xml.sax.EntityResolver;
37 import org.xml.sax.ErrorHandler;
38 import org.xml.sax.Locator;
39 import org.xml.sax.SAXException;
40
41 import java.io.File;
42 import java.io.IOException;
43 import java.util.ArrayList;
44 import java.util.LinkedHashMap;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Set;
48
49
50
51
52
53 public class ServiceContractModelPescXsdLoader implements
54 ServiceContractModel {
55
56 private static Logger log = LoggerFactory.getLogger(ServiceContractModelPescXsdLoader.class);
57
58 private List<String> xsdFileNames;
59 private List<Service> services = null;
60 private List<ServiceMethod> serviceMethods = null;
61 private Map<String, XmlType> xmlTypeMap = null;
62 private List<MessageStructure> messageStructures;
63
64 public ServiceContractModelPescXsdLoader(List<String> xsdFileNames) {
65 this.xsdFileNames = xsdFileNames;
66 }
67
68 @Override
69 public List<ServiceMethod> getServiceMethods() {
70 if (this.serviceMethods == null) {
71 this.parse();
72 }
73 return this.serviceMethods;
74 }
75
76 @Override
77 public List<String> getSourceNames() {
78 List<String> list = new ArrayList();
79 list.addAll(this.xsdFileNames);
80 return list;
81 }
82
83 @Override
84 public List<Service> getServices() {
85 if (services == null) {
86 this.parse();
87 }
88 return services;
89 }
90
91 @Override
92 public List<XmlType> getXmlTypes() {
93 if (xmlTypeMap == null) {
94 this.parse();
95 }
96 return new ArrayList(xmlTypeMap.values());
97 }
98
99 @Override
100 public List<MessageStructure> getMessageStructures() {
101 if (messageStructures == null) {
102 this.parse();
103 }
104 return this.messageStructures;
105 }
106
107 private void parse() {
108 log.info("ServiceContractModelQDoxLoader: Starting parse");
109 services = new ArrayList();
110 Service service = new Service();
111 services.add(service);
112 service.setKey("Pesc");
113 service.setName("PescService");
114 service.setComments("Derived from pesc CoreMain");
115 serviceMethods = new ArrayList();
116 ServiceMethod method = new ServiceMethod();
117 serviceMethods.add(method);
118 method.setName("dummy");
119 method.setDescription("Dummy method so validation won't fail");
120 method.setService("Pesc");
121 method.setParameters(new ArrayList());
122 ServiceMethodReturnValue rv = new ServiceMethodReturnValue();
123 rv.setType("void");
124 rv.setDescription("returns nothing");
125 method.setReturnValue(rv);
126 xmlTypeMap = new LinkedHashMap();
127 messageStructures = new ArrayList();
128
129 XSOMParser parser = new XSOMParser();
130 parser.setAnnotationParser(new DomAnnotationParserFactory());
131
132 try {
133 for (String xsdFileName : this.xsdFileNames) {
134
135 parser.parse(new File(xsdFileName));
136 }
137 } catch (SAXException ex) {
138 throw new IllegalArgumentException(ex);
139 } catch (IOException ex) {
140 throw new IllegalArgumentException(ex);
141 }
142 XSSchemaSet schemas;
143 try {
144 schemas = parser.getResult();
145 } catch (SAXException ex) {
146 throw new IllegalArgumentException(ex);
147 }
148
149
150
151 Set<SchemaDocument> schemaDocuments = parser.getDocuments();
152 for (SchemaDocument doc : schemaDocuments) {
153 XSSchema schema = doc.getSchema();
154 this.processSchema(schema);
155 }
156 }
157
158 private void processSchema(XSSchema schema) {
159 for (XSElementDecl element : schema.getElementDecls().values()) {
160 this.addElementDecl(element);
161 }
162 for (XSSimpleType st : schema.getSimpleTypes().values()) {
163
164 addSimpleType(st);
165 }
166 for (XSComplexType ct : schema.getComplexTypes().values()) {
167 if (!shouldInclude(ct)) {
168
169 continue;
170 }
171
172 addComplexType(ct);
173 }
174 }
175
176 private boolean shouldInclude(XSComplexType ct) {
177
178 return true;
179
180
181 }
182
183 private void addSimpleType(XSSimpleType simpleType) {
184 XmlType xmlType = xmlTypeMap.get(simpleType.getName());
185 if (xmlType != null) {
186
187
188 return;
189 }
190 xmlType = new XmlType();
191 xmlTypeMap.put(simpleType.getName(), xmlType);
192 xmlType.setName(simpleType.getName());
193 xmlType.setDesc(calcMissing(calcDesc(simpleType.getAnnotation())));
194 xmlType.setComments("???");
195 xmlType.setExamples("???");
196 xmlType.setService("Pesc");
197 xmlType.setPrimitive("Primitive");
198 }
199
200 private void addComplexType(XSComplexType complexType) {
201 addComplexType(complexType, complexType.getName());
202 }
203
204 private void addElementDecl(XSElementDecl element) {
205 String name = element.getName();
206 XmlType xmlType = xmlTypeMap.get(name);
207 if (xmlType != null) {
208
209 return;
210 }
211
212 xmlType = new XmlType();
213 xmlTypeMap.put(name, xmlType);
214 xmlType.setName(name);
215 xmlType.setDesc(calcMissing(calcDesc(element.getAnnotation())));
216 xmlType.setComments("???");
217 xmlType.setExamples("???");
218 xmlType.setService("Pesc");
219 xmlType.setPrimitive(XmlType.COMPLEX);
220 addMessageStructure(xmlType.getName(), element);
221 }
222
223 private void addComplexType(XSComplexType complexType, String name) {
224 XmlType xmlType = xmlTypeMap.get(name);
225 if (xmlType != null) {
226
227 return;
228 }
229
230 xmlType = new XmlType();
231 xmlTypeMap.put(name, xmlType);
232 xmlType.setName(name);
233 xmlType.setDesc(calcMissing(calcDesc(complexType.getAnnotation())));
234 xmlType.setComments("???");
235 xmlType.setExamples("???");
236 xmlType.setService("Pesc");
237 xmlType.setPrimitive(XmlType.COMPLEX);
238
239 boolean found = false;
240 XSContentType contentType = complexType.getContentType();
241 XSParticle particle = contentType.asParticle();
242 if (particle != null) {
243 XSTerm term = particle.getTerm();
244 if (term.isModelGroup()) {
245 XSModelGroup xsModelGroup = term.asModelGroup();
246 XSParticle[] particles = xsModelGroup.getChildren();
247 for (XSParticle p : particles) {
248 XSTerm pterm = p.getTerm();
249 if (pterm.isElementDecl()) {
250 XSElementDecl element = pterm.asElementDecl();
251 addMessageStructure(xmlType.getName(), element);
252 found = true;
253 }
254 }
255 }
256 }
257
258
259
260 }
261
262 private String calcMissing(String str) {
263 if (str == null) {
264 return "???";
265 }
266 if (str.trim().isEmpty()) {
267 return "???";
268 }
269 return str;
270 }
271
272 private String calcDesc(XSAnnotation annotation) {
273 if (annotation == null) {
274 return null;
275 }
276 if (annotation.getAnnotation() == null) {
277 return null;
278 }
279 return annotation.getAnnotation().toString();
280 }
281
282 private void addMessageStructure(String xmlObject, XSElementDecl element) {
283 MessageStructure ms = new MessageStructure();
284 this.messageStructures.add(ms);
285 ms.setXmlObject(xmlObject);
286 ms.setShortName(element.getName());
287 ms.setName("???");
288 ms.setId(xmlObject + "." + ms.getShortName());
289 ms.setType(calcType(element, xmlObject + "" + ms.getShortName()));
290 ms.setDescription(calcMissing(calcDesc(element.getAnnotation())));
291
292 ms.setRequired(calcRequired(element));
293 ms.setCardinality(calcCardinality(element));
294 }
295
296 private String calcType(XSElementDecl element, String inLinePrefix) {
297 String type = calcType(element.getType(), inLinePrefix);
298 if (type != null) {
299 return type;
300 }
301 return type;
302 }
303
304 private String calcType(XSType xsType, String inLinePrefix) {
305 if (xsType.isSimpleType()) {
306 XSSimpleType st = xsType.asSimpleType();
307 return st.getBaseType().getName();
308
309
310
311
312
313 }
314 String type = xsType.getName();
315 if (type != null) {
316 return type;
317 }
318 if ((xsType.isComplexType())) {
319 XSComplexType ct = xsType.asComplexType();
320 String baseType = ct.getBaseType().getName();
321 if (baseType.equals("anyType")) {
322 baseType = "";
323 }
324 String inlineTypeName = inLinePrefix + baseType;
325 addComplexType(ct, inlineTypeName);
326 return inlineTypeName;
327 }
328 throw new IllegalArgumentException("cannot calculate the type of the field " + inLinePrefix);
329 }
330
331 private String calcRequired(XSElementDecl element) {
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347 return "???";
348 }
349
350 private String calcCardinality(XSElementDecl element) {
351
352
353
354 if (this.getIsRepeated(element)) {
355 return "Many";
356 }
357
358
359
360
361
362
363
364
365
366
367 return null;
368 }
369
370 private boolean getIsRepeated (XSElementDecl element) {
371 XSType type = element.getType();
372 if (type == null) {
373 return false;
374 }
375 if (type.isComplexType()) {
376 XSContentType contentType = type.asComplexType().getContentType();
377 if (contentType == null) {
378 return false;
379 }
380 XSParticle particle = contentType.asParticle();
381 if (particle == null) {
382 return false;
383 }
384 particle.isRepeated();
385 }
386
387 return false;
388 }
389
390 private String getFacetValue(XSElementDecl element, String name) {
391 XSType type = element.getType();
392 if (type == null) {
393 return null;
394 }
395 if (type.isSimpleType()) {
396 XSFacet facet = type.asSimpleType().getFacet(name);
397 if (facet == null) {
398 return null;
399 }
400 return facet.getValue().toString();
401 }
402 if (type.isComplexType()) {
403 XSContentType contentType = type.asComplexType().getContentType();
404 if (contentType == null) {
405 return null;
406 }
407 XSSimpleType simpleType = contentType.asSimpleType();
408 if (simpleType == null) {
409 return null;
410 }
411 XSFacet facet = simpleType.getFacet(name);
412 if (facet == null) {
413 return null;
414 }
415 return facet.getValue().toString();
416 }
417 return null;
418 }
419
420
421
422
423 private static class XsdAnnotationParserFactory implements AnnotationParserFactory {
424
425 @Override
426 public AnnotationParser create() {
427 return new XsdAnnotationParser();
428 }
429 }
430
431
432
433
434 private static class XsdAnnotationParser extends AnnotationParser {
435
436 private StringBuilder documentation = new StringBuilder();
437
438 @Override
439 public ContentHandler getContentHandler(AnnotationContext context,
440 String parentElementName,
441 ErrorHandler handler,
442 EntityResolver resolver) {
443 return new XsdContentHandler(documentation);
444 }
445
446 @Override
447 public Object getResult(Object existing) {
448 return documentation.toString().trim();
449 }
450 }
451
452
453
454
455 private static class XsdContentHandler implements ContentHandler {
456
457 private StringBuilder documentation;
458
459 public XsdContentHandler(StringBuilder documentation) {
460 this.documentation = documentation;
461 }
462 private boolean parsingDocumentation = false;
463
464 @Override
465 public void characters(char[] ch, int start, int length)
466 throws SAXException {
467 if (parsingDocumentation) {
468 documentation.append(ch, start, length);
469 }
470 }
471
472 @Override
473 public void endElement(String uri, String localName, String name)
474 throws SAXException {
475 if (localName.equals("documentation")) {
476 parsingDocumentation = false;
477 }
478 }
479
480 @Override
481 public void startElement(String uri, String localName, String name,
482 Attributes atts) throws SAXException {
483 if (localName.equals("documentation")) {
484 parsingDocumentation = true;
485 }
486 }
487
488 @Override
489 public void endDocument() throws SAXException {
490
491 }
492
493 @Override
494 public void endPrefixMapping(String prefix) throws SAXException {
495
496 }
497
498 @Override
499 public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
500
501 }
502
503 @Override
504 public void processingInstruction(String target, String data) throws SAXException {
505
506 }
507
508 @Override
509 public void setDocumentLocator(Locator locator) {
510
511 }
512
513 @Override
514 public void skippedEntity(String name) throws SAXException {
515
516 }
517
518 @Override
519 public void startDocument() throws SAXException {
520
521 }
522
523 @Override
524 public void startPrefixMapping(String prefix, String uri) throws SAXException {
525
526 }
527 }
528
529 @Override
530 public String toString() {
531 return "ServiceContractModelPescXsdLoader{" +
532 "xsdFileNames=" + xsdFileNames +
533 ", services=" + services +
534 ", serviceMethods=" + serviceMethods +
535 ", xmlTypeMap=" + xmlTypeMap +
536 ", messageStructures=" + messageStructures +
537 '}';
538 }
539 }