0001: /*
0002: Copyright (c) 2007, 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.generator;
0030:
0031: import java.io.IOException;
0032: import java.io.InputStream;
0033: import java.lang.reflect.InvocationTargetException;
0034: import java.lang.reflect.Method;
0035: import java.util.AbstractList;
0036: import java.util.ArrayList;
0037: import java.util.Collection;
0038: import java.util.HashMap;
0039: import java.util.Iterator;
0040: import java.util.List;
0041: import java.util.Map;
0042:
0043: import org.jibx.binding.model.CollectionElement;
0044: import org.jibx.binding.model.ContainerElementBase;
0045: import org.jibx.binding.model.DocumentFormatter;
0046: import org.jibx.binding.model.IClass;
0047: import org.jibx.binding.model.IClassItem;
0048: import org.jibx.binding.model.IClassLocator;
0049: import org.jibx.binding.model.IComponent;
0050: import org.jibx.binding.model.MappingElement;
0051: import org.jibx.binding.model.StructureElement;
0052: import org.jibx.binding.model.StructureElementBase;
0053: import org.jibx.binding.model.TemplateElementBase;
0054: import org.jibx.binding.model.ValidationContext;
0055: import org.jibx.binding.model.ValidationProblem;
0056: import org.jibx.binding.model.ValueElement;
0057: import org.jibx.runtime.QName;
0058: import org.jibx.schema.ISchemaResolver;
0059: import org.jibx.schema.TreeWalker;
0060: import org.jibx.schema.elements.AllElement;
0061: import org.jibx.schema.elements.AnnotatedBase;
0062: import org.jibx.schema.elements.AnnotationElement;
0063: import org.jibx.schema.elements.AttributeElement;
0064: import org.jibx.schema.elements.AttributeGroupElement;
0065: import org.jibx.schema.elements.AttributeGroupRefElement;
0066: import org.jibx.schema.elements.ChoiceElement;
0067: import org.jibx.schema.elements.CommonCompositorDefinition;
0068: import org.jibx.schema.elements.ComplexContentElement;
0069: import org.jibx.schema.elements.ComplexExtensionElement;
0070: import org.jibx.schema.elements.ComplexTypeElement;
0071: import org.jibx.schema.elements.DocumentationElement;
0072: import org.jibx.schema.elements.ElementElement;
0073: import org.jibx.schema.elements.GroupElement;
0074: import org.jibx.schema.elements.GroupRefElement;
0075: import org.jibx.schema.elements.SchemaElement;
0076: import org.jibx.schema.elements.SequenceElement;
0077: import org.jibx.schema.elements.SimpleContentElement;
0078: import org.jibx.schema.elements.SimpleRestrictionElement;
0079: import org.jibx.schema.elements.SimpleTypeElement;
0080: import org.jibx.schema.elements.FacetElement.Enumeration;
0081: import org.jibx.schema.types.Count;
0082: import org.jibx.schema.validation.PrevalidationVisitor;
0083: import org.jibx.schema.validation.RegistrationVisitor;
0084: import org.jibx.schema.validation.ValidationVisitor;
0085: import org.jibx.util.Types;
0086: import org.w3c.dom.Node;
0087:
0088: /**
0089: * Schema generator from binding definition.
0090: */
0091: public class SchemaGenerator {
0092: /** Locator for class information (<code>null</code> if none). */
0093: private final IClassLocator m_locator;
0094:
0095: /** Binding customization information. */
0096: private final GlobalCustom m_custom;
0097:
0098: /** Document used for annotations (<code>null</code> if none). */
0099: private final DocumentFormatter m_formatter;
0100:
0101: /** Map from fully-qualified class name to qualified simple type name. */
0102: private final Map m_simpleTypeMap;
0103:
0104: /** Map from namespace to schema holder. */
0105: private final Map m_uriSchemaMap;
0106:
0107: /** Validation context for bindings. */
0108: private final ValidationContext m_context;
0109:
0110: /** Details for mappings and enum usages. */
0111: private final SchemaDetailDirectory m_detailDirectory;
0112:
0113: /**
0114: * Constructor.
0115: *
0116: * @param loc locator for class information (<code>null</code> if none)
0117: * @param custom binding customization information (used for creating names
0118: * as needed)
0119: */
0120: public SchemaGenerator(IClassLocator loc, GlobalCustom custom) {
0121: m_locator = loc;
0122: if (loc == null) {
0123: m_formatter = null;
0124: } else {
0125: m_formatter = new DocumentFormatter();
0126: }
0127: m_custom = custom;
0128: m_simpleTypeMap = new HashMap();
0129: m_uriSchemaMap = new HashMap();
0130: m_context = new ValidationContext(loc);
0131: m_detailDirectory = new SchemaDetailDirectory(loc, custom,
0132: m_context);
0133: }
0134:
0135: /**
0136: * Find the schema to be used for a particular namespace. If this is the
0137: * first time a particular namespace was requested, a new schema will be
0138: * created for that namespace and returned, using a default file name.
0139: *
0140: * @param uri namespace URI (<code>null</code> if no namespace)
0141: * @return schema holder
0142: */
0143: public SchemaHolder findSchema(String uri) {
0144: SchemaHolder hold = (SchemaHolder) m_uriSchemaMap.get(uri);
0145: if (hold == null) {
0146: hold = new SchemaHolder(uri);
0147: m_uriSchemaMap.put(uri, hold);
0148: String sname;
0149: if (uri == null) {
0150: sname = "nonamespace.xsd";
0151: } else {
0152: sname = uri.substring(uri.lastIndexOf('/') + 1)
0153: + ".xsd";
0154: }
0155: hold.setFileName(sname);
0156: }
0157: return hold;
0158: }
0159:
0160: /**
0161: * Get the qualified name of the simple type used for a component, if a
0162: * named simple type. If this returns <code>null</code>, the {@link
0163: * #buildSimpleType(IComponent)} method needs to be able to construct the
0164: * appropriate simple type definition.
0165: *
0166: * @param comp
0167: * @return qualified type name, <code>null</code> if inline definition
0168: * needed
0169: */
0170: private QName getSimpleTypeQName(IComponent comp) {
0171: IClass type = comp.getType();
0172: String tname = comp.getType().getName();
0173: QName qname = Types.schemaType(tname);
0174: if (qname == null) {
0175: qname = (QName) m_simpleTypeMap.get(tname);
0176: if (qname == null) {
0177: if (!type.isSuperclass("java.lang.Enum")) {
0178: m_context
0179: .addWarning(
0180: "No schema equivalent known for type - treating as string",
0181: comp);
0182: qname = Types.STRING_QNAME;
0183: }
0184: }
0185: }
0186: return qname;
0187: }
0188:
0189: /**
0190: * Add class-level documentation to a schema component.
0191: *
0192: * @param info
0193: * @param root
0194: */
0195: private void addDocumentation(IClass info, AnnotatedBase root) {
0196: if (info != null) {
0197: List nodes = m_formatter.docToNodes(info.getJavaDoc());
0198: if (nodes != null) {
0199: AnnotationElement anno = new AnnotationElement();
0200: DocumentationElement doc = new DocumentationElement();
0201: for (Iterator iter = nodes.iterator(); iter.hasNext();) {
0202: Node node = (Node) iter.next();
0203: doc.addContent(node);
0204: }
0205: anno.getItemsList().add(doc);
0206: root.setAnnotation(anno);
0207: }
0208: }
0209: }
0210:
0211: /**
0212: * Set the documentation for a schema component matching a class member.
0213: *
0214: * @param elem
0215: * @param item
0216: */
0217: private void setDocumentation(IClassItem item, AnnotatedBase elem) {
0218: List nodes = m_formatter.docToNodes(item.getJavaDoc());
0219: if (nodes != null) {
0220: AnnotationElement anno = new AnnotationElement();
0221: DocumentationElement doc = new DocumentationElement();
0222: for (Iterator iter = nodes.iterator(); iter.hasNext();) {
0223: Node node = (Node) iter.next();
0224: doc.addContent(node);
0225: }
0226: anno.getItemsList().add(doc);
0227: elem.setAnnotation(anno);
0228: }
0229: }
0230:
0231: /**
0232: * Add documentation for a particular field or property.
0233: *
0234: * @param struct
0235: * @param elem
0236: */
0237: private void addItemDocumentation(StructureElementBase struct,
0238: AnnotatedBase elem) {
0239: IClassItem item = struct.getField();
0240: if (item == null) {
0241: item = struct.getGet();
0242: }
0243: if (item != null) {
0244: setDocumentation(item, elem);
0245: }
0246: }
0247:
0248: /**
0249: * Add documentation for a particular field or property.
0250: *
0251: * @param value
0252: * @param elem
0253: */
0254: private void addItemDocumentation(ValueElement value,
0255: AnnotatedBase elem) {
0256: IClassItem item = value.getField();
0257: if (item == null) {
0258: item = value.getGet();
0259: }
0260: if (item != null) {
0261: setDocumentation(item, elem);
0262: }
0263: }
0264:
0265: /**
0266: * Create the simple type definition for a type. This is only used for cases
0267: * where {@link #getSimpleTypeQName(IComponent)} returns <code>null</code>.
0268: * The current implementation only supports Java 5 Enum types, but in the
0269: * future should be extended to support <format>-type conversions with
0270: * supplied schema definitions.
0271: *
0272: * @param type
0273: * @return type definition
0274: */
0275: private SimpleTypeElement buildSimpleType(IClass type) {
0276: SchemaEnumDetail detail = m_detailDirectory
0277: .getSimpleDetail(type.getName());
0278: try {
0279: Class clas = type.loadClass();
0280: Method method = clas.getMethod("values", (Class[]) null);
0281: try {
0282: method.setAccessible(true);
0283: } catch (RuntimeException e) { /* deliberately empty */
0284: }
0285: Enum[] values = (Enum[]) method.invoke(null,
0286: (Object[]) null);
0287: SimpleTypeElement simple = new SimpleTypeElement();
0288: SimpleRestrictionElement restr = new SimpleRestrictionElement();
0289: restr.setBase(Types.STRING_QNAME);
0290: for (int i = 0; i < values.length; i++) {
0291: Enumeration enumel = new Enumeration();
0292: enumel.setValue(values[i].name());
0293: restr.getFacetsList().add(enumel);
0294: }
0295: simple.setDerivation(restr);
0296: addDocumentation(detail.getCustom().getClassInformation(),
0297: simple);
0298: return simple;
0299: } catch (SecurityException e) {
0300: e.printStackTrace();
0301: } catch (NoSuchMethodException e) {
0302: e.printStackTrace();
0303: } catch (IllegalArgumentException e) {
0304: e.printStackTrace();
0305: } catch (IllegalAccessException e) {
0306: e.printStackTrace();
0307: } catch (InvocationTargetException e) {
0308: e.printStackTrace();
0309: }
0310: return null;
0311: }
0312:
0313: /**
0314: * Convenience method to create the simple type definition for the type of a
0315: * component.
0316: *
0317: * @param comp
0318: * @return type definition
0319: */
0320: private SimpleTypeElement buildSimpleType(IComponent comp) {
0321: return buildSimpleType(comp.getType());
0322: }
0323:
0324: /**
0325: * General object comparison method. Don't know why Sun hasn't seen fit to
0326: * include this somewhere, but at least it's easy to write (over and over
0327: * again).
0328: *
0329: * @param a first object to be compared
0330: * @param b second object to be compared
0331: * @return <code>true</code> if both objects are <code>null</code>, or if
0332: * <code>a.equals(b)</code>; <code>false</code> otherwise
0333: */
0334: public static boolean isEqual(Object a, Object b) {
0335: return (a == null) ? b == null : a.equals(b);
0336: }
0337:
0338: /**
0339: * Check for dependency on another schema.
0340: *
0341: * @param qname name referenced by this schema
0342: * @param hold schema holder
0343: */
0344: private void checkDependency(QName qname, SchemaHolder hold) {
0345: if (qname != null
0346: && !isEqual(qname.getUri(), hold.getNamespace())) {
0347: String uri = qname.getUri();
0348: SchemaHolder tohold = findSchema(uri);
0349: hold.addReference(tohold);
0350: hold.getPrefix(uri);
0351: }
0352: }
0353:
0354: /**
0355: * Build a schema element description from a binding content component.
0356: *
0357: * @param comp source component
0358: * @param repeat repeated element flag
0359: * @param holder
0360: * @return element
0361: */
0362: private ElementElement buildElement(IComponent comp,
0363: boolean repeat, SchemaHolder holder) {
0364:
0365: // create the basic element structure
0366: ElementElement elem = new ElementElement();
0367: if (repeat) {
0368: elem.setMinOccurs(Count.COUNT_ZERO);
0369: elem.setMaxOccurs(Count.COUNT_UNBOUNDED);
0370: } else if (comp.isOptional()) {
0371: elem.setMinOccurs(Count.COUNT_ZERO);
0372: }
0373:
0374: // set or create the element type definition
0375: boolean isref = false;
0376: if (comp instanceof ValueElement) {
0377: QName qname = getSimpleTypeQName(comp);
0378: if (qname == null) {
0379: elem.setInlineType(buildSimpleType(comp));
0380: } else {
0381: elem.setType(qname);
0382: }
0383: addItemDocumentation((ValueElement) comp, elem);
0384: } else if (comp instanceof CollectionElement) {
0385: CollectionElement coll = (CollectionElement) comp;
0386: if (coll.children().size() > 0) {
0387:
0388: // collection with children, choice or sequence from order
0389: ComplexTypeElement type = new ComplexTypeElement();
0390: if (coll.isOrdered()) {
0391:
0392: // ordered children go to sequence element, repeating
0393: SequenceElement seq = new SequenceElement();
0394: type.setContentDefinition(seq);
0395:
0396: } else {
0397:
0398: // unordered children go to repeated choice element
0399: ChoiceElement choice = new ChoiceElement();
0400: type.setContentDefinition(choice);
0401:
0402: }
0403: type.setContentDefinition(buildCompositor(coll, 0,
0404: true, holder));
0405: elem.setInlineType(type);
0406:
0407: } else {
0408:
0409: // empty collection, item-type must reference a concrete mapping
0410: String itype = coll.getItemTypeName();
0411: TemplateElementBase ref = coll.getDefinitions()
0412: .getSpecificTemplate(itype);
0413: if (ref instanceof MappingElement) {
0414:
0415: // item type with concrete mapping, make it an element
0416: SchemaMappingDetail detail = m_detailDirectory
0417: .getMappingDetail((MappingElement) ref);
0418: checkDependency(detail.getOtherName(), holder);
0419: ComplexTypeElement type = new ComplexTypeElement();
0420: SequenceElement seq = new SequenceElement();
0421: type.setContentDefinition(seq);
0422: ElementElement item = new ElementElement();
0423: item.setRef(detail.getOtherName());
0424: item.setMinOccurs(Count.COUNT_ZERO);
0425: item.setMaxOccurs(Count.COUNT_UNBOUNDED);
0426: seq.getParticleList().add(item);
0427: elem.setInlineType(type);
0428: addItemDocumentation(coll, item);
0429:
0430: } else {
0431:
0432: // TODO: handle this with xs:any strict?
0433: m_context
0434: .addWarning(
0435: "Handling not implemented for unspecified mapping",
0436: coll);
0437: }
0438:
0439: }
0440: } else {
0441:
0442: // must be a structure, check children
0443: StructureElement struct = (StructureElement) comp;
0444: if (struct.children().size() > 0) {
0445:
0446: // structure with children, choice or sequence from order
0447: ComplexTypeElement type = new ComplexTypeElement();
0448: if (struct.isOrdered()) {
0449:
0450: // ordered children go to sequence element
0451: SequenceElement seq = new SequenceElement();
0452: type.setContentDefinition(seq);
0453:
0454: } else {
0455:
0456: // unordered children go to repeated choice element
0457: ChoiceElement choice = new ChoiceElement();
0458: type.setContentDefinition(choice);
0459: }
0460: type.setContentDefinition(buildCompositor(struct, 0,
0461: false, holder));
0462: fillAttributes(struct, 0, type.getAttributeList(),
0463: holder);
0464: elem.setInlineType(type);
0465:
0466: } else {
0467:
0468: // no children, must be a mapping reference
0469: MappingElement ref = (MappingElement) struct
0470: .getEffectiveMapping();
0471: if (ref == null) {
0472:
0473: // TODO: handle this with xs:any strict?
0474: m_context
0475: .addWarning(
0476: "Handling not implemented for unspecified mapping",
0477: struct);
0478:
0479: } else {
0480:
0481: // implicit mapping reference, find the handling
0482: SchemaMappingDetail detail = m_detailDirectory
0483: .getMappingDetail(ref);
0484: if (detail.isElement()) {
0485:
0486: // structure with concrete mapping
0487: checkDependency(detail.getOtherName(), holder);
0488: elem.setRef(detail.getOtherName());
0489: isref = true;
0490:
0491: } else {
0492:
0493: // set element type to that constructed from mapping
0494: checkDependency(detail.getTypeName(), holder);
0495: elem.setType(detail.getTypeName());
0496:
0497: }
0498: }
0499: }
0500: addItemDocumentation(struct, elem);
0501: }
0502: if (!isref) {
0503: elem.setName(comp.getName());
0504: }
0505: return elem;
0506: }
0507:
0508: /**
0509: * Build compositor for type definition. This creates and returns the
0510: * appropriate form of compositor for the container, populated with
0511: * particles matching the child element components of the binding container.
0512: *
0513: * @param cont container element defining structure
0514: * @param offset offset for first child component to process
0515: * @param repeat repeated item flag
0516: * @param holder holder for schema under construction
0517: * @return constructed compositor
0518: */
0519: private CommonCompositorDefinition buildCompositor(
0520: ContainerElementBase cont, int offset, boolean repeat,
0521: SchemaHolder holder) {
0522:
0523: // start with the appropriate type of compositor
0524: CommonCompositorDefinition cdef;
0525: if (cont.isChoice()) {
0526:
0527: // choice container goes directly to choice compositor
0528: cdef = new ChoiceElement();
0529:
0530: } else if (cont.isOrdered()) {
0531:
0532: // ordered container goes directly to sequence compositor
0533: cdef = new SequenceElement();
0534:
0535: } else if (repeat) {
0536:
0537: // unordered repeat treated as repeated choice compositor
0538: cdef = new ChoiceElement();
0539: cdef.setMaxOccurs(Count.COUNT_UNBOUNDED);
0540:
0541: } else {
0542:
0543: // unordered non-repeat treated as all compositor
0544: // TODO: verify conditions for all
0545: cdef = new AllElement();
0546: }
0547:
0548: // generate schema equivalents for content components of container
0549: ArrayList comps = cont.getContentComponents();
0550: for (int i = offset; i < comps.size(); i++) {
0551: IComponent comp = (IComponent) comps.get(i);
0552: if (comp.hasName()) {
0553:
0554: // create element for named content component
0555: ElementElement element = buildElement(comp, repeat,
0556: holder);
0557: if (comp instanceof StructureElementBase) {
0558: addItemDocumentation((StructureElementBase) comp,
0559: element);
0560: }
0561: cdef.getParticleList().add(element);
0562:
0563: } else if (comp instanceof ContainerElementBase) {
0564: ContainerElementBase contain = (ContainerElementBase) comp;
0565: boolean iscoll = comp instanceof CollectionElement;
0566: if (contain.children().size() > 0) {
0567:
0568: // no element name, but children; handle with recursive call
0569: CommonCompositorDefinition part = buildCompositor(
0570: contain, 0, iscoll, holder);
0571: cdef.getParticleList().add(part);
0572:
0573: } else if (iscoll) {
0574:
0575: // collection without a wrapper element
0576: CollectionElement coll = (CollectionElement) comp;
0577: String itype = coll.getItemTypeName();
0578: TemplateElementBase ref = coll.getDefinitions()
0579: .getSpecificTemplate(itype);
0580: if (ref instanceof MappingElement) {
0581:
0582: // item type with concrete mapping, make it an element
0583: SchemaMappingDetail detail = m_detailDirectory
0584: .getMappingDetail((MappingElement) ref);
0585: checkDependency(detail.getOtherName(), holder);
0586: ElementElement item = new ElementElement();
0587: item.setRef(detail.getOtherName());
0588: item.setMinOccurs(Count.COUNT_ZERO);
0589: item.setMaxOccurs(Count.COUNT_UNBOUNDED);
0590: addItemDocumentation(coll, item);
0591: cdef.getParticleList().add(item);
0592:
0593: } else {
0594:
0595: // TODO: handle this with xs:any strict?
0596: m_context
0597: .addWarning(
0598: "Handling not implemented for unspecified mapping",
0599: coll);
0600: }
0601:
0602: } else if (comp instanceof StructureElement) {
0603:
0604: // no children, must be mapping reference
0605: StructureElement struct = (StructureElement) comp;
0606: MappingElement ref = (MappingElement) struct
0607: .getEffectiveMapping();
0608: if (ref == null) {
0609:
0610: // TODO: handle this with xs:any strict?
0611: m_context
0612: .addWarning(
0613: "Handling not implemented for unspecified mapping",
0614: struct);
0615:
0616: } else {
0617:
0618: // handle mapping reference based on form and name use
0619: SchemaMappingDetail detail = m_detailDirectory
0620: .getMappingDetail(ref);
0621: checkDependency(detail.getOtherName(), holder);
0622: if (ref.isAbstract()) {
0623:
0624: // abstract inline treated as group
0625: GroupRefElement group = new GroupRefElement();
0626: group.setRef(detail.getOtherName());
0627: if (comp.isOptional()) {
0628: group.setMinOccurs(Count.COUNT_ZERO);
0629: }
0630: cdef.getParticleList().add(group);
0631:
0632: } else {
0633:
0634: // concrete treated as element reference
0635: ElementElement elem = new ElementElement();
0636: elem.setRef(detail.getOtherName());
0637: if (comp.isOptional()) {
0638: elem.setMinOccurs(Count.COUNT_ZERO);
0639: }
0640: addItemDocumentation(struct, elem);
0641: cdef.getParticleList().add(elem);
0642:
0643: }
0644: }
0645: } else {
0646: m_context.addError("Unsupported binding construct",
0647: comp);
0648: }
0649:
0650: } else {
0651: m_context.addError("Unsupported component", comp);
0652: }
0653: }
0654:
0655: // simplify by eliminating this level if only child a compositor
0656: if ((cont.isOrdered() || cont.isChoice())
0657: && cdef.getParticleList().size() == 1) {
0658: Object child = cdef.getParticleList().get(0);
0659: if (child instanceof CommonCompositorDefinition) {
0660: cdef = (CommonCompositorDefinition) child;
0661: }
0662: }
0663: return cdef;
0664: }
0665:
0666: /**
0667: * Build attributes as part of complex type definition.
0668: *
0669: * @param cont container element defining structure
0670: * @param offset offset for first child component to process
0671: * @param attrs complex type attribute list
0672: * @param holder holder for schema under construction
0673: */
0674: private void fillAttributes(ContainerElementBase cont, int offset,
0675: AbstractList attrs, SchemaHolder holder) {
0676:
0677: // add child attribute components
0678: ArrayList comps = cont.getAttributeComponents();
0679: for (int i = 0; i < comps.size(); i++) {
0680: IComponent comp = (IComponent) comps.get(i);
0681: if (comp instanceof ValueElement) {
0682:
0683: // simple attribute definition
0684: AttributeElement attr = new AttributeElement();
0685: attr.setName(comp.getName());
0686: QName qname = getSimpleTypeQName(comp);
0687: if (qname == null) {
0688: attr.setInlineType(buildSimpleType(comp));
0689: } else {
0690: attr.setType(qname);
0691: }
0692: if (!comp.isOptional()) {
0693: attr.setUse(AttributeElement.REQUIRED_USE);
0694: }
0695: addItemDocumentation((ValueElement) comp, attr);
0696: attrs.add(attr);
0697:
0698: } else if (comp instanceof ContainerElementBase) {
0699: ContainerElementBase contain = (ContainerElementBase) comp;
0700: if (contain.children().size() > 0) {
0701:
0702: // handle children with recursive call
0703: fillAttributes(contain, 0, attrs, holder);
0704:
0705: } else if (comp instanceof StructureElement) {
0706:
0707: // no children, must be mapping reference
0708: StructureElement struct = (StructureElement) comp;
0709: if (struct.isOptional()) {
0710: m_context
0711: .addError(
0712: "No schema equivalent for optional abstract mapping with attributes",
0713: comp);
0714: } else {
0715: MappingElement ref = (MappingElement) struct
0716: .getEffectiveMapping();
0717: if (ref != null && ref.isAbstract()) {
0718:
0719: // abstract inline treated as group
0720: SchemaMappingDetail detail = m_detailDirectory
0721: .getMappingDetail(ref);
0722: checkDependency(detail.getOtherName(),
0723: holder);
0724: AttributeGroupRefElement group = new AttributeGroupRefElement();
0725: group.setRef(detail.getOtherName());
0726: attrs.add(group);
0727:
0728: } else {
0729: throw new IllegalStateException(
0730: "Unsupported binding construct "
0731: + comp);
0732: }
0733: }
0734: } else {
0735: m_context.addError("Unsupported binding construct",
0736: comp);
0737: }
0738:
0739: } else {
0740: m_context.addError("Unsupported component", comp);
0741: }
0742: }
0743: }
0744:
0745: /**
0746: * Check if a container element of the binding represents complex content.
0747: *
0748: * @param cont
0749: * @return <code>true</code> if complex content, <code>false</code> if not
0750: */
0751: private static boolean isComplexContent(ContainerElementBase cont) {
0752: ArrayList conts = cont.getContentComponents();
0753: for (int i = 0; i < conts.size(); i++) {
0754: IComponent comp = (IComponent) conts.get(i);
0755: if (comp.hasName()) {
0756: return true;
0757: } else if (comp instanceof ContainerElementBase
0758: && isComplexContent((ContainerElementBase) comp)) {
0759: return true;
0760: }
0761: }
0762: return false;
0763: }
0764:
0765: /**
0766: * Build the complex type definition for a mapping.
0767: *
0768: * @param detail mapping detail
0769: * @param hold target schema for definition
0770: * @return constructed complex type
0771: */
0772: private ComplexTypeElement buildComplexType(
0773: SchemaMappingDetail detail, SchemaHolder hold) {
0774:
0775: // create the type and compositor
0776: ComplexTypeElement type = new ComplexTypeElement();
0777: MappingElement mapping = detail.getMapping();
0778: MappingElement base = detail.getExtensionBase();
0779: if (base == null) {
0780: if (detail.isGroup()) {
0781:
0782: // create type using references to group and/or attributeGroup
0783: SequenceElement seq = new SequenceElement();
0784: if (detail.hasChild()) {
0785: GroupRefElement gref = new GroupRefElement();
0786: gref.setRef(detail.getOtherName());
0787: seq.getParticleList().add(gref);
0788: }
0789: type.setContentDefinition(seq);
0790: if (detail.hasAttribute()) {
0791: AttributeGroupRefElement gref = new AttributeGroupRefElement();
0792: gref.setRef(detail.getOtherName());
0793: type.getAttributeList().add(gref);
0794: }
0795:
0796: } else {
0797:
0798: // create type directly
0799: type.setContentDefinition(buildCompositor(mapping, 0,
0800: false, hold));
0801: fillAttributes(mapping, 0, type.getAttributeList(),
0802: hold);
0803:
0804: }
0805: } else {
0806:
0807: // create type as extension of base type
0808: ComplexExtensionElement ext = new ComplexExtensionElement();
0809: SchemaMappingDetail basedet = m_detailDirectory
0810: .getMappingDetail(base);
0811: ext.setBase(basedet.getTypeName());
0812: ext.setContentDefinition(buildCompositor(mapping, 1, false,
0813: hold));
0814: if (isComplexContent(base) || isComplexContent(mapping)) {
0815: ComplexContentElement cont = new ComplexContentElement();
0816: cont.setDerivation(ext);
0817: type.setContentType(cont);
0818: } else {
0819: SimpleContentElement cont = new SimpleContentElement();
0820: cont.setDerivation(ext);
0821: type.setContentType(cont);
0822: }
0823:
0824: }
0825: return type;
0826: }
0827:
0828: /**
0829: * Add mapping to schema definitions. This generates the appropriate schema
0830: * representation for the mapping based on the detail flags, which may
0831: * include group and/or attributeGroup, complexType, and element
0832: * definitions.
0833: *
0834: * @param detail
0835: */
0836: private void addMapping(SchemaMappingDetail detail) {
0837:
0838: // get the documentation to be used for type
0839: IClass info = null;
0840: if (m_locator != null) {
0841: info = m_locator.getClassInfo(detail.getMapping()
0842: .getClassName());
0843: }
0844:
0845: // start by generating group/attributeGroup schema components
0846: MappingElement mapping = detail.getMapping();
0847: if (detail.isGroup()) {
0848: // TODO: extend base type for group/attributeGroup?
0849: QName qname = detail.getOtherName();
0850: SchemaHolder hold = findSchema(qname.getUri());
0851: if (detail.hasChild()) {
0852: GroupElement group = new GroupElement();
0853: group.setName(qname.getName());
0854: group.setDefinition(buildCompositor(mapping, 0, false,
0855: hold));
0856: addDocumentation(info, group);
0857: hold.getSchema().getTopLevelChildren().add(group);
0858: }
0859: if (detail.hasAttribute()) {
0860: AttributeGroupElement attgrp = new AttributeGroupElement();
0861: attgrp.setName(qname.getName());
0862: fillAttributes(mapping, 0, attgrp.getAttributeList(),
0863: hold);
0864: addDocumentation(info, attgrp);
0865: hold.getSchema().getTopLevelChildren().add(attgrp);
0866: }
0867: }
0868:
0869: // next generate the complex type definition
0870: if (detail.isType()) {
0871:
0872: // set up the basic definition structure
0873: QName qname = detail.getTypeName();
0874: SchemaHolder hold = findSchema(qname.getUri());
0875: ComplexTypeElement type = buildComplexType(detail, hold);
0876: type.setName(qname.getName());
0877: addDocumentation(info, type);
0878: hold.getSchema().getTopLevelChildren().add(type);
0879: }
0880:
0881: // finish by generating element definition
0882: if (detail.isElement()) {
0883: QName qname = detail.getOtherName();
0884: SchemaHolder hold = findSchema(qname.getUri());
0885: ElementElement elem = new ElementElement();
0886: elem.setName(qname.getName());
0887: elem.setSubstitutionGroup(detail.getSubstitution());
0888: if (detail.isType()) {
0889: elem.setType(detail.getTypeName());
0890: } else {
0891:
0892: // check for just an element wrapper around type reference
0893: MappingElement ext = detail.getExtensionBase();
0894: if (ext != null
0895: && mapping.getContentComponents().size() == 1) {
0896: elem.setType(ext.getTypeQName());
0897: } else {
0898:
0899: // add documentation to element which is not also a type
0900: addDocumentation(info, elem);
0901: elem.setInlineType(buildComplexType(detail, hold));
0902: }
0903: }
0904: hold.getSchema().getTopLevelChildren().add(elem);
0905: }
0906: }
0907:
0908: /**
0909: * Generate a list of schemas from a list of bindings. The two lists are not
0910: * necessarily in any particular relationship to each other.
0911: *
0912: * @param holders list of {@link BindingHolder}
0913: * @return schemas list of {@link SchemaHolder}
0914: */
0915: public ArrayList generate(ArrayList holders) {
0916:
0917: // start by scanning all bindings to build mapping and enum details
0918: m_detailDirectory.populate(holders);
0919:
0920: // process all the simple type definitions (formats or enums)
0921: Collection simples = m_detailDirectory.getSimpleDetails();
0922: for (Iterator iter = simples.iterator(); iter.hasNext();) {
0923: SchemaEnumDetail detail = (SchemaEnumDetail) iter.next();
0924: if (detail.isGlobal()) {
0925: ClassCustom custom = detail.getCustom();
0926: SimpleTypeElement type = buildSimpleType(custom
0927: .getClassInformation());
0928: type.setName(custom.getTypeName());
0929: SchemaHolder hold = findSchema(custom.getNamespace());
0930: hold.getSchema().getTopLevelChildren().add(type);
0931: m_simpleTypeMap.put(custom.getName(), custom
0932: .getTypeQName());
0933: }
0934: }
0935:
0936: // process all the mapping definitions from directory
0937: Collection mappings = m_detailDirectory.getComplexDetails();
0938: for (Iterator iter = mappings.iterator(); iter.hasNext();) {
0939: SchemaMappingDetail detail = (SchemaMappingDetail) iter
0940: .next();
0941: addMapping(detail);
0942: }
0943:
0944: // report any problems found in schema generation
0945: ArrayList probs = m_context.getProblems();
0946: boolean error = m_context.getErrorCount() > 0
0947: || m_context.getFatalCount() > 0;
0948: if (probs.size() > 0) {
0949: System.out.print(error ? "Errors" : "Warnings");
0950: System.out.println(" in generated binding:");
0951: for (int j = 0; j < probs.size(); j++) {
0952: ValidationProblem prob = (ValidationProblem) probs
0953: .get(j);
0954: System.out
0955: .print(prob.getSeverity() >= ValidationProblem.ERROR_LEVEL ? "Error: "
0956: : "Warning: ");
0957: System.out.println(prob.getDescription());
0958: }
0959: }
0960:
0961: // fix all references between schemas
0962: ArrayList schemas = new ArrayList(m_uriSchemaMap.values());
0963: for (int i = 0; i < schemas.size(); i++) {
0964: SchemaHolder hold = (SchemaHolder) schemas.get(i);
0965: hold.fixReferences();
0966: }
0967:
0968: // validate the schemas and report any problems
0969: org.jibx.schema.validation.ValidationContext vctx = new org.jibx.schema.validation.ValidationContext();
0970: for (int i = 0; i < schemas.size(); i++) {
0971: SchemaHolder holder = (SchemaHolder) schemas.get(i);
0972: String id = holder.getFileName();
0973: SchemaElement schema = holder.getSchema();
0974: schema.setResolver(new MemoryResolver(id));
0975: vctx.setSchema(id, schema);
0976: }
0977: TreeWalker wlkr = new TreeWalker(vctx, vctx);
0978: vctx.clearTraversed();
0979: for (int i = 0; i < schemas.size(); i++) {
0980: wlkr.walkSchema(
0981: ((SchemaHolder) schemas.get(i)).getSchema(),
0982: new PrevalidationVisitor(vctx));
0983: }
0984: vctx.clearTraversed();
0985: for (int i = 0; i < schemas.size(); i++) {
0986: wlkr.walkSchema(
0987: ((SchemaHolder) schemas.get(i)).getSchema(),
0988: new RegistrationVisitor(vctx));
0989: }
0990: vctx.clearTraversed();
0991: for (int i = 0; i < schemas.size(); i++) {
0992: wlkr.walkSchema(
0993: ((SchemaHolder) schemas.get(i)).getSchema(),
0994: new ValidationVisitor(vctx));
0995: }
0996: probs = vctx.getProblems();
0997: if (probs.size() > 0) {
0998: for (int j = 0; j < probs.size(); j++) {
0999: org.jibx.schema.validation.ValidationProblem prob = (org.jibx.schema.validation.ValidationProblem) probs
1000: .get(j);
1001: System.out
1002: .print(prob.getSeverity() >= ValidationProblem.ERROR_LEVEL ? "Error: "
1003: : "Warning: ");
1004: System.out.println(prob.getDescription());
1005: }
1006: }
1007: return schemas;
1008: }
1009:
1010: /**
1011: * Get details of schema handling of a mapping.
1012: *
1013: * @param map
1014: * @return mapping details
1015: */
1016: public SchemaMappingDetail getMappingDetail(MappingElement map) {
1017: return m_detailDirectory.forceMappingDetail(map);
1018: }
1019:
1020: private static class MemoryResolver implements ISchemaResolver {
1021: private final String m_id;
1022:
1023: public MemoryResolver(String id) {
1024: m_id = id;
1025: }
1026:
1027: public InputStream getContent() throws IOException {
1028: throw new IOException("Source not available");
1029: }
1030:
1031: public String getId() {
1032: return m_id;
1033: }
1034:
1035: public ISchemaResolver resolve(String loc) throws IOException {
1036: return new MemoryResolver(loc);
1037: }
1038: }
1039: }
|