com.init.octo.schema.XSDSchema.java Source code

Java tutorial

Introduction

Here is the source code for com.init.octo.schema.XSDSchema.java

Source

/**
 *  This class represents an XSD schema.  It controls the building of the schema
 *  representation and population of elements in an XML structure.
 * 
 *  Copyright (C) 2007  Stephen Harding
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *  
 *  Please send inquiries to; steve@inverse2.com
 *
 * $Revision: 1.4 $
 * 
 * $Log: XSDSchema.java,v $
 * Revision 1.4  2008/09/01 11:05:22  stevewdh
 * Added a couple of helper methods to get the schema's root name and xsd element.
 *
 * Revision 1.3  2008/07/01 17:26:46  stevewdh
 * Changed logging so that user can specify the type they want (Java, Log4J or HTML).
 *
 * Revision 1.2  2008/03/05 10:47:27  stevewdh
 * *** empty log message ***
 *
 * Revision 1.1  2007/10/04 11:06:48  stevewdh
 * *** empty log message ***
 *
 * Revision 1.3  2007/09/30 13:09:05  stephen harding
 * Added contact details to license header.
 *
 * Revision 1.2  2007/09/22 16:38:43  stephen harding
 * No changes.
 *
 * Revision 1.1  2007/09/15 16:09:05  stephen harding
 * Added header.
 *
 *
 */

package com.init.octo.schema;

import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Properties;

import org.apache.ws.commons.schema.XmlSchemaException;
import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.input.SAXBuilder;

import com.init.octo.util.Logger;

public class XSDSchema extends XMLType {

    public static String SCHEMA = "schema";
    public static String ELEMENT = "element";
    public static String COMPLEXTYPE = "complexType";
    public static String ANNOTATION = "annotation";
    public static String SIMPLETYPE = "simpleType";
    public static String RESTRICTION = "restriction";
    public static String EXTENSION = "extension";
    public static String LIST = "list";
    public static String SIMPLECONTENT = "simpleContent";
    public static String COMPLEXCONTENT = "complexContent";
    public static String GROUP = "group";
    public static String ALL = "all";
    public static String CHOICE = "choice";
    public static String SEQUENCE = "sequence";
    public static String ATTRIBUTE = "attribute";
    public static String ATTRIBUTEGROUP = "attributeGroup";
    public static String IMPORT = "import";
    public static String INCLUDE = "include";

    public static String ID_ATT = "id";
    public static String NAME_ATT = "name";
    public static String REF_ATT = "ref";
    public static String TYPE_ATT = "type";
    public static String ABSTRACT_ATT = "abstract";
    public static String MIXED_ATT = "mixed";
    public static String BLOCK_ATT = "block";
    public static String FINAL_ATT = "final";
    public static String DEFAULT_ATT = "default";
    public static String FORM_ATT = "form";
    public static String USE_ATT = "use";
    public static String FIXED_ATT = "fixed";
    public static String MAXOCCURS_ATT = "maxOccurs";
    public static String MINOCCURS_ATT = "minOccurs";
    public static String ATTFORMDEF_ATT = "attributeFormDefault";
    public static String BLOCKDEF_ATT = "blockDefault";
    public static String ELFORMDEF_ATT = "elementFormDefault";
    public static String FINALDEF_ATT = "final Default";
    public static String TARGNAMSP_ATT = "targetNamespace";
    public static String NAMESPACE_ATT = "xmlns";
    public static String VERSION_ATT = "version";
    public static String XMLLANG_ATT = "xml:lang";
    public static String SCHEMALOC_ATT = "schemaLocation";
    public static String BASE_ATT = "base";

    private Logger log; // logging object
    private XSDCache cache; // cache of shared elements...
    private XSDElement root = null; // root element of the XML structure
    private Map<String, XSDElement> topLevelElements;//XML elements that are defined at the top level
    private Map<String, XSDElementTypeComplex> topLevelTypes; //type declarations at the top level
    private int indent;
    private Document buildDocument; // document being built using the schema...
    private Element[] elementPath; // the path of elements that created during an add element action
    private XSDElement[] schemaPath; // the path through the schema that was used creating an element

