001 /* 002 * Copyright 2010 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.osedu.org/licenses/ECL-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.student.contract.model.impl; 017 018 import com.sun.xml.xsom.XSAnnotation; 019 import com.sun.xml.xsom.XSComplexType; 020 import com.sun.xml.xsom.XSContentType; 021 import com.sun.xml.xsom.XSElementDecl; 022 import com.sun.xml.xsom.XSFacet; 023 import com.sun.xml.xsom.XSModelGroup; 024 import com.sun.xml.xsom.XSParticle; 025 import com.sun.xml.xsom.XSSchema; 026 import com.sun.xml.xsom.XSSchemaSet; 027 import com.sun.xml.xsom.XSSimpleType; 028 import com.sun.xml.xsom.XSTerm; 029 import com.sun.xml.xsom.XSType; 030 import com.sun.xml.xsom.parser.AnnotationContext; 031 import com.sun.xml.xsom.parser.AnnotationParser; 032 import com.sun.xml.xsom.parser.AnnotationParserFactory; 033 import com.sun.xml.xsom.parser.SchemaDocument; 034 import com.sun.xml.xsom.parser.XSOMParser; 035 import com.sun.xml.xsom.util.DomAnnotationParserFactory; 036 import java.io.File; 037 import java.io.IOException; 038 import java.util.ArrayList; 039 import java.util.LinkedHashMap; 040 import java.util.List; 041 import java.util.Map; 042 import java.util.Set; 043 import org.kuali.student.contract.model.MessageStructure; 044 import org.kuali.student.contract.model.Service; 045 import org.kuali.student.contract.model.ServiceContractModel; 046 import org.kuali.student.contract.model.ServiceMethod; 047 import org.kuali.student.contract.model.ServiceMethodReturnValue; 048 import org.kuali.student.contract.model.XmlType; 049 import org.xml.sax.Attributes; 050 import org.xml.sax.ContentHandler; 051 import org.xml.sax.EntityResolver; 052 import org.xml.sax.ErrorHandler; 053 import org.xml.sax.Locator; 054 import org.xml.sax.SAXException; 055 056 /** 057 * 058 * @author nwright 059 */ 060 public class ServiceContractModelPescXsdLoader implements 061 ServiceContractModel { 062 063 private List<String> xsdFileNames; 064 private List<Service> services = null; 065 private List<ServiceMethod> serviceMethods = null; 066 private Map<String, XmlType> xmlTypeMap = null; 067 private List<MessageStructure> messageStructures; 068 069 public ServiceContractModelPescXsdLoader(List<String> xsdFileNames) { 070 this.xsdFileNames = xsdFileNames; 071 } 072 073 @Override 074 public List<ServiceMethod> getServiceMethods() { 075 if (this.serviceMethods == null) { 076 this.parse(); 077 } 078 return this.serviceMethods; 079 } 080 081 @Override 082 public List<String> getSourceNames() { 083 List<String> list = new ArrayList(); 084 list.addAll(this.xsdFileNames); 085 return list; 086 } 087 088 @Override 089 public List<Service> getServices() { 090 if (services == null) { 091 this.parse(); 092 } 093 return services; 094 } 095 096 @Override 097 public List<XmlType> getXmlTypes() { 098 if (xmlTypeMap == null) { 099 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 }