0001: /*
0002: Copyright (c) 2004-2005, Dennis M. Sosnoski
0003: All rights reserved.
0004:
0005: Redistribution and use in source and binary forms, with or without modification,
0006: are permitted provided that the following conditions are met:
0007:
0008: * Redistributions of source code must retain the above copyright notice, this
0009: list of conditions and the following disclaimer.
0010: * Redistributions in binary form must reproduce the above copyright notice,
0011: this list of conditions and the following disclaimer in the documentation
0012: and/or other materials provided with the distribution.
0013: * Neither the name of JiBX nor the names of its contributors may be used
0014: to endorse or promote products derived from this software without specific
0015: prior written permission.
0016:
0017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
0018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
0019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
0021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
0022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
0024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0027: */
0028:
0029: package org.jibx.binding;
0030:
0031: import java.io.File;
0032: import java.io.FileInputStream;
0033: import java.io.FileNotFoundException;
0034: import java.io.FileOutputStream;
0035: import java.io.IOException;
0036: import java.net.MalformedURLException;
0037: import java.net.URL;
0038: import java.util.ArrayList;
0039: import java.util.HashMap;
0040: import java.util.Iterator;
0041:
0042: import javax.xml.parsers.DocumentBuilderFactory;
0043: import javax.xml.parsers.FactoryConfigurationError;
0044: import javax.xml.parsers.ParserConfigurationException;
0045: import javax.xml.transform.Result;
0046: import javax.xml.transform.Transformer;
0047: import javax.xml.transform.TransformerConfigurationException;
0048: import javax.xml.transform.TransformerException;
0049: import javax.xml.transform.TransformerFactory;
0050: import javax.xml.transform.TransformerFactoryConfigurationError;
0051: import javax.xml.transform.dom.DOMSource;
0052: import javax.xml.transform.stream.StreamResult;
0053:
0054: import org.jibx.binding.classes.ClassCache;
0055: import org.jibx.binding.classes.ClassFile;
0056: import org.jibx.binding.model.BindingElement;
0057: import org.jibx.binding.model.ClassWrapper;
0058: import org.jibx.binding.model.CollectionElement;
0059: import org.jibx.binding.model.ContainerElementBase;
0060: import org.jibx.binding.model.DefinitionContext;
0061: import org.jibx.binding.model.ElementBase;
0062: import org.jibx.binding.model.IClass;
0063: import org.jibx.binding.model.IClassLocator;
0064: import org.jibx.binding.model.MappingElement;
0065: import org.jibx.binding.model.NestingAttributes;
0066: import org.jibx.binding.model.NestingElementBase;
0067: import org.jibx.binding.model.StructureElement;
0068: import org.jibx.binding.model.StructureElementBase;
0069: import org.jibx.binding.model.TemplateElementBase;
0070: import org.jibx.binding.model.ValidationContext;
0071: import org.jibx.binding.model.ValidationProblem;
0072: import org.jibx.binding.model.ValueElement;
0073: import org.jibx.binding.util.ObjectStack;
0074: import org.jibx.runtime.JiBXException;
0075: import org.jibx.runtime.ValidationException;
0076: import org.w3c.dom.Document;
0077: import org.w3c.dom.Element;
0078:
0079: /**
0080: * Binding generator. This loads the specified input classes and processes them
0081: * to generate a default binding definition.
0082: *
0083: * @author Dennis M. Sosnoski
0084: * @version 1.0
0085: */
0086:
0087: public class SchemaGenerator {
0088: /** Generator version. */
0089: private static String CURRENT_VERSION = "0.2";
0090:
0091: /** Schema namespace URI. */
0092: private static final String XSD_URI = "http://www.w3.org/2001/XMLSchema";
0093:
0094: /** Fixed XML namespace. */
0095: public static final String XML_URI = "http://www.w3.org/XML/1998/namespace";
0096:
0097: /** Fixed XML namespace namespace. */
0098: public static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
0099:
0100: /** Set of object types mapped to schema types. */
0101: private static HashMap s_objectTypeMap = new HashMap();
0102:
0103: static {
0104: s_objectTypeMap.put("java.lang.Boolean", "xsd:boolean");
0105: s_objectTypeMap.put("java.lang.Byte", "xsd:byte");
0106: s_objectTypeMap.put("java.lang.Char", "xsd:unsignedInt");
0107: s_objectTypeMap.put("java.lang.Double", "xsd:double");
0108: s_objectTypeMap.put("java.lang.Float", "xsd:float");
0109: s_objectTypeMap.put("java.lang.Integer", "xsd:int");
0110: s_objectTypeMap.put("java.lang.Long", "xsd:long");
0111: s_objectTypeMap.put("java.lang.Short", "xsd:short");
0112: s_objectTypeMap.put("java.math.BigDecimal", "xsd:decimal");
0113: s_objectTypeMap.put("java.math.BigInteger", "xsd:integer");
0114: //#!j2me{
0115: s_objectTypeMap.put("java.sql.Date", "xsd:date");
0116: s_objectTypeMap.put("java.sql.Time", "xsd:time");
0117: s_objectTypeMap.put("java.sql.Timestamp", "xsd:dateTime");
0118: //#j2me}
0119: s_objectTypeMap.put("java.util.Date", "xsd:dateTime");
0120: s_objectTypeMap.put("byte[]", "xsd:base64");
0121: }
0122:
0123: /** Set of primitive types mapped to schema types. */
0124: private static HashMap s_primitiveTypeMap = new HashMap();
0125:
0126: static {
0127: s_primitiveTypeMap.put("boolean", "xsd:boolean");
0128: s_primitiveTypeMap.put("byte", "xsd:byte");
0129: s_primitiveTypeMap.put("char", "xsd:unsignedInt");
0130: s_primitiveTypeMap.put("double", "xsd:double");
0131: s_primitiveTypeMap.put("float", "xsd:float");
0132: s_primitiveTypeMap.put("int", "xsd:int");
0133: s_primitiveTypeMap.put("long", "xsd:long");
0134: s_primitiveTypeMap.put("short", "xsd:short");
0135: }
0136:
0137: /** Show verbose output flag. */
0138: private boolean m_verbose;
0139:
0140: /** Use qualified elements default in schema flag. */
0141: private boolean m_isElementQualified;
0142:
0143: /** Use qualified attributes default in schema flag. */
0144: private boolean m_isAttributeQualified;
0145:
0146: /** Indentation sequence per level of nesting. */
0147: private String m_indentSequence;
0148:
0149: /** Map from namespaces to schemas. */
0150: private HashMap m_schemaMap;
0151:
0152: /** Locator for finding classes referenced by binding. */
0153: private IClassLocator m_classLocator;
0154:
0155: /** Document used for all schema definitions. */
0156: private Document m_document;
0157:
0158: /** Stack of structure definitions in progress (used to detect cycles). */
0159: private ObjectStack m_structureStack;
0160:
0161: /**
0162: * Constructor with only paths supplied. This just initializes all other
0163: * options disabled.
0164: *
0165: * @param paths class paths to be checked for classes referenced by bindings
0166: */
0167: public SchemaGenerator(ArrayList paths) {
0168: m_structureStack = new ObjectStack();
0169: m_schemaMap = new HashMap();
0170:
0171: // set paths to be used for loading referenced classes
0172: String[] parray = (String[]) paths.toArray(new String[paths
0173: .size()]);
0174: ClassCache.setPaths(parray);
0175: ClassFile.setPaths(parray);
0176:
0177: // set class locator
0178: m_classLocator = new IClassLocator() {
0179: public IClass getClassInfo(String name) {
0180: try {
0181: return new ClassWrapper(ClassCache
0182: .getClassFile(name));
0183: } catch (JiBXException e) {
0184: throw new IllegalStateException("Class not found "
0185: + name);
0186: }
0187: }
0188: };
0189: try {
0190:
0191: // create the document used for all schemas
0192: DocumentBuilderFactory dbf = DocumentBuilderFactory
0193: .newInstance();
0194: dbf.setNamespaceAware(true);
0195: m_document = dbf.newDocumentBuilder().newDocument();
0196:
0197: } catch (ParserConfigurationException e) {
0198: throw new IllegalStateException(
0199: "Parser configuration error " + e.getMessage());
0200: } catch (FactoryConfigurationError e) {
0201: throw new IllegalStateException(
0202: "Factory configuration error " + e.getMessage());
0203: }
0204: }
0205:
0206: /**
0207: * Constructor with settings specified.
0208: *
0209: * @param verbose report binding details and results
0210: * @param equal use element form default qualified flag
0211: * @param aqual use attribute form default qualified flag
0212: * @param paths class paths to be checked for classes referenced by bindings
0213: */
0214: public SchemaGenerator(boolean verbose, boolean equal,
0215: boolean aqual, ArrayList paths) {
0216: this (paths);
0217: m_verbose = verbose;
0218: m_isElementQualified = equal;
0219: m_isAttributeQualified = aqual;
0220: m_indentSequence = " ";
0221: }
0222:
0223: /**
0224: * Set control flag for verbose processing reports.
0225: *
0226: * @param verbose report verbose information in processing bindings flag
0227: */
0228: public void setVerbose(boolean verbose) {
0229: m_verbose = verbose;
0230: }
0231:
0232: /**
0233: * Set control flag for element qualified default schema.
0234: *
0235: * @param qual element qualified default schemas flag
0236: */
0237: public void setElementQualified(boolean qual) {
0238: m_isElementQualified = qual;
0239: }
0240:
0241: /**
0242: * Set control flag for attribute qualified default schema.
0243: *
0244: * @param qual attribute qualified default schemas flag
0245: */
0246: public void setAttributeQualified(boolean qual) {
0247: m_isAttributeQualified = qual;
0248: }
0249:
0250: /**
0251: * Get array of generated schemas.
0252: *
0253: * @return array of schema elements
0254: */
0255: public Element[] getSchemas() {
0256: Element[] schemas = new Element[m_schemaMap.size()];
0257: int fill = 0;
0258: for (Iterator iter = m_schemaMap.values().iterator(); iter
0259: .hasNext();) {
0260: schemas[fill++] = (Element) iter.next();
0261: }
0262: return schemas;
0263: }
0264:
0265: /**
0266: * Generate indentation to proper depth for current item. This creates the
0267: * indentation text and appends it to the supplied parent. The generated
0268: * indentation is appropriate for the close tag of the parent element; if
0269: * a child element is to be added following this indentation it needs to
0270: * use an additional leading indent.
0271: *
0272: * @param parent element to contain indented child item
0273: */
0274: private void indentForClose(Element parent) {
0275: StringBuffer buff = new StringBuffer(20);
0276: buff.append('\n');
0277: Element ancestor = parent;
0278: boolean count = false;
0279: while (ancestor != null) {
0280: if (count) {
0281: buff.append(m_indentSequence);
0282: }
0283: ancestor = (Element) ancestor.getParentNode();
0284: count = true;
0285: }
0286: parent.appendChild(m_document.createTextNode(buff.toString()));
0287: }
0288:
0289: /**
0290: * Add comment with appropriate indentation.
0291: *
0292: * @param parent element to contain indented child item
0293: * @param text comment text
0294: */
0295: private void addComment(Element parent, String text) {
0296: if (parent.getChildNodes().getLength() == 0) {
0297: indentForClose(parent);
0298: }
0299: parent.appendChild(m_document.createTextNode(m_indentSequence));
0300: parent.appendChild(m_document.createComment(text));
0301: indentForClose(parent);
0302: }
0303:
0304: /**
0305: * Add child element with appropriate indentation. This generates and
0306: * returns the child element after adding it to the supplied parent,
0307: * allowing further modification of the new child element.
0308: *
0309: * @param parent element to contain indented child item
0310: * @param name child element name
0311: */
0312: private Element addChildElement(Element parent, String name) {
0313: if (parent.getChildNodes().getLength() == 0) {
0314: indentForClose(parent);
0315: }
0316: parent.appendChild(m_document.createTextNode(m_indentSequence));
0317: Element element = m_document.createElementNS(XSD_URI, name);
0318: parent.appendChild(element);
0319: indentForClose(parent);
0320: return element;
0321: }
0322:
0323: /**
0324: * Get innermost containing definition context.
0325: *
0326: * @return innermost definition context containing this element
0327: */
0328: public DefinitionContext getDefinitions() {
0329: int index = 0;
0330: while (index < m_structureStack.size()) {
0331: NestingElementBase nest = (NestingElementBase) m_structureStack
0332: .peek(index++);
0333: if (nest.getDefinitions() != null) {
0334: return nest.getDefinitions();
0335: }
0336: }
0337: throw new IllegalStateException(
0338: "Internal error: no definition context");
0339: }
0340:
0341: /**
0342: * Process a structure component (structure or collection element) with no
0343: * name and no child components. This adds the appropriate type of element
0344: * or any definition to the container schema element.
0345: *
0346: * @param comp structure component to be processed
0347: * @param egroup schema element to contain element definitions
0348: * @param agroup schema element to contain attribute definitions
0349: */
0350: private void defineEmptyStructureComponent(
0351: StructureElementBase comp, Element egroup, Element agroup) {
0352: NestingElementBase parent = (NestingElementBase) m_structureStack
0353: .peek(0);
0354: boolean only = parent.children().size() == 1;
0355: if (comp.type() == ElementBase.COLLECTION_ELEMENT) {
0356:
0357: // collection may define type or not
0358: CollectionElement collection = (CollectionElement) comp;
0359: String itype = collection.getItemTypeClass().getName();
0360: DefinitionContext dctx = getDefinitions();
0361: TemplateElementBase templ = dctx.getSpecificTemplate(itype);
0362: Element element = null;
0363: if (!(templ instanceof MappingElement)) {
0364: if (only) {
0365: addComment(egroup,
0366: " Replace \"any\" with details of "
0367: + "content to complete schema ");
0368: element = addChildElement(egroup, "any");
0369: } else {
0370: addComment(egroup,
0371: " No mapping for items of collection at "
0372: + ValidationException
0373: .describe(collection) + " ");
0374: addComment(egroup,
0375: " Fill in details of content to complete schema ");
0376: }
0377: } else {
0378: element = addChildElement(egroup, "element");
0379: element.setAttribute("ref", "tns:"
0380: + ((MappingElement) templ).getName());
0381: }
0382: if (element != null) {
0383: element.setAttribute("minOccurs", "0");
0384: element.setAttribute("maxOccurs", "unbounded");
0385: }
0386:
0387: } else {
0388:
0389: // check for reference to a mapped class
0390: StructureElement structure = (StructureElement) comp;
0391: TemplateElementBase templ = structure.getMapAsMapping();
0392: if (!(templ instanceof MappingElement)) {
0393:
0394: // unknown content, leave it to user to fill in details
0395: if (only) {
0396: addComment(egroup,
0397: " Replace \"any\" with details of "
0398: + "content to complete schema ");
0399: addChildElement(egroup, "any");
0400: } else {
0401: addComment(egroup, " No mapping for structure at "
0402: + ValidationException.describe(structure)
0403: + " ");
0404: addComment(egroup,
0405: " Fill in details of content here to complete schema ");
0406: }
0407:
0408: } else {
0409: MappingElement mapping = (MappingElement) templ;
0410: if (mapping.isAbstract()) {
0411:
0412: // check name to be used for instance of type
0413: String ename = structure.getName();
0414: if (ename == null) {
0415: ename = mapping.getName();
0416: }
0417: if (ename == null) {
0418:
0419: // no schema equivalent, embed definition directly
0420: addComment(
0421: egroup,
0422: "No schema representation for "
0423: + "directly-embedded type, inlining definition");
0424: addComment(egroup,
0425: "Add element name to structure at "
0426: + ValidationException
0427: .describe(structure)
0428: + " to avoid inlining");
0429: defineList(mapping.children(), egroup, agroup,
0430: false);
0431:
0432: } else {
0433:
0434: // handle abstract mapping element as reference to type
0435: Element element = addChildElement(egroup,
0436: "element");
0437: String tname = simpleClassName(mapping
0438: .getClassName());
0439: element.setAttribute("type", "tns:" + tname);
0440: String name = structure.getName();
0441: if (name == null) {
0442: name = mapping.getName();
0443: }
0444: element.setAttribute("name", name);
0445: if (structure.isOptional()) {
0446: element.setAttribute("minOccurs", "0");
0447: }
0448: }
0449:
0450: } else {
0451:
0452: // concrete mapping, check for name overridden
0453: String sname = structure.getName();
0454: String mname = mapping.getName();
0455: if (sname != null && !sname.equals(mname)) {
0456:
0457: // inline definition for overridden name
0458: addComment(
0459: egroup,
0460: "No schema representation for "
0461: + "element reference with different name, inlining "
0462: + "definition");
0463: addComment(egroup,
0464: "Remove name on mapping reference at "
0465: + ValidationException
0466: .describe(structure)
0467: + " to avoid inlining");
0468: defineList(mapping.children(), egroup, agroup,
0469: false);
0470:
0471: } else {
0472:
0473: // use element reference for concrete mapping
0474: Element element = addChildElement(egroup,
0475: "element");
0476: String tname = simpleClassName(mapping
0477: .getClassName());
0478: element.setAttribute("ref", "tns:" + mname);
0479: if (structure.isOptional()) {
0480: element.setAttribute("minOccurs", "0");
0481: }
0482: }
0483:
0484: }
0485: }
0486:
0487: }
0488: }
0489:
0490: /**
0491: * Process a structure component (structure or collection element) within a
0492: * list of child components. This adds the appropriate type of element or
0493: * any definition to the container, if necessary calling other methods for
0494: * recursive handling of nested child components.
0495: *
0496: * @param comp structure component to be processed
0497: * @param egroup schema element to contain element definitions
0498: * @param agroup schema element to contain attribute definitions
0499: * @param mult allow any number of occurrences of components flag
0500: */
0501: private void defineStructureComponent(StructureElementBase comp,
0502: Element egroup, Element agroup, boolean mult) {
0503:
0504: // check for element defined by binding component
0505: if (comp.getName() != null) {
0506:
0507: // create basic element definition for name
0508: Element element = addChildElement(egroup, "element");
0509: element.setAttribute("name", comp.getName());
0510: if (mult) {
0511: element.setAttribute("minOccurs", "0");
0512: element.setAttribute("maxOccurs", "unbounded");
0513: } else if (comp.isOptional()) {
0514: element.setAttribute("minOccurs", "0");
0515: }
0516:
0517: // check for children present
0518: if (comp.children().size() > 0) {
0519: defineNestedStructure(comp, element);
0520: } else {
0521:
0522: // nest complex type definition for actual content
0523: Element type = addChildElement(element, "complexType");
0524: Element seq = addChildElement(type, "sequence");
0525:
0526: // process the content description
0527: defineEmptyStructureComponent(comp, seq, type);
0528: }
0529:
0530: } else if (comp.children().size() > 0) {
0531:
0532: // handle child components with recursive call
0533: boolean coll = comp.type() == ElementBase.COLLECTION_ELEMENT;
0534: m_structureStack.push(comp);
0535: defineList(comp.children(), egroup, agroup, coll);
0536: m_structureStack.pop();
0537:
0538: } else {
0539:
0540: // handle empty structure definition inline
0541: defineEmptyStructureComponent(comp, egroup, agroup);
0542: }
0543: }
0544:
0545: /**
0546: * Create the schema definition list for a binding component list. This
0547: * builds the sequence of elements and attributes defined by the binding
0548: * components, including nested complex types for elements with structure.
0549: *
0550: * @param comps binding component list
0551: * @param egroup schema element to contain element definitions
0552: * @param agroup schema element to contain attribute definitions
0553: * @param mult allow any number of occurrences of components flag
0554: */
0555: private void defineList(ArrayList comps, Element egroup,
0556: Element agroup, boolean mult) {
0557:
0558: // handle all nested elements of container
0559: for (int i = 0; i < comps.size(); i++) {
0560: ElementBase child = (ElementBase) comps.get(i);
0561: switch (child.type()) {
0562:
0563: case ElementBase.COLLECTION_ELEMENT:
0564: case ElementBase.STRUCTURE_ELEMENT: {
0565: defineStructureComponent((StructureElementBase) child,
0566: egroup, agroup, mult);
0567: break;
0568: }
0569:
0570: case ElementBase.MAPPING_ELEMENT: {
0571: // nested mapping definitions not handled
0572: System.err
0573: .println("Error: nested mapping not supported "
0574: + "(class "
0575: + ((MappingElement) child)
0576: .getClassName() + ")");
0577: break;
0578: }
0579:
0580: case ElementBase.TEMPLATE_ELEMENT: {
0581: // templates to be added once usable in binding
0582: System.err
0583: .println("Error: template component not yet supported");
0584: break;
0585: }
0586:
0587: case ElementBase.VALUE_ELEMENT: {
0588: // get type information for value
0589: ValueElement value = (ValueElement) child;
0590: String tname = value.getType().getName();
0591: String stype = (String) s_primitiveTypeMap.get(tname);
0592: if (stype == null) {
0593: stype = (String) s_objectTypeMap.get(tname);
0594: if (stype == null) {
0595: stype = "xsd:string";
0596: }
0597: }
0598:
0599: // build schema element or attribute for value
0600: Element element;
0601: int style = value.getStyle();
0602: if (style == NestingAttributes.ATTRIBUTE_STYLE) {
0603:
0604: // append attribute as child of type
0605: element = addChildElement(agroup, "attribute");
0606: if (!value.isOptional()) {
0607: element.setAttribute("use", "required");
0608: }
0609:
0610: } else if (style == NestingAttributes.ELEMENT_STYLE) {
0611:
0612: // append simple element as child of grouping
0613: element = addChildElement(egroup, "element");
0614: if (mult) {
0615: element.setAttribute("minOccurs", "0");
0616: element.setAttribute("maxOccurs", "unbounded");
0617: } else if (value.isOptional()) {
0618: element.setAttribute("minOccurs", "0");
0619: }
0620:
0621: } else {
0622:
0623: // other types are not currently handled
0624: System.err.println("Error: value type "
0625: + value.getEffectiveStyleName()
0626: + " not supported");
0627: break;
0628:
0629: }
0630:
0631: // set common attributes on definition
0632: element.setAttribute("name", value.getName());
0633: element.setAttribute("type", stype);
0634: break;
0635: }
0636: }
0637: }
0638: }
0639:
0640: /**
0641: * Create the schema definition for a nested structure. This defines a
0642: * complex type, if necessary calling itself recursively for elements which
0643: * are themselves complex types. In the special case where the container
0644: * element is a mapping which extends an abstract base class this generates
0645: * the complex type as an extension of the base class complex type.
0646: *
0647: * @param container binding definition element containing nested structure
0648: * @param parent schema element to hold the definition
0649: * @return constructed complex type
0650: */
0651: private Element defineNestedStructure(
0652: ContainerElementBase container, Element parent) {
0653:
0654: // create complex type as holder for definition
0655: Element type = addChildElement(parent, "complexType");
0656:
0657: // check whether ordered or unordered container
0658: Element group;
0659: ArrayList childs = container.children();
0660: if (container.isOrdered()) {
0661:
0662: // define content list as sequence
0663: group = addChildElement(type, "sequence");
0664:
0665: // check for mapping which extends another mapping
0666: /* if (container instanceof MappingElement) {
0667: MappingElement mapping = (MappingElement)container;
0668: MappingElement extended = mapping.getExtendsMapping();
0669: if (extended != null) {
0670:
0671: // now see if the mapping can extend base complex type
0672: boolean extend = false;
0673: for (int i = 0; i < childs.size(); i++) {
0674: ElementBase child = (ElementBase)childs.get(i);
0675: if (child instanceof StructureElement) {
0676: StructureElement struct = (StructureElement)child;
0677: if (struct.getMapAsMapping() == extended) {
0678: if (struct.getName() == null) {
0679: extend = true;
0680:
0681: }
0682: }
0683: }
0684: if (child instanceof IComponent) {
0685:
0686: }
0687: }
0688: }
0689: } */
0690:
0691: } else {
0692: group = addChildElement(type, "all");
0693: }
0694:
0695: // handle all nested elements of container
0696: m_structureStack.push(container);
0697: defineList(childs, group, type,
0698: container.type() == ElementBase.COLLECTION_ELEMENT);
0699: m_structureStack.pop();
0700: return type;
0701: }
0702:
0703: /**
0704: * Generate a schema from a binding using supplied classpaths. If the schema
0705: * for the binding namespace (or default namespace) already exists the
0706: * definitions from this binding are added to the existing schema; otherwise
0707: * a new schema is created and added to the collection defined.
0708: *
0709: * @param binding root element of binding
0710: */
0711: private void generateSchema(BindingElement binding) {
0712:
0713: // process each mapping definition for binding
0714: m_structureStack.push(binding);
0715: ArrayList tops = binding.topChildren();
0716: for (int i = 0; i < tops.size(); i++) {
0717: ElementBase top = (ElementBase) tops.get(i);
0718: if (top.type() == ElementBase.MAPPING_ELEMENT) {
0719:
0720: // find or create schema for mapped class
0721: MappingElement mapping = (MappingElement) top;
0722: String uri = mapping.getNamespace().getUri();
0723: Element schema = (Element) m_schemaMap.get(uri);
0724: if (schema == null) {
0725:
0726: // build new schema element for this namespace
0727: schema = m_document.createElementNS(XSD_URI,
0728: "schema");
0729: if (uri != null) {
0730: schema.setAttribute("targetNamespace", uri);
0731: }
0732: m_schemaMap.put(uri, schema);
0733:
0734: // set qualification attributes if needed
0735: if (m_isElementQualified) {
0736: schema.setAttribute("elementFormDefault",
0737: "qualified");
0738: }
0739: if (m_isAttributeQualified) {
0740: schema.setAttribute("attributeFormDefault",
0741: "qualified");
0742: }
0743:
0744: // add namespace declarations to element
0745: if (uri != null) {
0746: schema.setAttributeNS(XMLNS_URI, "xmlns:tns",
0747: uri);
0748: }
0749: schema.setAttributeNS(XMLNS_URI, "xmlns:xsd",
0750: XSD_URI);
0751: schema.setAttributeNS(XMLNS_URI, "xmlns", XSD_URI);
0752:
0753: // add spacing for first child node
0754: indentForClose(schema);
0755: }
0756:
0757: // add spacer and comment before actual definition
0758: indentForClose(schema);
0759: String cname = mapping.getClassName();
0760: addComment(schema, " Created from mapping for class "
0761: + cname + " ");
0762: if (mapping.isAbstract()) {
0763:
0764: // add mapping as global type in binding
0765: Element type = defineNestedStructure(mapping,
0766: schema);
0767: type.setAttribute("name", simpleClassName(cname));
0768:
0769: } else {
0770:
0771: // add mapping as global element in binding
0772: Element element = addChildElement(schema, "element");
0773: element.setAttribute("name", mapping.getName());
0774:
0775: // check type of mapping definition
0776: if (mapping.getMarshaller() != null
0777: || mapping.getUnmarshaller() != null) {
0778:
0779: // use "any" for custom marshaller/unmarshaller
0780: Element type = addChildElement(element,
0781: "complexType");
0782: Element seq = addChildElement(type, "sequence");
0783: addComment(seq,
0784: " Replace \"any\" with details of "
0785: + "content to complete schema ");
0786: addChildElement(seq, "any");
0787:
0788: } else {
0789:
0790: // use complex type for embedded definition
0791: defineNestedStructure(mapping, element);
0792:
0793: }
0794: }
0795: }
0796: }
0797: m_structureStack.pop();
0798: }
0799:
0800: /**
0801: * Process a binding definition for schema generation. This first validates
0802: * the binding definition, and if it is valid then handles schema generation
0803: * from the binding.
0804: *
0805: * @param binding root element of binding
0806: * @exception JiBXException if error in generating the schema
0807: */
0808: public void generate(BindingElement binding) throws JiBXException {
0809:
0810: // validate the binding definition
0811: ValidationContext vctx = new ValidationContext(m_classLocator);
0812: binding.runValidation(vctx);
0813: boolean usable = true;
0814: if (vctx.getProblems().size() > 0) {
0815:
0816: // report problems found
0817: System.err.println("Problems found in binding "
0818: + binding.getName());
0819: ArrayList probs = vctx.getProblems();
0820: for (int i = 0; i < probs.size(); i++) {
0821: ValidationProblem prob = (ValidationProblem) probs
0822: .get(i);
0823: System.err.println(prob.getDescription());
0824: if (prob.getSeverity() > ValidationProblem.WARNING_LEVEL) {
0825: usable = false;
0826: }
0827: }
0828: }
0829:
0830: // check if binding usable for schema generation
0831: if (usable) {
0832: generateSchema(binding);
0833: } else {
0834: System.err
0835: .println("Binding validation errors prevent schema generation");
0836: System.exit(1);
0837: }
0838: }
0839:
0840: /**
0841: * Get simple class name.
0842: *
0843: * @param cname class name with full package specification
0844: * @return class name only
0845: */
0846: private String simpleClassName(String cname) {
0847: int split = cname.lastIndexOf('.');
0848: if (split >= 0) {
0849: cname = cname.substring(split + 1);
0850: }
0851: return cname;
0852: }
0853:
0854: /**
0855: * Main method for running compiler as application.
0856: *
0857: * @param args command line arguments
0858: */
0859: public static void main(String[] args) {
0860: if (args.length > 0) {
0861: try {
0862:
0863: // check for various flags set
0864: boolean verbose = false;
0865: boolean edflt = true;
0866: boolean adflt = false;
0867: ArrayList paths = new ArrayList();
0868: int offset = 0;
0869: for (; offset < args.length; offset++) {
0870: String arg = args[offset];
0871: if ("-v".equalsIgnoreCase(arg)) {
0872: verbose = true;
0873: } else if ("-e".equalsIgnoreCase(arg)) {
0874: edflt = false;
0875: } else if ("-a".equalsIgnoreCase(arg)) {
0876: adflt = true;
0877: } else if ("-p".equalsIgnoreCase(arg)) {
0878: paths.add(args[++offset]);
0879: } else {
0880: break;
0881: }
0882: }
0883:
0884: // set up path and binding lists
0885: String[] vmpaths = Utility.getClassPaths();
0886: for (int i = 0; i < vmpaths.length; i++) {
0887: paths.add(vmpaths[i]);
0888: }
0889: ArrayList bindings = new ArrayList();
0890: for (int i = offset; i < args.length; i++) {
0891: bindings.add(args[i]);
0892: }
0893:
0894: // report on the configuration
0895: System.out.println("Running schema generator version "
0896: + CURRENT_VERSION);
0897: if (verbose) {
0898: System.out.println("Using paths:");
0899: for (int i = 0; i < paths.size(); i++) {
0900: System.out.println(" " + paths.get(i));
0901: }
0902: System.out.println("Using input bindings:");
0903: for (int i = 0; i < bindings.size(); i++) {
0904: System.out.println(" " + bindings.get(i));
0905: }
0906: }
0907:
0908: // process all specified binding files
0909: SchemaGenerator schemagen = new SchemaGenerator(
0910: verbose, edflt, adflt, paths);
0911: for (int i = 0; i < bindings.size(); i++) {
0912:
0913: // read binding from file
0914: String bpath = (String) bindings.get(i);
0915: String name = Utility.convertName(Utility
0916: .fileName(bpath));
0917: File file = new File(bpath);
0918: BindingElement binding = Utility.validateBinding(
0919: name, new URL("file://"
0920: + file.getAbsolutePath()),
0921: new FileInputStream(file));
0922:
0923: // convert the binding to a schema
0924: if (binding != null) {
0925: schemagen.generate(binding);
0926: }
0927: }
0928:
0929: // output each schema to separate file
0930: Element[] schemas = schemagen.getSchemas();
0931: for (int i = 0; i < schemas.length; i++) {
0932:
0933: // get base name for output file from last part of namespace
0934: Element schema = schemas[i];
0935: String tns = schema.getAttribute("targetNamespace");
0936: String name = tns;
0937: if (name.length() == 0) {
0938: // fix this to relate back to binding
0939: name = (String) bindings.get(0);
0940: int split = name.lastIndexOf('.');
0941: if (split >= 0) {
0942: name = name.substring(0, split);
0943: }
0944: } else {
0945: int split = name.lastIndexOf('/');
0946: if (split >= 0) {
0947: name = name.substring(split + 1);
0948: }
0949: }
0950: try {
0951:
0952: // write schema to output file
0953: name += ".xsd";
0954: FileOutputStream out = new FileOutputStream(
0955: name);
0956: Transformer transformer = TransformerFactory
0957: .newInstance().newTransformer();
0958: transformer.setOutputProperty("indent", "no");
0959: DOMSource source = new DOMSource(schema);
0960: Result result = new StreamResult(out);
0961: transformer.transform(source, result);
0962: out.close();
0963: System.out.print("Wrote schema " + name);
0964: if (tns.length() == 0) {
0965: System.out
0966: .println(" for default namespace");
0967: } else {
0968: System.out.println(" for namespace " + tns);
0969: }
0970:
0971: } catch (TransformerConfigurationException e) {
0972: e.printStackTrace();
0973: } catch (TransformerFactoryConfigurationError e) {
0974: e.printStackTrace();
0975: } catch (TransformerException e) {
0976: e.printStackTrace();
0977: } catch (IOException e) {
0978: e.printStackTrace();
0979: }
0980: }
0981:
0982: } catch (JiBXException ex) {
0983: ex.printStackTrace();
0984: System.exit(1);
0985: } catch (FileNotFoundException e) {
0986: e.printStackTrace();
0987: System.exit(2);
0988: } catch (MalformedURLException e) {
0989: e.printStackTrace();
0990: System.exit(3);
0991: }
0992:
0993: } else {
0994: System.out
0995: .println("\nUsage: java org.jibx.binding.SchemaGenerator [-v] [-e]"
0996: + " [-a] [-p path]* binding1 binding2 ...\nwhere:"
0997: + "\n -v turns on verbose output,"
0998: + "\n -e sets elementFormDefault=\"false\" for the schemas,"
0999: + "\n -a sets attributeFormDefault=\"true\" for the schemas, "
1000: + "and\n -p gives a path component for looking up the classes "
1001: + "referenced in the binding\nThe binding# files are "
1002: + "different bindings to be used for schema generation.\n");
1003: System.exit(1);
1004: }
1005: }
1006: }
|