    private boolean schemaDefined; // has an XSD schema been defined
    private boolean firstCall;
    private SAXBuilder builder; // object to build JDOM documents
    private String schemaRoot;
    private List<String> schemaFileNames;

    private Map<String, Boolean> newTrigger = new HashMap<String, Boolean>(); // list of elements that should always be created as new

    /** Attributes of the schema element.. **/
    private String targetNamespace;

    /*
    private String          id;
    private String          attributeFormDefault;
    private String          blockDefault;
    private String          elementFormDefault;
    private String          finalDefault;
        
    private String          version;
    private String          xml_lang;
    */
    /**
     * Constructor
     */

    public XSDSchema(Properties properties, int indent) {
        this.cache = new XSDCache();
        this.log = Logger.getLogger(XSDSchema.class.getName());
        this.root = null;
        this.indent = indent;
        this.schemaDefined = false;
        this.builder = new SAXBuilder();
    }

    /**
     * This method builds an internal structure of the XML schema
    * @throws Exception 
     */
    public boolean build(String schemaFileName, Document document) throws Exception {
        log.debug("" + indent + ": " + "Building representation of the XML schema");

        topLevelElements = new HashMap<String, XSDElement>();
        topLevelTypes = new HashMap<String, XSDElementTypeComplex>();
        schemaFileNames = new ArrayList<String>();

        schemaFileNames.add(schemaFileName);

        /** Add the document to the list of documents referenced in the schema... **/

        cache.putSchema("main", document);

        // build 1
        if (privateBuild(schemaFileName, document, true) == false) {
            log.fatal("Error building the schema");
            return (false);
        }

        cache.clearSchemaCache();
        cache.putSchema("main", document);

        // build 2 (why?)
        if (privateBuild(schemaFileName, document, true) == false) {
            log.fatal("Error building the schema");
            return (false);
        }

        if (null != schemaRoot) {
            root = topLevelElements.get(schemaRoot);
        } else {
            // no root specified, just choose the first one
            root = new XSDElement();
            root.setElementType((XSDElementTypeComplex) topLevelElements.entrySet().toArray()[0]);
            root.setName("root");
        }
        log.debug("" + indent + ": " + "XML Schema representation built successfully");

        schemaDefined = true;
        return (true);
    }

