| 1 28 29 package org.jibx.binding; 30 31 import java.io.File ; 32 import java.io.FileInputStream ; 33 import java.io.FileNotFoundException ; 34 import java.io.FileOutputStream ; 35 import java.io.IOException ; 36 import java.net.MalformedURLException ; 37 import java.net.URL ; 38 import java.util.ArrayList ; 39 import java.util.HashMap ; 40 import java.util.Iterator ; 41 42 import javax.xml.parsers.DocumentBuilderFactory ; 43 import javax.xml.parsers.FactoryConfigurationError ; 44 import javax.xml.parsers.ParserConfigurationException ; 45 import javax.xml.transform.Result ; 46 import javax.xml.transform.Transformer ; 47 import javax.xml.transform.TransformerConfigurationException ; 48 import javax.xml.transform.TransformerException ; 49 import javax.xml.transform.TransformerFactory ; 50 import javax.xml.transform.TransformerFactoryConfigurationError ; 51 import javax.xml.transform.dom.DOMSource ; 52 import javax.xml.transform.stream.StreamResult ; 53 54 import org.jibx.binding.classes.ClassCache; 55 import org.jibx.binding.classes.ClassFile; 56 import org.jibx.binding.model.BindingElement; 57 import org.jibx.binding.model.ClassWrapper; 58 import org.jibx.binding.model.CollectionElement; 59 import org.jibx.binding.model.ContainerElementBase; 60 import org.jibx.binding.model.DefinitionContext; 61 import org.jibx.binding.model.ElementBase; 62 import org.jibx.binding.model.IClass; 63 import org.jibx.binding.model.IClassLocator; 64 import org.jibx.binding.model.MappingElement; 65 import org.jibx.binding.model.NestingAttributes; 66 import org.jibx.binding.model.NestingElementBase; 67 import org.jibx.binding.model.StructureElement; 68 import org.jibx.binding.model.StructureElementBase; 69 import org.jibx.binding.model.TemplateElementBase; 70 import org.jibx.binding.model.ValidationContext; 71 import org.jibx.binding.model.ValidationProblem; 72 import org.jibx.binding.model.ValueElement; 73 import org.jibx.binding.util.ObjectStack; 74 import org.jibx.runtime.JiBXException; 75 import org.jibx.runtime.ValidationException; 76 import org.w3c.dom.Document ; 77 import org.w3c.dom.Element ; 78 79 86 87 public class SchemaGenerator 88 { 89 90 private static String CURRENT_VERSION = "0.2"; 91 92 93 private static final String XSD_URI = "http://www.w3.org/2001/XMLSchema"; 94 95 96 public static final String XML_URI = "http://www.w3.org/XML/1998/namespace"; 97 98 99 public static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/"; 100 101 102 private static HashMap s_objectTypeMap = new HashMap (); 103 104 static { 105 s_objectTypeMap.put("java.lang.Boolean", "xsd:boolean"); 106 s_objectTypeMap.put("java.lang.Byte", "xsd:byte"); 107 s_objectTypeMap.put("java.lang.Char", "xsd:unsignedInt"); 108 s_objectTypeMap.put("java.lang.Double", "xsd:double"); 109 s_objectTypeMap.put("java.lang.Float", "xsd:float"); 110 s_objectTypeMap.put("java.lang.Integer", "xsd:int"); 111 s_objectTypeMap.put("java.lang.Long", "xsd:long"); 112 s_objectTypeMap.put("java.lang.Short", "xsd:short"); 113 s_objectTypeMap.put("java.math.BigDecimal", "xsd:decimal"); 114 s_objectTypeMap.put("java.math.BigInteger", "xsd:integer"); 115 s_objectTypeMap.put("java.sql.Date", "xsd:date"); 116 s_objectTypeMap.put("java.sql.Time", "xsd:time"); 117 s_objectTypeMap.put("java.sql.Timestamp", "xsd:dateTime"); 118 s_objectTypeMap.put("java.util.Date", "xsd:dateTime"); 119 s_objectTypeMap.put("byte[]", "xsd:base64"); 120 } 121 122 123 private static HashMap s_primitiveTypeMap = new HashMap (); 124 125 static { 126 s_primitiveTypeMap.put("boolean", "xsd:boolean"); 127 s_primitiveTypeMap.put("byte", "xsd:byte"); 128 s_primitiveTypeMap.put("char", "xsd:unsignedInt"); 129 s_primitiveTypeMap.put("double", "xsd:double"); 130 s_primitiveTypeMap.put("float", "xsd:float"); 131 s_primitiveTypeMap.put("int", "xsd:int"); 132 s_primitiveTypeMap.put("long", "xsd:long"); 133 s_primitiveTypeMap.put("short", "xsd:short"); 134 } 135 136 137 private boolean m_verbose; 138 139 140 private boolean m_isElementQualified; 141 142 143 private boolean m_isAttributeQualified; 144 145 146 private String m_indentSequence; 147 148 149 private HashMap m_schemaMap; 150 151 152 private IClassLocator m_classLocator; 153 154 155 private Document m_document; 156 157 158 private ObjectStack m_structureStack; 159 160 166 public SchemaGenerator(ArrayList paths) { 167 m_structureStack = new ObjectStack(); 168 m_schemaMap = new HashMap (); 169 170 String [] parray = (String [])paths.toArray(new String [paths.size()]); 172 ClassCache.setPaths(parray); 173 ClassFile.setPaths(parray); 174 175 m_classLocator = new IClassLocator() { 177 public IClass getClassInfo(String name) { 178 try { 179 return new ClassWrapper(ClassCache.getClassFile(name)); 180 } catch (JiBXException e) { 181 throw new IllegalStateException ("Class not found " + name); 182 } 183 } 184 }; 185 try { 186 187 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 189 dbf.setNamespaceAware(true); 190 m_document = dbf.newDocumentBuilder().newDocument(); 191 192 } catch (ParserConfigurationException e) { 193 throw new IllegalStateException ("Parser configuration error " + 194 e.getMessage()); 195 } catch (FactoryConfigurationError e) { 196 throw new IllegalStateException ("Factory configuration error " + 197 e.getMessage()); 198 } 199 } 200 201 209 public SchemaGenerator(boolean verbose, boolean equal, boolean aqual, 210 ArrayList paths) { 211 this(paths); 212 m_verbose = verbose; 213 m_isElementQualified = equal; 214 m_isAttributeQualified = aqual; 215 m_indentSequence = " "; 216 } 217 218 223 public void setVerbose(boolean verbose) { 224 m_verbose = verbose; 225 } 226 227 232 public void setElementQualified(boolean qual) { 233 m_isElementQualified = qual; 234 } 235 236 241 public void setAttributeQualified(boolean qual) { 242 m_isAttributeQualified = qual; 243 } 244 245 250 public Element[] getSchemas() { 251 Element[] schemas = new Element[m_schemaMap.size()]; 252 int fill = 0; 253 for (Iterator iter = m_schemaMap.values().iterator(); iter.hasNext();) { 254 schemas[fill++] = (Element)iter.next(); 255 } 256 return schemas; 257 } 258 259 268 private void indentForClose(Element parent) { 269 StringBuffer buff = new StringBuffer (20); 270 buff.append('\n'); 271 Element ancestor = parent; 272 boolean count = false; 273 while (ancestor != null) { 274 if (count) { 275 buff.append(m_indentSequence); 276 } 277 ancestor = (Element)ancestor.getParentNode(); 278 count = true; 279 } 280 parent.appendChild(m_document.createTextNode(buff.toString())); 281 } 282 283 289 private void addComment(Element parent, String text) { 290 if (parent.getChildNodes().getLength() == 0) { 291 indentForClose(parent); 292 } 293 parent.appendChild(m_document.createTextNode(m_indentSequence)); 294 parent.appendChild(m_document.createComment(text)); 295 indentForClose(parent); 296 } 297 298 306 private Element addChildElement(Element parent, String name) { 307 if (parent.getChildNodes().getLength() == 0) { 308 indentForClose(parent); 309 } 310 parent.appendChild(m_document.createTextNode(m_indentSequence)); 311 Element element = m_document.createElementNS(XSD_URI, name); 312 parent.appendChild(element); 313 indentForClose(parent); 314 return element; 315 } 316 317 322 public DefinitionContext getDefinitions() { 323 int index = 0; 324 while (index < m_structureStack.size()) { 325 NestingElementBase nest = 326 (NestingElementBase)m_structureStack.peek(index++); 327 if (nest.getDefinitions() != null) { 328 return nest.getDefinitions(); 329 } 330 } 331 throw new IllegalStateException  332 ("Internal error: no definition context"); 333 } 334 335 344 private void defineEmptyStructureComponent(StructureElementBase comp, 345 Element egroup, Element agroup) { 346 NestingElementBase parent = 347 (NestingElementBase)m_structureStack.peek(0); 348 boolean only = parent.children().size() == 1; 349 if (comp.type() == ElementBase.COLLECTION_ELEMENT) { 350 351 CollectionElement collection = (CollectionElement)comp; 353 String itype = collection.getItemTypeClass().getName(); 354 DefinitionContext dctx = getDefinitions(); 355 TemplateElementBase templ = dctx.getSpecificTemplate(itype); 356 Element element = null; 357 if (! (templ instanceof MappingElement)) { 358 if (only) { 359 addComment(egroup, " Replace \"any\" with details of " + 360 "content to complete schema "); 361 element = addChildElement(egroup, "any"); 362 } else { 363 addComment(egroup, 364 " No mapping for items of collection at " + 365 ValidationException.describe(collection) + " "); 366 addComment(egroup, 367 " Fill in details of content to complete schema "); 368 } 369 } else { 370 element = addChildElement(egroup, "element"); 371 element.setAttribute("ref", "tns:" + 372 ((MappingElement)templ).getName()); 373 } 374 if (element != null) { 375 element.setAttribute("minOccurs", "0"); 376 element.setAttribute("maxOccurs", "unbounded"); 377 } 378 379 } else { 380 381 StructureElement structure = (StructureElement)comp; 383 TemplateElementBase templ = structure.getMapAsMapping(); 384 if (! (templ instanceof MappingElement)) { 385 386 if (only) { 388 addComment(egroup, " Replace \"any\" with details of " + 389 "content to complete schema "); 390 addChildElement(egroup, "any"); 391 } else { 392 addComment(egroup, " No mapping for structure at " + 393 ValidationException.describe(structure) + " "); 394 addComment(egroup, 395 " Fill in details of content here to complete schema "); 396 } 397 398 } else { 399 MappingElement mapping = (MappingElement)templ; 400 if (mapping.isAbstract()) { 401 402 String ename = structure.getName(); 404 if (ename == null) { 405 ename = mapping.getName(); 406 } 407 if (ename == null) { 408 409 addComment(egroup, "No schema representation for " + 411 "directly-embedded type, inlining definition"); 412 addComment(egroup, "Add element name to structure at " + 413 ValidationException.describe(structure) + 414 " to avoid inlining"); 415 defineList(mapping.children(), egroup, agroup, false); 416 417 } else { 418 419 Element element = addChildElement(egroup, "element"); 421 String tname = simpleClassName(mapping.getClassName()); 422 element.setAttribute("type", "tns:" + tname); 423 String name = structure.getName(); 424 if (name == null) { 425 name = mapping.getName(); 426 } 427 element.setAttribute("name", name); 428 if (structure.isOptional()) { 429 element.setAttribute("minOccurs", "0"); 430 } 431 } 432 433 } else { 434 435 String sname = structure.getName(); 437 String mname = mapping.getName(); 438 if (sname != null && !sname.equals(mname)) { 439 440 addComment(egroup, "No schema representation for " + 442 "element reference with different name, inlining " + 443 "definition"); 444 addComment(egroup, 445 "Remove name on mapping reference at " + 446 ValidationException.describe(structure) + 447 " to avoid inlining"); 448 defineList(mapping.children(), egroup, agroup, false); 449 450 } else { 451 452 Element element = addChildElement(egroup, "element"); 454 String tname = simpleClassName(mapping.getClassName()); 455 element.setAttribute("ref", "tns:" + mname); 456 if (structure.isOptional()) { 457 element.setAttribute("minOccurs", "0"); 458 } 459 } 460 461 } 462 } 463 464 } 465 } 466 467 478 private void defineStructureComponent(StructureElementBase comp, 479 Element egroup, Element agroup, boolean mult) { 480 481 if (comp.getName() != null) { 483 484 Element element = addChildElement(egroup, "element"); 486 element.setAttribute("name", comp.getName()); 487 if (mult) { 488 element.setAttribute("minOccurs", "0"); 489 element.setAttribute("maxOccurs", "unbounded"); 490 } else if (comp.isOptional()) { 491 element.setAttribute("minOccurs", "0"); 492 } 493 494 if (comp.children().size() > 0) { 496 defineNestedStructure(comp, element); 497 } else { 498 499 Element type = addChildElement(element, "complexType"); 501 Element seq = addChildElement(type, "sequence"); 502 503 defineEmptyStructureComponent(comp, seq, type); 505 } 506 507 } else if (comp.children().size() > 0) { 508 509 boolean coll = comp.type() == ElementBase.COLLECTION_ELEMENT; 511 m_structureStack.push(comp); 512 defineList(comp.children(), egroup, agroup, coll); 513 m_structureStack.pop(); 514 515 } else { 516 517 defineEmptyStructureComponent(comp, egroup, agroup); 519 } 520 } 521 522 532 private void defineList(ArrayList comps, Element egroup, Element agroup, 533 boolean mult) { 534 535 for (int i = 0; i < comps.size(); i++) { 537 ElementBase child = (ElementBase)comps.get(i); 538 switch (child.type()) { 539 540 case ElementBase.COLLECTION_ELEMENT: 541 case ElementBase.STRUCTURE_ELEMENT: 542 { 543 defineStructureComponent((StructureElementBase)child, 544 egroup, agroup, mult); 545 break; 546 } 547 548 case ElementBase.MAPPING_ELEMENT: 549 { 550 System.err.println("Error: nested mapping not supported " + 552 "(class " + ((MappingElement)child).getClassName() + 553 ")"); 554 break; 555 } 556 557 case ElementBase.TEMPLATE_ELEMENT: 558 { 559 System.err.println 561 ("Error: template component not yet supported"); 562 break; 563 } 564 565 case ElementBase.VALUE_ELEMENT: 566 { 567 ValueElement value = (ValueElement)child; 569 String tname = value.getType().getName(); 570 String stype = (String )s_primitiveTypeMap.get(tname); 571 if (stype == null) { 572 stype = (String )s_objectTypeMap.get(tname); 573 if (stype == null) { 574 stype = "xsd:string"; 575 } 576 } 577 578 Element element; 580 int style = value.getStyle(); 581 if (style == NestingAttributes.ATTRIBUTE_STYLE) { 582 583 element = addChildElement(agroup, "attribute"); 585 if (!value.isOptional()) { 586 element.setAttribute("use", "required"); 587 } 588 589 } else if (style == NestingAttributes.ELEMENT_STYLE) { 590 591 element = addChildElement(egroup, "element"); 593 if (mult) { 594 element.setAttribute("minOccurs", "0"); 595 element.setAttribute("maxOccurs", "unbounded"); 596 } else if (value.isOptional()) { 597 element.setAttribute("minOccurs", "0"); 598 } 599 600 } else { 601 602 System.err.println("Error: value type " + 604 value.getEffectiveStyleName() + " not supported"); 605 break; 606 607 } 608 609 element.setAttribute("name", value.getName()); 611 element.setAttribute("type", stype); 612 break; 613 } 614 } 615 } 616 } 617 618 629 private Element defineNestedStructure(ContainerElementBase container, 630 Element parent) { 631 632 Element type = addChildElement(parent, "complexType"); 634 635 Element group; 637 ArrayList childs = container.children(); 638 if (container.isOrdered()) { 639 640 group = addChildElement(type, "sequence"); 642 643 668 669 } else { 670 group = addChildElement(type, "all"); 671 } 672 673 m_structureStack.push(container); 675 defineList(childs, group, type, 676 container.type() == ElementBase.COLLECTION_ELEMENT); 677 m_structureStack.pop(); 678 return type; 679 } 680 681 689 private void generateSchema(BindingElement binding) { 690 691 m_structureStack.push(binding); 693 ArrayList tops = binding.topChildren(); 694 for (int i = 0; i < tops.size(); i++) { 695 ElementBase top = (ElementBase)tops.get(i); 696 if (top.type() == ElementBase.MAPPING_ELEMENT) { 697 698 MappingElement mapping = (MappingElement)top; 700 String uri = mapping.getNamespace().getUri(); 701 Element schema = (Element)m_schemaMap.get(uri); 702 if (schema == null) { 703 704 schema = m_document.createElementNS(XSD_URI, "schema"); 706 if (uri != null) { 707 schema.setAttribute("targetNamespace", uri); 708 } 709 m_schemaMap.put(uri, schema); 710 711 if (m_isElementQualified) { 713 schema.setAttribute("elementFormDefault", "qualified"); 714 }
|