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 } 715 if (m_isAttributeQualified) { 716 schema.setAttribute("attributeFormDefault", 717 "qualified"); 718 } 719 720 if (uri != null) { 722 schema.setAttributeNS(XMLNS_URI, "xmlns:tns", uri); 723 } 724 schema.setAttributeNS(XMLNS_URI, "xmlns:xsd", XSD_URI); 725 schema.setAttributeNS(XMLNS_URI, "xmlns", XSD_URI); 726 727 indentForClose(schema); 729 } 730 731 indentForClose(schema); 733 String cname = mapping.getClassName(); 734 addComment(schema, " Created from mapping for class " + 735 cname + " "); 736 if (mapping.isAbstract()) { 737 738 Element type = defineNestedStructure(mapping, schema); 740 type.setAttribute("name", simpleClassName(cname)); 741 742 } else { 743 744 Element element = addChildElement(schema, "element"); 746 element.setAttribute("name", mapping.getName()); 747 748 if (mapping.getMarshaller() != null || 750 mapping.getUnmarshaller() != null) { 751 752 Element type = addChildElement(element, "complexType"); 754 Element seq = addChildElement(type, "sequence"); 755 addComment(seq, " Replace \"any\" with details of " + 756 "content to complete schema "); 757 addChildElement(seq, "any"); 758 759 } else { 760 761 defineNestedStructure(mapping, element); 763 764 } 765 } 766 } 767 } 768 m_structureStack.pop(); 769 } 770 771 779 public void generate(BindingElement binding) throws JiBXException { 780 781 ValidationContext vctx = new ValidationContext(m_classLocator); 783 binding.runValidation(vctx); 784 boolean usable = true; 785 if (vctx.getProblems().size() > 0) { 786 787 System.err.println("Problems found in binding " + 789 binding.getName()); 790 ArrayList probs = vctx.getProblems(); 791 for (int i = 0; i < probs.size(); i++) { 792 ValidationProblem prob = (ValidationProblem)probs.get(i); 793 System.err.println(prob.getDescription()); 794 if (prob.getSeverity() > ValidationProblem.WARNING_LEVEL) { 795 usable = false; 796 } 797 } 798 } 799 800 if (usable) { 802 generateSchema(binding); 803 } else { 804 System.err.println 805 ("Binding validation errors prevent schema generation"); 806 System.exit(1); 807 } 808 } 809 810 814 private String simpleClassName(String cname) { 815 int split = cname.lastIndexOf('.'); 816 if (split >= 0) { 817 cname = cname.substring(split+1); 818 } 819 return cname; 820 } 821 822 827 public static void main(String [] args) { 828 if (args.length > 0) { 829 try { 830 831 boolean verbose = false; 833 boolean edflt = true; 834 boolean adflt = false; 835 ArrayList paths = new ArrayList (); 836 int offset = 0; 837 for (; offset < args.length; offset++) { 838 String arg = args[offset]; 839 if ("-v".equalsIgnoreCase(arg)) { 840 verbose = true; 841 } else if ("-e".equalsIgnoreCase(arg)) { 842 edflt = false; 843 } else if ("-a".equalsIgnoreCase(arg)) { 844 adflt = true; 845 } else if ("-p".equalsIgnoreCase(arg)) { 846 paths.add(args[++offset]); 847 } else { 848 break; 849 } 850 } 851 852 String [] vmpaths = Utility.getClassPaths(); 854 for (int i = 0; i < vmpaths.length; i++) { 855 paths.add(vmpaths[i]); 856 } 857 ArrayList bindings = new ArrayList (); 858 for (int i = offset; i < args.length; i++) { 859 bindings.add(args[i]); 860 } 861 862 System.out.println("Running schema generator version " + 864 CURRENT_VERSION); 865 if (verbose) { 866 System.out.println("Using paths:"); 867 for (int i = 0; i < paths.size(); i++) { 868 System.out.println(" " + paths.get(i)); 869 } 870 System.out.println("Using input bindings:"); 871 for (int i = 0; i < bindings.size(); i++) { 872 System.out.println(" " + bindings.get(i)); 873 } 874 } 875 876 SchemaGenerator schemagen = new SchemaGenerator(verbose, edflt, 878 adflt, paths); 879 for (int i = 0; i < bindings.size(); i++) { 880 881 String bpath = (String )bindings.get(i); 883 String name = Utility.convertName(Utility.fileName(bpath)); 884 File file = new File (bpath); 885 BindingElement binding = Utility.validateBinding(name, 886 new URL ("file://" + file.getAbsolutePath()), 887 new FileInputStream (file)); 888 889 if (binding != null) { 891 schemagen.generate(binding); 892 } 893 } 894 895 Element[] schemas = schemagen.getSchemas(); 897 for (int i = 0; i < schemas.length; i++) { 898 899 Element schema = schemas[i]; 901 String tns = schema.getAttribute("targetNamespace"); 902 String name = tns; 903 if (name.length() == 0) { 904 name = (String )bindings.get(0); 906 int split = name.lastIndexOf('.'); 907 if (split >= 0) { 908 name = name.substring(0, split); 909 } 910 } else { 911 int split = name.lastIndexOf('/'); 912 if (split >= 0) { 913 name = name.substring(split+1); 914 } 915 } 916 try { 917 918 name += ".xsd"; 920 FileOutputStream out = new FileOutputStream (name); 921 Transformer transformer = 922 TransformerFactory.newInstance().newTransformer(); 923 transformer.setOutputProperty("indent", "no"); 924 DOMSource source = new DOMSource (schema); 925 Result result = new StreamResult (out); 926 transformer.transform(source, result); 927 out.close(); 928 System.out.print("Wrote schema " + name); 929 if (tns.length() == 0) { 930 System.out.println(" for default namespace"); 931 } else { 932 System.out.println(" for namespace " + tns); 933 } 934 935 } catch (TransformerConfigurationException e) { 936 e.printStackTrace(); 937 } catch (TransformerFactoryConfigurationError e) { 938 e.printStackTrace(); 939 } catch (TransformerException e) { 940 e.printStackTrace(); 941 } catch (IOException e) { 942 e.printStackTrace(); 943 } 944 } 945 946 } catch (JiBXException ex) { 947 ex.printStackTrace(); 948 System.exit(1); 949 } catch (FileNotFoundException e) { 950 e.printStackTrace(); 951 System.exit(2); 952 } catch (MalformedURLException e) { 953 e.printStackTrace(); 954 System.exit(3); 955 } 956 957 } else { 958 System.out.println 959 ("\nUsage: java org.jibx.binding.SchemaGenerator [-v] [-e]" + 960 " [-a] [-p path]* binding1 binding2 ...\nwhere:" + 961 "\n -v turns on verbose output," + 962 "\n -e sets elementFormDefault=\"false\" for the schemas," + 963 "\n -a sets attributeFormDefault=\"true\" for the schemas, " + 964 "and\n -p gives a path component for looking up the classes " + 965 "referenced in the binding\nThe binding# files are " + 966 "different bindings to be used for schema generation.\n"); 967 System.exit(1); 968 } 969 } 970 } | Popular Tags |