    private boolean privateBuild(String schemaFileName, Document document, boolean topLevel) throws Exception {
        int schemaFileNamesIdx = 0;

        String attributeValue;
        XSDElement thisDocRoot;

        // get the root element of the XML - this should be <schema>, or we wont process the document 

        Element xsdRoot = document.getRootElement();
        String elementName = xsdRoot.getName();

        if (elementName.equals(SCHEMA) == false) {
            throw new Exception("The root element of the XML document is not <" + SCHEMA + ">");
        }

        id = xsdRoot.getAttributeValue(XSDSchema.ID_ATT);
        targetNamespace = xsdRoot.getAttributeValue(XSDSchema.TARGNAMSP_ATT);

        try {
            cache.pushNamespaces(schemaFileName);
        } catch (Exception ex) {
            log.fatal("Exception caching namespaces: " + ex.toString());
            return (false);
        }

        /** Go through the document and find any include or import elements... **/

        for (Element element : xsdRoot.getChildren()) {
            elementName = element.getName();

            if (elementName.equals(IMPORT) || elementName.equals(INCLUDE)) {
                attributeValue = element.getAttributeValue(SCHEMALOC_ATT);
                if (cache.schemaCached(attributeValue) == true) {
                    log.debug("Schema already cached... ignoring");
                    continue;
                }

                log.debug(elementName + " schema [" + attributeValue + "]");

                String importSchemaName = null;

                /* If the schema is relative to the current schema then work out the absolute name... */

                if (attributeValue.startsWith("..") == true) {

                    String[] currentSchemaPath = ((String) schemaFileNames.get(schemaFileNamesIdx))
                            .split("[/\\\\]");
                    String[] importSchemaFile = attributeValue.split("[/\\\\]");
                    int idx = 0;
                    int n = 0;
                    StringBuffer realImportName = new StringBuffer();

                    while (importSchemaFile[n].equals("..")) {
                        idx++;
                        n++;
                    }

                    for (n = 0; n < (currentSchemaPath.length - 1) - idx; n++) {
                        realImportName.append(currentSchemaPath[n]);
                        realImportName.append(File.separator);
                    }

                    for (n = idx; n < importSchemaFile.length; n++) {
                        realImportName.append(importSchemaFile[n]);
                        if (n < importSchemaFile.length - 1) {
                            realImportName.append(File.separator);
                        }
                    }

                    importSchemaName = realImportName.toString();
                } else { /* schema in current directory... */

                    File sf = new File((String) schemaFileNames.get(schemaFileNamesIdx));
                    String dirName = sf.getParent();
                    importSchemaName = dirName + File.separator + attributeValue;
                }

                Document doc = builder.build(new FileReader(importSchemaName));

                schemaFileNames.add(importSchemaName);
                schemaFileNamesIdx++;

                cache.putSchema(attributeValue, document);
                privateBuild(importSchemaName, doc, false);

                schemaFileNames.remove(schemaFileNamesIdx);
                schemaFileNamesIdx--;
            }

        }

        // do it twice... so that all predefined types are picked up, no matter what order they are defined in...
        // @todo I know it is clunky, and when I get time I will make it scan for types (and imports etc!) first then build the definition.....

        for (int x = 0; x < 2; x++) {

            /** iterate all of the child elements of schema **/

            for (Element element : xsdRoot.getChildren()) {
                elementName = element.getName();
                log.debug("" + indent + ": " + "Element <" + elementName + ">");

                if (elementName.equals(ELEMENT)) {
                    thisDocRoot = new XSDElement(indent);
                    if (thisDocRoot.build(element, cache, "") != true) {
                        log.fatal("Error processing the schema");
                        return (false);
                    }

                    if (topLevel == true) {
                        topLevelElements.put(elementName, thisDocRoot);
                    }
                } else if (elementName.equals(SIMPLETYPE)) {
                } else if (elementName.equals(COMPLEXTYPE)) {
                    XSDElementTypeComplex complexType = new XSDElementTypeComplex(indent);
                    if (complexType.build(element, cache, "") != true) {
                        log.fatal("Error building a complex type");
                        throw new Exception("Error building a complex type");
                    }
                    log.debug("Adding type <" + complexType.getName() + "> to type cache");
                    cache.putType(complexType.getName(), complexType);

                    if (topLevel == true) {
                        topLevelTypes.put(complexType.getName(), complexType);
                    }

                } else if (elementName.equals(GROUP)) {
                    XSDGroupType group = new XSDElementGroup(indent);
                    if (group.build(element, cache, "") != true) {
                        log.fatal("Error building a group");
                        return (false);
                    }
                } else if (elementName.equals(ATTRIBUTEGROUP)) {
                    XSDAttributeGroup attGroup = new XSDAttributeGroup();
                    if (attGroup.build(element, cache, "") != true) {
                        log.warn("Error building an attribute group");
                        continue;
                    }
                } else if (elementName.equals(ATTRIBUTE)) {
                    XSDAttribute attribute = new XSDAttribute();
                    if (attribute.build(element, cache, "") != true) {
                        log.warn("Error building an attribute object");
                        continue;
                    }
                } else if (elementName.equals(IMPORT) || elementName.equals(INCLUDE)) {
                    // do nothing - these have already been processed...
                } else {
                    log.debug("" + indent + ": " + "Unexpected element <" + elementName + "> found and ignored");
                }
            } // end for all child elements of the schema root

        }

        cache.popNamespaces();

        return (true);

    } // end build

