/* * Copyright (c) 2007, Dennis M. Sosnoski All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following * disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of * JiBX nor the names of its contributors may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jibx.binding.generator; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.jibx.binding.model.BindingHolder; import org.jibx.binding.model.CollectionElement; import org.jibx.binding.model.ContainerElementBase; import org.jibx.binding.model.DocumentFormatter; import org.jibx.binding.model.IClass; import org.jibx.binding.model.IClassItem; import org.jibx.binding.model.IClassLocator; import org.jibx.binding.model.IComponent; import org.jibx.binding.model.MappingElement; import org.jibx.binding.model.StructureElement; import org.jibx.binding.model.StructureElementBase; import org.jibx.binding.model.TemplateElementBase; import org.jibx.binding.model.ValidationContext; import org.jibx.binding.model.ValidationProblem; import org.jibx.binding.model.ValueElement; import org.jibx.runtime.QName; import org.jibx.schema.ISchemaResolver; import org.jibx.schema.SchemaHolder; import org.jibx.schema.TreeWalker; import org.jibx.schema.elements.AllElement; import org.jibx.schema.elements.AnnotatedBase; import org.jibx.schema.elements.AnnotationElement; import org.jibx.schema.elements.AttributeElement; import org.jibx.schema.elements.AttributeGroupElement; import org.jibx.schema.elements.AttributeGroupRefElement; import org.jibx.schema.elements.ChoiceElement; import org.jibx.schema.elements.CommonCompositorDefinition; import org.jibx.schema.elements.ComplexContentElement; import org.jibx.schema.elements.ComplexExtensionElement; import org.jibx.schema.elements.ComplexTypeElement; import org.jibx.schema.elements.DocumentationElement; import org.jibx.schema.elements.ElementElement; import org.jibx.schema.elements.FilteredSegmentList; import org.jibx.schema.elements.GroupElement; import org.jibx.schema.elements.GroupRefElement; import org.jibx.schema.elements.SchemaElement; import org.jibx.schema.elements.SequenceElement; import org.jibx.schema.elements.SimpleContentElement; import org.jibx.schema.elements.SimpleExtensionElement; import org.jibx.schema.elements.SimpleRestrictionElement; import org.jibx.schema.elements.SimpleTypeElement; import org.jibx.schema.elements.FacetElement.Enumeration; import org.jibx.schema.types.Count; import org.jibx.schema.validation.PrevalidationVisitor; import org.jibx.schema.validation.RegistrationVisitor; import org.jibx.schema.validation.ValidationVisitor; import org.jibx.util.Types; import org.w3c.dom.Node; /** * Schema generator from binding definition. * * @author Dennis M. Sosnoski */ public class SchemaGenerator { /** Locator for class information (null if none). */ private final IClassLocator m_locator; /** Binding customization information. */ private final GlobalCustom m_custom; /** Document used for annotations (null if none). */ private final DocumentFormatter m_formatter; /** Map from fully-qualified class name to qualified simple type name. */ private final Map m_simpleTypeMap; /** Map from namespace to schema holder. */ private final Map m_uriSchemaMap; /** Validation context for bindings. */ private final ValidationContext m_context; /** Details for mappings and enum usages. */ private final SchemaDetailDirectory m_detailDirectory; /** * Constructor. * * @param loc locator for class information (null if none) * @param custom binding customization information (used for creating names as needed) */ public SchemaGenerator(IClassLocator loc, GlobalCustom custom) { m_locator = loc; if (loc == null) { m_formatter = null; } else { m_formatter = new DocumentFormatter(); } m_custom = custom; m_simpleTypeMap = new HashMap(); m_uriSchemaMap = new HashMap(); m_context = new ValidationContext(loc); m_detailDirectory = new SchemaDetailDirectory(loc, custom, m_context); } /** * Find the schema to be used for a particular namespace. If this is the first time a particular namespace was * requested, a new schema will be created for that namespace and returned, using a default file name. * * @param uri namespace URI (null if no namespace) * @return schema holder */ public SchemaHolder findSchema(String uri) { SchemaHolder hold = (SchemaHolder)m_uriSchemaMap.get(uri); if (hold == null) { hold = new SchemaHolder(uri); m_uriSchemaMap.put(uri, hold); String sname; if (uri == null) { sname = "nonamespace.xsd"; } else { sname = uri.substring(uri.lastIndexOf('/') + 1) + ".xsd"; } hold.setFileName(sname); } return hold; } /** * Get the qualified name of the simple type used for a component, if a named simple type. If this returns * null, the {@link #buildSimpleType(IComponent)} method needs to be able to construct the * appropriate simple type definition. * * @param comp * @return qualified type name, null if inline definition needed */ private QName getSimpleTypeQName(IComponent comp) { IClass type = comp.getType(); String tname = comp.getType().getName(); QName qname = Types.schemaType(tname); if (qname == null) { qname = (QName)m_simpleTypeMap.get(tname); if (qname == null) { if (!type.isSuperclass("java.lang.Enum")) { m_context.addWarning("No schema equivalent known for type - treating as string", comp); qname = Types.STRING_QNAME; } } } return qname; } /** * Add class-level documentation to a schema component. * * @param info * @param root */ private void addDocumentation(IClass info, AnnotatedBase root) { if (info != null) { List nodes = m_formatter.docToNodes(info.getJavaDoc()); if (nodes != null) { AnnotationElement anno = new AnnotationElement(); DocumentationElement doc = new DocumentationElement(); for (Iterator iter = nodes.iterator(); iter.hasNext();) { Node node = (Node)iter.next(); doc.addContent(node); } anno.getItemsList().add(doc); root.setAnnotation(anno); } } } /** * Set the documentation for a schema component matching a class member. * * @param elem * @param item */ private void setDocumentation(IClassItem item, AnnotatedBase elem) { List nodes = m_formatter.docToNodes(item.getJavaDoc()); if (nodes != null) { AnnotationElement anno = new AnnotationElement(); DocumentationElement doc = new DocumentationElement(); for (Iterator iter = nodes.iterator(); iter.hasNext();) { Node node = (Node)iter.next(); doc.addContent(node); } anno.getItemsList().add(doc); elem.setAnnotation(anno); } } /** * Add documentation for a particular field or property. * * @param struct * @param elem */ private void addItemDocumentation(StructureElementBase struct, AnnotatedBase elem) { IClassItem item = struct.getField(); if (item == null) { item = struct.getGet(); } if (item != null) { setDocumentation(item, elem); } } /** * Add documentation for a particular field or property. * * @param value * @param elem */ private void addItemDocumentation(ValueElement value, AnnotatedBase elem) { IClassItem item = value.getField(); if (item == null) { item = value.getGet(); } if (item != null) { setDocumentation(item, elem); } } /** * Create the simple type definition for a type. This is only used for cases where * {@link #getSimpleTypeQName(IComponent)} returns null. The current implementation only supports * Java 5 Enum types, but in the future should be extended to support <format>-type conversions with supplied * schema definitions. This code requires a Java 5+ JVM to execute, but uses reflection to allow this class to be * loaded on older JVMs. * * @param type * @return type definition */ private SimpleTypeElement buildSimpleType(IClass type) { SchemaEnumDetail detail = m_detailDirectory.getSimpleDetail(type.getName()); try { Class clas = type.loadClass(); Method valsmeth = clas.getMethod("values", (Class[])null); String totxtname = detail.getCustom().getEnumValueMethod(); if (totxtname == null) { totxtname = "name"; } Method namemeth = clas.getMethod(totxtname, (Class[])null); try { valsmeth.setAccessible(true); } catch (RuntimeException e) { /* deliberately empty */ } try { namemeth.setAccessible(true); } catch (RuntimeException e) { /* deliberately empty */ } Object[] values = (Object[])valsmeth.invoke(null, (Object[])null); SimpleTypeElement simple = new SimpleTypeElement(); SimpleRestrictionElement restr = new SimpleRestrictionElement(); restr.setBase(Types.STRING_QNAME); for (int i = 0; i < values.length; i++) { Enumeration enumel = new Enumeration(); enumel.setValue(namemeth.invoke(values[i], (Object[])null).toString()); restr.getFacetsList().add(enumel); } simple.setDerivation(restr); addDocumentation(detail.getCustom().getClassInformation(), simple); return simple; } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } /** * Convenience method to create the simple type definition for the type of a component. * * @param comp * @return type definition */ private SimpleTypeElement buildSimpleType(IComponent comp) { return buildSimpleType(comp.getType()); } /** * General object comparison method. Don't know why Sun hasn't seen fit to include this somewhere, but at least it's * easy to write (over and over again). * * @param a first object to be compared * @param b second object to be compared * @return true if both objects are null, or if a.equals(b); * false otherwise */ public static boolean isEqual(Object a, Object b) { return (a == null) ? b == null : a.equals(b); } /** * Check if a name references a different schema. * * @param qname name to be checked * @param hold schema holder * @return true if different, false if the same */ public static boolean isDifferentSchema(QName qname, SchemaHolder hold) { return qname != null && !isEqual(qname.getUri(), hold.getNamespace()) && !isEqual(qname.getUri(), Types.SCHEMA_NAMESPACE); } /** * Check for dependency on another schema. This creates an import for the target schema, and assigns a prefix for * referencing the target schema namespace. * * @param qname name referenced by this schema * @param hold schema holder */ public void checkDependency(QName qname, SchemaHolder hold) { if (isDifferentSchema(qname, hold)) { String uri = qname.getUri(); SchemaHolder tohold = findSchema(uri); hold.addReference(tohold); hold.getPrefix(uri); } } /** * Check for dependency between two schemas. This creates schema imports in both directions, and assigns a prefix * for referencing the target schema namespace from within the current schema. * * @param qname name referenced by this schema * @param hold schema holder */ private void checkBidirectionalDependency(QName qname, SchemaHolder hold) { if (isDifferentSchema(qname, hold)) { String uri = qname.getUri(); SchemaHolder tohold = findSchema(uri); hold.addReference(tohold); hold.getPrefix(uri); tohold.addReference(hold); } } /** * Build a schema element description from a binding content component. * * @param comp source component * @param repeat repeated element flag * @param hold * @return element */ private ElementElement buildElement(IComponent comp, boolean repeat, SchemaHolder hold) { // create the basic element structure ElementElement elem = new ElementElement(); if (repeat) { elem.setMinOccurs(Count.COUNT_ZERO); elem.setMaxOccurs(Count.COUNT_UNBOUNDED); } else if (comp.isOptional()) { elem.setMinOccurs(Count.COUNT_ZERO); } // set or create the element type definition boolean isref = false; if (comp instanceof ValueElement) { QName qname = getSimpleTypeQName(comp); if (qname == null) { elem.setTypeDefinition(buildSimpleType(comp)); } else { setElementType(qname, elem, hold); } addItemDocumentation((ValueElement)comp, elem); } else if (comp instanceof CollectionElement) { CollectionElement coll = (CollectionElement)comp; if (coll.children().size() > 0) { // collection with children, choice or sequence from order ComplexTypeElement type = new ComplexTypeElement(); if (coll.isOrdered()) { // ordered children go to sequence element, repeating SequenceElement seq = new SequenceElement(); type.setContentDefinition(seq); } else { // unordered children go to repeated choice element ChoiceElement choice = new ChoiceElement(); type.setContentDefinition(choice); } type.setContentDefinition(buildCompositor(coll, 0, true, hold)); elem.setTypeDefinition(type); } else { // empty collection, item-type must reference a concrete mapping String itype = coll.getItemTypeName(); TemplateElementBase ref = coll.getDefinitions().getSpecificTemplate(itype); if (ref instanceof MappingElement) { // item type with concrete mapping, make it an element SchemaMappingDetail detail = m_detailDirectory.getMappingDetail((MappingElement)ref); ComplexTypeElement type = new ComplexTypeElement(); SequenceElement seq = new SequenceElement(); type.setContentDefinition(seq); ElementElement item = new ElementElement(); setElementRef(detail.getOtherName(), item, hold); item.setMinOccurs(Count.COUNT_ZERO); item.setMaxOccurs(Count.COUNT_UNBOUNDED); seq.getParticleList().add(item); elem.setTypeDefinition(type); addItemDocumentation(coll, item); } else { // TODO: handle this with xs:any strict? m_context.addWarning("Handling not implemented for unspecified mapping", coll); } } } else { // must be a structure, check children StructureElement struct = (StructureElement)comp; if (struct.children().size() > 0) { // structure with children, choice or sequence from order ComplexTypeElement type = new ComplexTypeElement(); if (struct.isOrdered()) { // ordered children go to sequence element SequenceElement seq = new SequenceElement(); type.setContentDefinition(seq); } else { // unordered children go to repeated choice element ChoiceElement choice = new ChoiceElement(); type.setContentDefinition(choice); } type.setContentDefinition(buildCompositor(struct, 0, false, hold)); fillAttributes(struct, 0, type.getAttributeList(), hold); elem.setTypeDefinition(type); } else { // no children, must be a mapping reference MappingElement ref = (MappingElement)struct.getEffectiveMapping(); if (ref == null) { // TODO: handle this with xs:any strict? m_context.addWarning("Handling not implemented for unspecified mapping", struct); } else { // implicit mapping reference, find the handling SchemaMappingDetail detail = m_detailDirectory.getMappingDetail(ref); if (detail.isElement()) { // structure with concrete mapping setElementRef(detail.getOtherName(), elem, hold); isref = true; } else { // set element type to that constructed from mapping setElementType(detail.getTypeName(), elem, hold); } } } addItemDocumentation(struct, elem); } if (!isref) { elem.setName(comp.getName()); } return elem; } /** * Add a compositor as a particle in another compositor. If the compositor being added has only one particle, the * particle is directly added to the containing compositor. * * @param part compositor being added as a particle * @param comp target compositor for add */ private static void addCompositorPart(CommonCompositorDefinition part, CommonCompositorDefinition comp) { FilteredSegmentList parts = part.getParticleList(); Count maxo = part.getMaxOccurs(); Count mino = part.getMinOccurs(); if (parts.size() == 1 && (maxo == null || maxo.isEqual(1)) && (mino == null || mino.isEqual(1))) { comp.getParticleList().add(parts.get(0)); } else if (parts.size() > 1) { comp.getParticleList().add(part); } } /** * Build compositor for type definition. This creates and returns the appropriate form of compositor for the * container, populated with particles matching the child element components of the binding container. * * @param cont container element defining structure * @param offset offset for first child component to process * @param repeat repeated item flag * @param hold holder for schema under construction * @return constructed compositor */ private CommonCompositorDefinition buildCompositor(ContainerElementBase cont, int offset, boolean repeat, SchemaHolder hold) { // start with the appropriate type of compositor CommonCompositorDefinition cdef; if (cont.isChoice()) { // choice container goes directly to choice compositor cdef = new ChoiceElement(); } else if (cont.isOrdered()) { // ordered container goes directly to sequence compositor cdef = new SequenceElement(); } else if (repeat) { // unordered repeat treated as repeated choice compositor cdef = new ChoiceElement(); cdef.setMaxOccurs(Count.COUNT_UNBOUNDED); } else { // unordered non-repeat treated as all compositor // TODO: verify conditions for all cdef = new AllElement(); } // generate schema equivalents for content components of container ArrayList comps = cont.getContentComponents(); for (int i = offset; i < comps.size(); i++) { IComponent comp = (IComponent)comps.get(i); if (comp.hasName()) { // create element for named content component ElementElement element = buildElement(comp, repeat, hold); if (comp instanceof StructureElementBase) { addItemDocumentation((StructureElementBase)comp, element); } cdef.getParticleList().add(element); } else if (comp instanceof ContainerElementBase) { ContainerElementBase contain = (ContainerElementBase)comp; boolean iscoll = comp instanceof CollectionElement; if (contain.children().size() > 0) { // no element name, but children; handle with recursive call CommonCompositorDefinition part = buildCompositor(contain, 0, iscoll, hold); addCompositorPart(part, cdef); } else if (iscoll) { // collection without a wrapper element CollectionElement coll = (CollectionElement)comp; String itype = coll.getItemTypeName(); TemplateElementBase ref = coll.getDefinitions().getSpecificTemplate(itype); if (ref instanceof MappingElement) { // item type with concrete mapping, make it an element SchemaMappingDetail detail = m_detailDirectory.getMappingDetail((MappingElement)ref); ElementElement item = new ElementElement(); QName oname = detail.getOtherName(); setElementRef(oname, item, hold); item.setMinOccurs(Count.COUNT_ZERO); item.setMaxOccurs(Count.COUNT_UNBOUNDED); addItemDocumentation(coll, item); cdef.getParticleList().add(item); } else { // TODO: handle this with xs:any strict? m_context.addWarning("Handling not implemented for unspecified mapping", coll); } } else if (comp instanceof StructureElement) { // no children, must be mapping reference StructureElement struct = (StructureElement)comp; MappingElement ref = (MappingElement)struct.getEffectiveMapping(); if (ref == null) { // TODO: handle this with xs:any strict? m_context.addWarning("Handling not implemented for unspecified mapping", struct); } else { // handle mapping reference based on form and name use SchemaMappingDetail detail = m_detailDirectory.getMappingDetail(ref); if (ref.isAbstract()) { // abstract inline treated as group GroupRefElement group = new GroupRefElement(); setGroupRef(detail.getOtherName(), group, hold); if (comp.isOptional()) { group.setMinOccurs(Count.COUNT_ZERO); } cdef.getParticleList().add(group); } else { // concrete treated as element reference ElementElement elem = new ElementElement(); setElementRef(detail.getOtherName(), elem, hold); if (comp.isOptional()) { elem.setMinOccurs(Count.COUNT_ZERO); } addItemDocumentation(struct, elem); cdef.getParticleList().add(elem); } } } else { m_context.addError("Unsupported binding construct", comp); } } else { m_context.addError("Unsupported component", comp); } } // simplify by eliminating this level if only child a compositor if ((cont.isOrdered() || cont.isChoice()) && cdef.getParticleList().size() == 1) { Object child = cdef.getParticleList().get(0); if (child instanceof CommonCompositorDefinition) { cdef = (CommonCompositorDefinition)child; } } return cdef; } /** * Set group reference, adding schema reference if necessary. * * @param qname reference name * @param group * @param hold schema holder */ public void setGroupRef(QName qname, AttributeGroupRefElement group, SchemaHolder hold) { checkDependency(qname, hold); group.setRef(qname); } /** * Set group reference, adding schema reference if necessary. * * @param qname reference name * @param group * @param hold schema holder */ public void setGroupRef(QName qname, GroupRefElement group, SchemaHolder hold) { checkDependency(qname, hold); group.setRef(qname); } /** * Set element reference, adding schema reference if necessary. * * @param qname reference name * @param elem * @param hold schema holder */ public void setElementRef(QName qname, ElementElement elem, SchemaHolder hold) { checkDependency(qname, hold); elem.setRef(qname); } /** * Set attribute type, adding schema reference if necessary. * * @param qname type name * @param elem * @param hold schema holder */ public void setAttributeType(QName qname, AttributeElement elem, SchemaHolder hold) { elem.setType(qname); checkDependency(qname, hold); } /** * Set element type, adding schema reference if necessary. * * @param qname type name * @param elem * @param hold schema holder */ public void setElementType(QName qname, ElementElement elem, SchemaHolder hold) { elem.setType(qname); checkDependency(qname, hold); } /** * Set element substitution group, adding schema references in each direction if necessary. * * @param qname substitution group element name * @param elem * @param hold schema holder */ public void setSubstitutionGroup(QName qname, ElementElement elem, SchemaHolder hold) { elem.setSubstitutionGroup(qname); checkBidirectionalDependency(qname, hold); } /** * Build attributes as part of complex type definition. * * @param cont container element defining structure * @param offset offset for first child component to process * @param attrs complex type attribute list * @param hold holder for schema under construction */ private void fillAttributes(ContainerElementBase cont, int offset, AbstractList attrs, SchemaHolder hold) { // add child attribute components ArrayList comps = cont.getAttributeComponents(); for (int i = 0; i < comps.size(); i++) { IComponent comp = (IComponent)comps.get(i); if (comp instanceof ValueElement) { // simple attribute definition AttributeElement attr = new AttributeElement(); attr.setName(comp.getName()); QName qname = getSimpleTypeQName(comp); if (qname == null) { attr.setTypeDefinition(buildSimpleType(comp)); } else { setAttributeType(qname, attr, hold); } if (!comp.isOptional()) { attr.setUse(AttributeElement.REQUIRED_USE); } addItemDocumentation((ValueElement)comp, attr); attrs.add(attr); } else if (comp instanceof ContainerElementBase) { ContainerElementBase contain = (ContainerElementBase)comp; if (contain.children().size() > 0) { // handle children with recursive call fillAttributes(contain, 0, attrs, hold); } else if (comp instanceof StructureElement) { // no children, must be mapping reference StructureElement struct = (StructureElement)comp; if (struct.isOptional()) { m_context.addError("No schema equivalent for optional abstract mapping with attributes", comp); } else { MappingElement ref = (MappingElement)struct.getEffectiveMapping(); if (ref != null && ref.isAbstract()) { // abstract inline treated as group SchemaMappingDetail detail = m_detailDirectory.getMappingDetail(ref); AttributeGroupRefElement group = new AttributeGroupRefElement(); setGroupRef(detail.getOtherName(), group, hold); attrs.add(group); } else { throw new IllegalStateException("Unsupported binding construct " + comp); } } } else { m_context.addError("Unsupported binding construct", comp); } } else { m_context.addError("Unsupported component", comp); } } } /** * Check if a container element of the binding represents complex content. * * @param cont * @return true if complex content, false if not */ private static boolean isComplexContent(ContainerElementBase cont) { ArrayList contents = cont.getContentComponents(); for (int i = 0; i < contents.size(); i++) { Object item = contents.get(i); if (item instanceof IComponent && ((IComponent)item).hasName()) { return true; } else if (item instanceof ContainerElementBase && isComplexContent((ContainerElementBase)item)) { return true; } } return false; } /** * Build the complex type definition for a mapping. * * @param detail mapping detail * @param hold target schema for definition * @return constructed complex type */ private ComplexTypeElement buildComplexType(SchemaMappingDetail detail, SchemaHolder hold) { // create the type and compositor ComplexTypeElement type = new ComplexTypeElement(); MappingElement mapping = detail.getMapping(); MappingElement base = detail.getExtensionBase(); if (base == null) { if (detail.isGroup()) { // create type using references to group and/or attributeGroup SequenceElement seq = new SequenceElement(); if (detail.hasChild()) { GroupRefElement gref = new GroupRefElement(); setGroupRef(detail.getOtherName(), gref, hold); seq.getParticleList().add(gref); } type.setContentDefinition(seq); if (detail.hasAttribute()) { AttributeGroupRefElement gref = new AttributeGroupRefElement(); setGroupRef(detail.getOtherName(), gref, hold); type.getAttributeList().add(gref); } } else { // create type directly type.setContentDefinition(buildCompositor(mapping, 0, false, hold)); fillAttributes(mapping, 0, type.getAttributeList(), hold); } } else { // create type as extension of base type SchemaMappingDetail basedet = m_detailDirectory.getMappingDetail(base); if (isComplexContent(base) || isComplexContent(mapping)) { // complex extension with complex content ComplexExtensionElement ext = new ComplexExtensionElement(); setComplexExtensionBase(basedet.getTypeName(), ext, hold); CommonCompositorDefinition comp = buildCompositor(mapping, 1, false, hold); if (comp.getParticleList().size() > 0) { ext.setContentDefinition(comp); } fillAttributes(mapping, 0, ext.getAttributeList(), hold); ComplexContentElement cont = new ComplexContentElement(); cont.setDerivation(ext); type.setContentType(cont); } else { // simple extension with simple content SimpleExtensionElement ext = new SimpleExtensionElement(); setSimpleExtensionBase(basedet.getTypeName(), ext, hold); fillAttributes(mapping, 0, ext.getAttributeList(), hold); SimpleContentElement cont = new SimpleContentElement(); cont.setDerivation(ext); type.setContentType(cont); } } return type; } /** * Set the base for a complex extension type. * * @param qname * @param ext * @param hold */ private void setComplexExtensionBase(QName qname, ComplexExtensionElement ext, SchemaHolder hold) { ext.setBase(qname); checkBidirectionalDependency(qname, hold); } /** * Set the base for a simple extension type. * * @param qname * @param ext * @param hold */ private void setSimpleExtensionBase(QName qname, SimpleExtensionElement ext, SchemaHolder hold) { ext.setBase(qname); checkBidirectionalDependency(qname, hold); } /** * Add mapping to schema definitions. This generates the appropriate schema representation for the mapping based on * the detail flags, which may include group and/or attributeGroup, complexType, and element definitions. * * @param detail */ private void addMapping(SchemaMappingDetail detail) { // get the documentation to be used for type IClass info = null; if (m_locator != null) { info = m_locator.getClassInfo(detail.getMapping().getClassName()); } // start by generating group/attributeGroup schema components MappingElement mapping = detail.getMapping(); if (detail.isGroup()) { // TODO: extend base type for group/attributeGroup? QName qname = detail.getOtherName(); SchemaHolder hold = findSchema(qname.getUri()); if (detail.hasChild()) { GroupElement group = new GroupElement(); group.setName(qname.getName()); group.setDefinition(buildCompositor(mapping, 0, false, hold)); addDocumentation(info, group); hold.getSchema().getTopLevelChildren().add(group); } if (detail.hasAttribute()) { AttributeGroupElement attgrp = new AttributeGroupElement(); attgrp.setName(qname.getName()); fillAttributes(mapping, 0, attgrp.getAttributeList(), hold); addDocumentation(info, attgrp); hold.getSchema().getTopLevelChildren().add(attgrp); } } // next generate the complex type definition if (detail.isType()) { // set up the basic definition structure QName qname = detail.getTypeName(); SchemaHolder hold = findSchema(qname.getUri()); ComplexTypeElement type = buildComplexType(detail, hold); type.setName(qname.getName()); addDocumentation(info, type); hold.getSchema().getTopLevelChildren().add(type); } // finish by generating element definition if (detail.isElement()) { QName qname = detail.getOtherName(); SchemaHolder hold = findSchema(qname.getUri()); ElementElement elem = new ElementElement(); elem.setName(qname.getName()); setSubstitutionGroup(detail.getSubstitution(), elem, hold); if (detail.isType()) { setElementType(detail.getTypeName(), elem, hold); } else { // check for just an element wrapper around type reference MappingElement ext = detail.getExtensionBase(); if (ext != null && !detail.hasAttribute() && mapping.getContentComponents().size() == 1) { setElementType(ext.getTypeQName(), elem, hold); } else { // add documentation to element which is not also a type addDocumentation(info, elem); elem.setTypeDefinition(buildComplexType(detail, hold)); } } hold.getSchema().getTopLevelChildren().add(elem); } } /** * Generate a list of schemas from a list of bindings. The two lists are not necessarily in any particular * relationship to each other. * * @param holders list of {@link BindingHolder} * @return schemas list of {@link SchemaHolder} */ public ArrayList generate(ArrayList holders) { // start by scanning all bindings to build mapping and enum details m_detailDirectory.populate(holders); // process all the simple type definitions (formats or enums) Collection simples = m_detailDirectory.getSimpleDetails(); for (Iterator iter = simples.iterator(); iter.hasNext();) { SchemaEnumDetail detail = (SchemaEnumDetail)iter.next(); if (detail.isGlobal()) { ClassCustom custom = detail.getCustom(); SimpleTypeElement type = buildSimpleType(custom.getClassInformation()); type.setName(custom.getTypeName()); SchemaHolder hold = findSchema(custom.getNamespace()); hold.getSchema().getTopLevelChildren().add(type); m_simpleTypeMap.put(custom.getName(), custom.getTypeQName()); } } // process all the mapping definitions from directory Collection mappings = m_detailDirectory.getComplexDetails(); for (Iterator iter = mappings.iterator(); iter.hasNext();) { SchemaMappingDetail detail = (SchemaMappingDetail)iter.next(); addMapping(detail); } // report any problems found in schema generation ArrayList probs = m_context.getProblems(); boolean error = m_context.getErrorCount() > 0 || m_context.getFatalCount() > 0; if (probs.size() > 0) { System.out.print(error ? "Errors" : "Warnings"); System.out.println(" in generated binding:"); for (int j = 0; j < probs.size(); j++) { ValidationProblem prob = (ValidationProblem)probs.get(j); System.out.print(prob.getSeverity() >= ValidationProblem.ERROR_LEVEL ? "Error: " : "Warning: "); System.out.println(prob.getDescription()); } } // fix all references between schemas ArrayList schemas = new ArrayList(m_uriSchemaMap.values()); for (int i = 0; i < schemas.size(); i++) { SchemaHolder hold = (SchemaHolder)schemas.get(i); hold.finish(); } // validate the schemas and report any problems org.jibx.schema.validation.ValidationContext vctx = new org.jibx.schema.validation.ValidationContext(); for (int i = 0; i < schemas.size(); i++) { SchemaHolder holder = (SchemaHolder)schemas.get(i); String id = holder.getFileName(); SchemaElement schema = holder.getSchema(); schema.setResolver(new MemoryResolver(id)); vctx.setSchema(id, schema); } TreeWalker wlkr = new TreeWalker(vctx, vctx); vctx.clearTraversed(); for (int i = 0; i < schemas.size(); i++) { wlkr.walkSchema(((SchemaHolder)schemas.get(i)).getSchema(), new PrevalidationVisitor(vctx)); } vctx.clearTraversed(); for (int i = 0; i < schemas.size(); i++) { wlkr.walkSchema(((SchemaHolder)schemas.get(i)).getSchema(), new RegistrationVisitor(vctx)); } vctx.clearTraversed(); for (int i = 0; i < schemas.size(); i++) { wlkr.walkSchema(((SchemaHolder)schemas.get(i)).getSchema(), new ValidationVisitor(vctx)); } probs = vctx.getProblems(); if (probs.size() > 0) { for (int j = 0; j < probs.size(); j++) { org.jibx.schema.validation.ValidationProblem prob = (org.jibx.schema.validation.ValidationProblem)probs.get(j); System.out.print(prob.getSeverity() >= ValidationProblem.ERROR_LEVEL ? "Error: " : "Warning: "); System.out.println(prob.getDescription()); } } return schemas; } /** * Get details of schema handling of a mapping. * * @param map * @return mapping details */ public SchemaMappingDetail getMappingDetail(MappingElement map) { return m_detailDirectory.forceMappingDetail(map); } private static class MemoryResolver implements ISchemaResolver { private final String m_id; public MemoryResolver(String id) { m_id = id; } public InputStream getContent() throws IOException { throw new IOException("Source not available"); } public String getName() { return m_id; } public String getId() { return m_id; } public ISchemaResolver resolve(String loc) throws IOException { return new MemoryResolver(loc); } } }