View Javadoc

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