    public XSDCache getCache() {
        return (cache);
    }

    /**
     * This method shows the internal representation of the schema
     */

    public void show(boolean htmlForm, String rootName) {
        if (root != null) {

            if (rootName != null) {
                root.setName(rootName);
            }

            root.show(0, false, htmlForm);
        } else {
            log.info("No root element defined so can't show schema.");
            log.info("Top level types = " + topLevelTypes.size());
        }
    }

    /**
     * This method returns a list of all of the elements and attributes in the schema, along with their indent...
     */

    public List<SchemaItem> getListOfSchemaItems() {

        List<SchemaItem> list = new LinkedList<SchemaItem>();

        if (root != null) {
            root.getListOfSchemaItems(0, false, list);
        }

        return (list);
    }

    public void initDoc() {
        buildDocument = new Document();

        if (schemaDefined == false) {
            root = null;
        }

        if (root != null) {
            buildDocument.setRootElement(new Element(schemaRoot != null ? schemaRoot : root.getName()));
        } else {
            /* else no proper root element was defined in the schema and we make a duff one to stop */
            /* a future exception...                                                                */
            buildDocument.setRootElement(new Element(schemaRoot != null ? schemaRoot : "root"));
        }

        elementPath = new Element[100];
        schemaPath = new XSDElement[100];
        // newTrigger     = new String[100];
        newTrigger = new HashMap<String, Boolean>();

        firstCall = true;
    }

    public void setSchemaRoot(String schemaRoot) {
        this.schemaRoot = schemaRoot;
    }

    /**
     * show me where the money is
     * @param elementSpec
     * @param value
     * @param appendCommand
     * @throws Exception
     */
    public void populateDoc(String elementSpec, String value, String appendCommand) throws Exception {
        boolean valueIsNull = (null == value);

        String[] path = elementSpec.split("\\.");

        log.debug(elementSpec + " contains " + path.length + " elements");
        for (int q = 0; q < path.length; q++) {
            log.debug("path[" + q + "] = " + path[q]);
        }

        if (path.length == 0) {
            log.warn("Zero length element spec [" + elementSpec + "]?");
            return;
        }

        if (schemaDefined == false && firstCall == true) {
            if (schemaRoot != null) {
                /* use the element defined in the xmlselect... */
                log.debug("Not using a schema - so creating the root element with the first specified element ["
                        + schemaRoot + "]");
                buildDocument.setRootElement(new Element(schemaRoot));
            } else {
                log.debug("Not using a schema - so creating the root element with the first specified element ["
                        + path[0] + "]");
                log.debug("element spec: [" + elementSpec + "]");
                buildDocument.setRootElement(new Element(path[0]));
            }
            firstCall = false;
        }

        /** Get the root element from the document and the schema... **/

        elementPath[0] = buildDocument.getRootElement();
        schemaPath[0] = root;

        if (schemaDefined && elementPath[0].getName().equals(path[0]) == false) {
            log.warn("The first element [" + path[0] + " is not the root element");
            return;
        }

        /** Follow the element spec down the document and schema tree... **/

        int idx = 1;
        boolean elementCreated = false;
        boolean xToManyElement = false;
        XSDElement schemaElement = null;

        while (idx < path.length) {

            xToManyElement = false;

            if (path[idx].startsWith("@")) {
                /** ... attribute specified... **/
                idx++;
                break;
            } else {
                String childName = path[idx];
                Element parentElement = elementPath[idx - 1];
                Element docElement = findElement(parentElement, childName);

                if (schemaDefined == true) {

                    schemaElement = schemaPath[idx - 1].getChild(path[idx]);

                    if (schemaElement == null) {
                        log.warn("The element [" + path[idx] + "] is not in the schema...");
                        return;
                    }

                    schemaPath[idx] = schemaElement;
                }

                if (docElement == null) {
                    log.debug("Element [" + path[idx] + "] does not exist - will create it");
                    docElement = new Element(path[idx]);
                    elementPath[idx] = docElement;

                    addElementToDocument(schemaPath[idx - 1], elementPath[idx - 1], docElement);

                    /** Clear any trigger new elements... **/
                    StringBuffer tmp = new StringBuffer();
                    for (int j = 0; j <= idx; j++) {
                        if (tmp.length() != 0) {
                            tmp.append(".");
                        }
                        tmp.append(path[j]);
                    }
                    triggerNew(tmp.toString());

                    elementCreated = true;
                } else {
                    log.debug("Element [" + path[idx] + "] does exist... checking if we should "
                            + "create a new one...");

                    /** Check for any throw new triggers... **/
                    StringBuffer tmp = new StringBuffer();
                    for (int j = 0; j <= idx; j++) {
                        if (tmp.length() != 0) {
                            tmp.append(".");
                        }
                        tmp.append(path[j]);
                    }

                    if (triggerNew(tmp.toString()) == true) {
                        log.debug("New element trigger detected");
                        /** Create the element... **/
                        docElement = new Element(path[idx]);
                        elementPath[idx] = docElement;

                        addElementToDocument(schemaPath[idx - 1], elementPath[idx - 1], docElement);
                    } else if (schemaDefined == true && schemaElement.xToMany() == true) {

                        /*
                         * Flag that a 0..m element has been found... if this is
                         * the element that we are going to populate (the last
                         * one in the while loop) then we will create a new one
                         * if it has not been created a fresh already. This
                         * enables, for example, address lines that come from a
                         * single database row to be mapped into a repeating XML
                         * element. Without this code the trigger mechanism
                         * would not create the new XML element and the address
                         * lines would be overwritten...
                         */
                        xToManyElement = true;

                    } else {
                        /**
                         * Element is 0..1 so we will just follow the tree
                         * down...
                         **/
                        log.debug("X..1 cardinality... just keep going...");
                    }

                }

                elementPath[idx] = docElement;

            }

            idx++;

        } // end while following the tree down...

        idx--;

        if (path[idx].startsWith("@")) {
            /** Set the attribute... **/
            if (valueIsNull == false) {
                log.debug("Setting attribute [" + path[idx] + "] (on " + path[idx - 1] + ") to [" + value + "]");
                elementPath[idx - 1].setAttribute(path[idx].substring(1, path[idx].length()), value);
            }
        } else {
            /** Set the element... **/

            boolean appendValid = false;
            String separatorChar = null;
            String[] appendParts;

            if (appendCommand != null && appendCommand.equals("") == false) {

                appendParts = appendCommand.split("[()]");

                if (appendParts.length != 2) {
                    throw new Exception("append length not 2.  :(");
                }

                if (appendParts[1].equals("newline")) {
                    separatorChar = "\n";
                } else if (appendParts[1].equals("comma")) {
                    separatorChar = ", ";
                } else if (appendParts[1].equals("dash")) {
                    separatorChar = " - ";
                } else if (appendParts[1].equals("space")) {
                    separatorChar = " ";
                }

                appendValid = true;

            }

            if (appendValid) {

                if (valueIsNull == false) {
                    log.debug("Append data to element [" + path[idx] + "] to [" + value + "]");

                    String data = elementPath[idx].getText();

                    data = data + separatorChar + value;

                    elementPath[idx].setText(data);
                }
            } else {
                if (xToManyElement == true && elementCreated == false) {

                    log.debug("Creating an 0..m element [" + path[idx] + "] with value [" + value + "]");
                    Element docElement = new Element(path[idx]);

                    // @todo may need to check if element is nullable...
                    if (valueIsNull) {
                        docElement.setAttribute("nil", "true");
                    }

                    docElement.setText(value);
                    elementPath[idx] = docElement;

                    addElementToDocument(schemaPath[idx - 1], elementPath[idx - 1], docElement);
                } else {
                    log.debug("Setting element [" + path[idx] + "] to [" + value + "]");

                    elementPath[idx].setText(value);

                    // @todo may need to check if element is nullable...
                    if (valueIsNull) {
                        elementPath[idx].setAttribute("nil", "true");
                    }

                }
            }

        }

        /*****
         * Useful for deep debugging... try { XMLOutputter xmlout = new
         * XMLOutputter();
         * xmlout.setFormat(org.jdom.output.Format.getPrettyFormat());
         * xmlout.output(buildDocument, System.out); } catch (Exception ex) {
         * System.out.println("EXCEPTION: " + ex.toString()); }
         *****/

    } // end populateDoc()

    private void addElementToDocument(XSDElement schemaParent, Element documentParent, Element newChild) {

        /* If no schema defined or no children have been added to the parent then we can safely just add our child... */

        if (schemaParent == null || documentParent.getChildren().size() == 0) {
            documentParent.addContent(newChild);
        } else {
            List<XSDElement> xsdChildren = schemaParent.getChildren();
            Object[] children = documentParent.getChildren().toArray();
            Element child = null;
            String newChildName = newChild.getName();
            boolean afterFlag = false;
            int childIdx = 0;

            for (childIdx = 0; childIdx < children.length && afterFlag == false; childIdx++) {

                child = (Element) children[childIdx];

                if (child.getName().equals(newChildName) == false) {
                    /* New child so check in XSD list of children to see if our new child goes before it... */
                    for (XSDElement el : xsdChildren) {
                        /* If the new child name equals the XSD name then we can add after the current position... */
                        if (el.getName().equals(newChildName)) {
                            afterFlag = true;
                            childIdx--;
                            break;
                        } else if (el.getName().equals(child.getName())) {
                            break;
                        }
                    }
                }

            }

            /* If the current child name equals the new child name then we need to go to the end of set of */
            /* those children before we can add our new child, i.e. new A must go after AAA<here>          */

            while (childIdx < children.length - 1 && child.getName().equals(newChildName)) {
                childIdx++;
                child = (Element) children[childIdx];
            }

            /* Now add the new child... */

            if (childIdx < children.length) {
                documentParent.addContent(childIdx, newChild);
            } else {
                documentParent.addContent(newChild);
            }
        }

    }

    /**
     * adds a throwNew trigger to the "newTrigger" list.... fuck me this is awful
     * @param elementSpec
     */
    public void throwNew(String elementSpec) {
        newTrigger.put(elementSpec, new Boolean(true));
    }

    private boolean triggerNew(String elementSpec) {
        log.debug("checking triggerNew [" + elementSpec + "]");
        boolean found = false;
        Boolean trigger = null;
        trigger = newTrigger.get(elementSpec);

        if (null != trigger && trigger.booleanValue() == true) {
            // clear out the trigger... it's been used!
            found = true;
            trigger = false;
            log.debug("activate triggerNew [" + elementSpec + "]");
        }
        return found;
    }

    /*
        for (int i = 0; i < newTrigger.length; i++) {
       if (newTrigger[i] != null) {
           if (newTrigger[i].equals(elementSpec)) {
               newTrigger[i] = null;
               log.debug("activate triggerNew [" + elementSpec + "] (" + i + ")");
               return(true);
           }
       }
        }
        
        return(false);
    }
    */
    /** wow
     * 
     * @param parent
     * @param childName
     * @return
     */
    private Element findElement(Element parent, String childName) {
        List<Element> children = parent.getChildren();

        ListIterator<?> it = children.listIterator();
        /** We need to go backwards through the list... **/

        while (it.hasNext()) {
            it.next();
        }

        while (it.hasPrevious()) {
            Element child = (Element) it.previous();

            if (child.getName().equals(childName)) {
                return (child);
            }
        }

        return (null);

    }

    public Document getDocument() {

        if (targetNamespace != null) {
            Element e = buildDocument.getRootElement();
            Namespace ns = Namespace.getNamespace("tns", targetNamespace);

            e.setNamespace(ns);

            buildDocument.setBaseURI(targetNamespace);
        }

        return (buildDocument);
    }

    public void fillDoc() {

        /** Add any mandatory elements that may not have been populated during the mapping phase... **/

        log.debug("fillDoc()");
        if (schemaDefined == true && root != null) {
            log.debug("fillDoc() = schema defined");
            fillMandatoryChildren(root, schemaRoot != null ? schemaRoot : root.getName(), buildDocument);
        }

    }

    private void fillMandatoryChildren(XSDElement element, String parentSpec, Document doc) {
        for (XSDElement el : element.getChildren()) {
            String path = parentSpec + "." + el.getName();
            if (el.isMandatory()) {
                addMandatoryElementToDocument(el, path, doc.getRootElement());
            }
            fillMandatoryChildren(el, path, doc);
        }

        /** Add any mandatory attributes... **/
        List<XSDAttributeType> attList = element.getAttributeList();

        if (attList != null) {
            for (XSDAttributeType att : attList) {
                if (((XSDAttribute) att).isMandatory()) {
                    addMandatoryElementToDocument(element, parentSpec + ".@" + att.getName(), doc.getRootElement());
                }
            }
        }

    }

    /**
     * 
     * @param elementSpec
     * @param schemaElement
     * @param element
     */
    private void addMandatoryElementToDocument(XSDElement schemaElement, String elementSpec, Element element) {
        String[] eSpec = elementSpec.split("\\.");

        if (eSpec[0].equals(element.getName()) == false) {
            log.warn("The parent element [" + element.getName() + "] does not match the element spec ["
                    + elementSpec + "]");
            return;
        }

        int idx = 1;
        while (idx < eSpec.length) {

            if (isAttribute(eSpec[idx])) {
                String attname = eSpec[idx].substring(1, eSpec[idx].length());
                Attribute att = element.getAttribute(attname);

                if (att == null) {
                    /** Mandatory attribute does not exist so add it.. **/
                    element.setAttribute(attname, schemaElement.getDefaultAttValue(attname));
                }

                return;
            }

            StringBuffer tmp = new StringBuffer();
            for (int i = idx; i < eSpec.length; i++) {
                if (tmp.length() != 0) {
                    tmp.append(".");
                }
                tmp.append(eSpec[i]);
            }

            Iterator<?> it = element.getChildren(eSpec[idx]).iterator();

            /**
             * If the document does not have the specified child element and we
             * are at the end
             **/
            /**
             * of the chain then this is a mandatory element that needs to be
             * added...
             **/
            if (it.hasNext() == false && idx == eSpec.length - 1) {

                Element newel = new Element(eSpec[idx]);

                addElementToDocument(schemaElement, element, newel);

                return;
            }

            /**
             * If the document does not have the specified child element and
             * that element is
             **/
            /** just part of the chain, then no need to carry on.. **/
            if (it.hasNext() == false) {
                return;
            }

            /**
             * If the document does have the specified child element and we are
             * at the end of the chain then every thing is ok, so just return **/
            if (idx == eSpec.length - 1) {
                return;
            } else {
                /** We have to follow every one of the elements down the tree.. **/
                while (it.hasNext()) {
                    addMandatoryElementToDocument(schemaElement, tmp.toString(), (Element) it.next());
                }
                return;
            }
        }
    }

    private boolean isAttribute(String el) {
        return el.startsWith("@");
    }

    @Override
    public boolean build(Element grandchild, XSDCache cache, String parentName) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void show(int preIndent, boolean subCachedType, boolean htmlForm) {
        // TODO Auto-generated method stub

    }

    @Override
    public List<XMLType> getGroup() {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     public XSDElement getSchemaRootXSDElement() {
     return(root);
     }
         
     public String getSchemaRootName() {
     return(this.schemaRoot);
     }
     */
} // end class XSDSchema