org.talend.datatools.xml.utils.SchemaPopulationUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.talend.datatools.xml.utils.SchemaPopulationUtil.java

Source

/***********************************************************************************************************************
 * Copyright (c) 2004, 2005 Actuate Corporation. All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors: Actuate Corporation - initial API and implementation
 **********************************************************************************************************************/

package org.talend.datatools.xml.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.lang.StringUtils;
import org.apache.xerces.impl.xs.XMLSchemaLoader;
import org.apache.xerces.impl.xs.XSAttributeGroupDecl;
import org.apache.xerces.impl.xs.XSAttributeUseImpl;
import org.apache.xerces.impl.xs.XSComplexTypeDecl;
import org.apache.xerces.impl.xs.XSElementDecl;
import org.apache.xerces.impl.xs.XSModelGroupImpl;
import org.apache.xerces.impl.xs.XSParticleDecl;
import org.apache.xerces.impl.xs.identity.UniqueOrKey;
import org.apache.xerces.xni.XNIException;
import org.apache.xerces.xni.grammars.Grammar;
import org.apache.xerces.xni.grammars.XSGrammar;
import org.apache.xerces.xni.parser.XMLInputSource;
import org.apache.xerces.xs.StringList;
import org.apache.xerces.xs.XSConstants;
import org.apache.xerces.xs.XSModel;
import org.apache.xerces.xs.XSNamedMap;
import org.apache.xerces.xs.XSObject;
import org.apache.xerces.xs.XSObjectList;
import org.apache.xerces.xs.XSParticle;
import org.apache.xerces.xs.XSTypeDefinition;
import org.eclipse.xsd.XSDSchema;
import org.talend.commons.exception.ExceptionHandler;
import org.talend.core.model.metadata.types.JavaDataTypeHelper;
import org.talend.core.model.metadata.types.JavaTypesManager;

/**
 * This class is used to offer GUI a utility to get an tree from certain xml/xsd file.
 */
public class SchemaPopulationUtil {

    /**
     * @param fileName
     * @throws URISyntaxException
     * @throws IOException
     */
    public static ATreeNode getSchemaTree(String fileName, boolean includeAttribute, boolean forMDM,
            int numberOfElementsAccessiable, List<String> attList)
            throws OdaException, URISyntaxException, IOException {
        if (fileName.toUpperCase().endsWith(".XSD")) {
            return XSDFileSchemaTreePopulator.getSchemaTree(fileName, includeAttribute, forMDM, attList);
        } else if (fileName.toUpperCase().endsWith(".DTD")) {
            return new DTDFileSchemaTreePopulator().getSchemaTree(fileName, includeAttribute);
        } else {
            return new XMLFileSchemaTreePopulator(numberOfElementsAccessiable).getSchemaTree(fileName,
                    includeAttribute);
        }
    }

    public static ATreeNode getSchemaTree(String fileName, boolean includeAttribute,
            int numberOfElementsAccessiable) throws OdaException, URISyntaxException, IOException {
        if (fileName.toUpperCase().endsWith(".XSD")) {
            return XSDFileSchemaTreePopulator.getSchemaTree(fileName, includeAttribute);
        } else if (fileName.toUpperCase().endsWith(".DTD")) {
            return new DTDFileSchemaTreePopulator().getSchemaTree(fileName, includeAttribute);
        } else {
            return new XMLFileSchemaTreePopulator(numberOfElementsAccessiable).getSchemaTree(fileName,
                    includeAttribute);
        }
    }

    /**
     * DOC ycbai Comment method "getSchemaTree".
     * 
     * Only for xsd.
     * 
     * @param xsModel
     * @param selectedNode
     * @param includeAttribute
     * @return
     * @throws OdaException
     * @throws URISyntaxException
     * @throws IOException
     */
    public static ATreeNode getSchemaTree(XSModel xsModel, ATreeNode selectedNode, boolean includeAttribute)
            throws OdaException, URISyntaxException, IOException {
        return XSDFileSchemaTreePopulator.getSchemaTree(xsModel, selectedNode, includeAttribute);
    }

    public static ATreeNode getSchemaTree(XSDSchema schema, ATreeNode selectedNode)
            throws OdaException, URISyntaxException, IOException {
        return getSchemaTree(null, schema, selectedNode);
    }

    public static ATreeNode getSchemaTree(XSDPopulationUtil2 popUtil, XSDSchema schema, ATreeNode selectedNode)
            throws OdaException, URISyntaxException, IOException {
        if (popUtil == null) {
            popUtil = new XSDPopulationUtil2();
        }
        return popUtil.getSchemaTree(schema, selectedNode);
    }

    public static ATreeNode getSchemaTree(XSDPopulationUtil2 popUtil, XSDSchema schema, ATreeNode selectedNode,
            boolean supportChoice, boolean supportSubstitution)
            throws OdaException, URISyntaxException, IOException {
        if (popUtil == null) {
            popUtil = new XSDPopulationUtil2();
        }
        return popUtil.getSchemaTree(schema, selectedNode, supportChoice, supportSubstitution);
    }

    public static ATreeNode getSchemaTree(XSDSchema schema, ATreeNode selectedNode, boolean supportChoice,
            boolean supportSubstitution) throws OdaException, URISyntaxException, IOException {
        return getSchemaTree(null, schema, selectedNode, supportChoice, supportSubstitution);
    }

}

/**
 * This class is used to populate an XML schema tree from an xml file.
 * 
 */
/**
 * DOC nrousseau class global comment. Detailled comment
 */
final class XMLFileSchemaTreePopulator implements ISaxParserConsumer {

    //
    private int rowCount;

    private final ATreeNode root;

    private SaxParser sp;

    private boolean includeAttribute = true;

    private final int numberOfElementsAccessiable;

    private Map<String, String> prefixToNamespace = new HashMap<String, String>();

    private Map<String, ATreeNode> nodePathMap = new HashMap<String, ATreeNode>();

    Thread spThread;

    /**
     * 
     * 
     */
    XMLFileSchemaTreePopulator(int numberOfElementsAccessiable) {
        this.rowCount = 0;
        this.root = new ATreeNode();
        this.root.setValue("ROOT");
        this.numberOfElementsAccessiable = numberOfElementsAccessiable == 0 ? Integer.MAX_VALUE
                : numberOfElementsAccessiable;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.datatools.enablement.oda.xml.util.ISaxParserConsumer#manipulateData(java.lang.String,
     * java.lang.String)
     */
    @Override
    public void manipulateData(String path, String value) {
        guessColumnInfo(path, value);
    }

    /**
     * DOC cmeng Comment method "guessDataType".
     * 
     * @param path
     * @param value
     */
    private void guessColumnInfo(String path, String value) {
        // Most of the codes are copied from XmlFileStep3Form.refreshMetaDataTable(...)

        ATreeNode treeNode = nodePathMap.get(getTreamedPath(path));
        if (treeNode == null) {
            return;
        }

        // 1. set data type
        String dataType = treeNode.getDataType();
        if (StringUtils.isEmpty(dataType)) {
            dataType = JavaDataTypeHelper.getTalendTypeOfValue(value);
        } else {
            dataType = JavaDataTypeHelper.getCommonType(dataType, JavaDataTypeHelper.getTalendTypeOfValue(value));
        }
        try {
            treeNode.setDataType(dataType);
        } catch (OdaException e) {
            ExceptionHandler.process(e);
        }

        if (StringUtils.isEmpty(value)) {
            return;
        }
        // 2. set data length
        long dataLength = treeNode.getDataMaxLength();
        long curValueLength = value.length();
        if (dataLength < curValueLength) {
            dataLength = curValueLength;
        }
        treeNode.setDataMaxLength(dataLength);

        // 3. set precision
        int curPrecision = 0;
        long precisionValue = 0;
        if (value.indexOf(',') > -1) {
            curPrecision = value.lastIndexOf(',');
            precisionValue = dataLength - curPrecision;
        } else if (value.indexOf('.') > -1) {
            curPrecision = value.lastIndexOf('.');
            precisionValue = dataLength - curPrecision;
        }

        if (JavaTypesManager.FLOAT.getId().equals(dataType) || JavaTypesManager.DOUBLE.getId().equals(dataType)) {
            treeNode.setPrecisionValue(precisionValue);
        } else {
            treeNode.setPrecisionValue(0);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.datatools.enablement.oda.xml.util.ISaxParserConsumer#detectNewRow(java.lang.String,
     * java.lang.String, java.lang.String, boolean)
     */
    @Override
    public void detectNewRow(String path, String prefix, String uri, boolean start) {
        String treamedPath = getTreamedPath(path);
        this.insertNode(treamedPath, prefix, uri);
        // If not attribute
        if (!isAttribute(path) && start) {
            rowCount++;
        }

        // Only parser the first 10000 elements
        if (rowCount >= numberOfElementsAccessiable) {
            assert sp != null;
            sp.setStart(false);
            sp.stopParsing();
        }

    }

    private String getTreamedPath(String path) {
        return path.replaceAll("\\Q[\\E\\d+\\Q]\\E", "").trim(); //$NON-NLS-1$ //$NON-NLS-2$
    }

    /**
     * Exam whether given path specified an attribute
     * 
     * @param path
     * @return
     */
    private boolean isAttribute(String path) {
        return path.matches(".*\\Q[@\\E.+\\Q]\\E.*");
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.datatools.enablement.oda.xml.util.ISaxParserConsumer#wakeup()
     */
    @Override
    public synchronized void wakeup() {
        notify();
    }

    /**
     * Return the root node of a schema tree.
     * 
     * @param fileName
     * @return
     */
    public ATreeNode getSchemaTree(String fileName, boolean includeAttribute) {
        this.includeAttribute = includeAttribute;
        // try {
        // sp = new SaxParser(XMLDataInputStreamCreator.getCreator(fileName).createXMLDataInputStream(), this);
        sp = new SaxParser(fileName, this);

        spThread = new Thread(sp);
        spThread.start();
        while (sp.isAlive() && !sp.isSuspended()) {
            try {
                synchronized (this) {
                    wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // } catch (OdaException e1) {
        // e1.printStackTrace();
        // }
        if (sp.isInvalidFile()) {
            throw new RuntimeException();
        }
        return root;
    }

    /**
     * Insert a node specified by the path.
     * 
     * @param treatedPath
     */
    private void insertNode(String treatedPath, String prefix, String uri) {
        boolean isAttribute = isAttribute(treatedPath);

        // Remove the leading "/" then split the path.
        String[] path = treatedPath.replaceFirst("/", "").split("/");

        // If the path specified an attribute then re-build the path array so
        // that it can divid element and
        // its attribute to two array items.
        if (isAttribute) {
            String[] temp = path[path.length - 1].split("\\Q[@\\E");

            assert temp.length == 2;

            String[] temp1 = new String[path.length + 1];
            for (int i = 0; i < path.length - 1; i++) {
                temp1[i] = path[i];
            }
            temp1[temp1.length - 2] = temp[0];
            temp1[temp1.length - 1] = temp[1].replaceAll("\\Q]\\E", "");
            path = temp1;
        }

        // The parentNode
        ATreeNode parentNode = root;

        // Iterate each path array element, find or create its countpart node
        // instance.
        for (int i = 0; i < path.length; i++) {
            // This variable hosts the node instance that matches the given path
            // array item value.
            ATreeNode matchedNode = null;

            for (int j = 0; j < parentNode.getChildren().length; j++) {
                if (((ATreeNode) parentNode.getChildren()[j]).getValue().equals(path[i])) {
                    matchedNode = (ATreeNode) parentNode.getChildren()[j];
                    break;
                }
            }
            if (matchedNode != null) {
                parentNode = matchedNode;
            } else {
                matchedNode = new ATreeNode();

                nodePathMap.put(treatedPath, matchedNode);

                if ((i == path.length - 1) && isAttribute) {
                    if (isAttribute && !this.includeAttribute) {
                        continue;
                    }
                    matchedNode.setType(ATreeNode.ATTRIBUTE_TYPE);
                } else {
                    matchedNode.setType(ATreeNode.ELEMENT_TYPE);
                }

                matchedNode.setValue(path[i]);
                parentNode.addChild(matchedNode);
                if (prefix != null) {
                    if (!prefixToNamespace.containsKey(prefix)) {
                        prefixToNamespace.put(prefix, uri);
                        ATreeNode namespaceNode = new ATreeNode();
                        try {
                            namespaceNode.setDataType(prefix);
                        } catch (OdaException e) {
                            // nothing
                        }
                        namespaceNode.setType(ATreeNode.NAMESPACE_TYPE);
                        namespaceNode.setValue(uri);
                        ((ATreeNode) root.getChildren()[0]).addAsFirstChild(namespaceNode);
                    }
                } else if (uri != null && !"".equals(uri)) {
                    ATreeNode namespaceNode = new ATreeNode();
                    try {
                        namespaceNode.setDataType("");
                    } catch (OdaException e) {
                        // nothing
                    }
                    namespaceNode.setType(ATreeNode.NAMESPACE_TYPE);
                    namespaceNode.setValue(uri);
                    matchedNode.addChild(namespaceNode);
                }
                parentNode = matchedNode;
            }
        }
    }
}

/**
 * This class is used to populate an XML schema tree from an xml file.
 * 
 */
final class XSDFileSchemaTreePopulator {

    private static boolean includeAttribute = true;

    /**
     * Populate the whole tree until all the leaves have no child.
     * 
     * @param root
     */
    private static void populateRoot(ATreeNode root) {
        Object[] toBeIterated = root.getChildren();
        for (Object element : toBeIterated) {
            Object value = ((ATreeNode) element).getDataType();
            List container = new ArrayList();
            if (value != null) {
                findNodeWithValue(root, value.toString(), container, new VisitingRecorder());
            }
            for (int j = 0; j < container.size(); j++) {
                if (((ATreeNode) container.get(j)).getChildren().length == 0) {
                    Object[] os = ((ATreeNode) element).getChildren();
                    for (int k = 0; k < os.length; k++) {
                        if (os[k] instanceof ATreeNode) {
                            if (!(((ATreeNode) os[k]).getDataType() != null && ((ATreeNode) os[k]).getDataType()
                                    .equals(((ATreeNode) container.get(j)).getDataType()))) {
                                ((ATreeNode) container.get(j)).addChild(os[k]);
                            }
                        }
                    }

                }
            }
        }
    }

    private static void populateRoot(ATreeNode root, ATreeNode selectedNode) {
        Object[] toBeIterated = root.getChildren();
        for (Object element : toBeIterated) {
            ATreeNode node = ((ATreeNode) element);
            Object nodeValue = node.getValue();
            if (nodeValue != null && !nodeValue.equals(selectedNode.getValue())
                    && !nodeValue.equals(selectedNode.getDataType())) {
                continue;
            }
            Object value = node.getDataType();
            List container = new ArrayList();
            findNodeWithValue(root, value.toString(), container, new VisitingRecorder());
            for (int j = 0; j < container.size(); j++) {
                if (((ATreeNode) container.get(j)).getChildren().length == 0) {
                    Object[] os = ((ATreeNode) element).getChildren();
                    for (int k = 0; k < os.length; k++) {
                        if (os[k] instanceof ATreeNode) {
                            if (!(((ATreeNode) os[k]).getDataType() != null && ((ATreeNode) os[k]).getDataType()
                                    .equals(((ATreeNode) container.get(j)).getDataType()))) {
                                ((ATreeNode) container.get(j)).addChild(os[k]);
                            }
                        }
                    }

                }
            }
            break;
        }
    }

    /**
     * Starting from a tree node, find all nodes with given value, and put it to container.
     * 
     * @param root
     * @param value
     * @param container
     */
    private static void findNodeWithValue(ATreeNode root, String value, List container, VisitingRecorder vr) {

        if (root.getType() == ATreeNode.ELEMENT_TYPE && !vr.visit(root.getValue().toString())) {
            return;
        }
        if (root.getDataType() != null && root.getDataType().equals(value)) {
            container.add(root);
        }
        Object[] children = root.getChildren();
        for (Object element : children) {
            if (element instanceof ATreeNode) {
                findNodeWithValue((ATreeNode) element, value, container, vr);
            }

        }
    }

    /**
     * Return the root node of a schema tree.
     * 
     * @param fileName
     * @return
     * @throws OdaException
     * @throws MalformedURLException
     * @throws URISyntaxException
     */
    public static ATreeNode getSchemaTree(String fileName, boolean incAttr, boolean forMDM, List<String> attList)
            throws OdaException, MalformedURLException, URISyntaxException {
        includeAttribute = incAttr;
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        URI uri = null;
        File f = new File(fileName);
        if (f.exists()) {
            uri = f.toURI();
        } else {
            URL url = new URL(fileName);
            uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(),
                    url.getQuery(), url.getRef());
        }

        // Then try to parse the input string as a url in web.
        if (uri == null) {
            uri = new URI(fileName);
        }

        // fixed a bug when parse one file contians Franch ,maybe need modification
        XMLSchemaLoader xsLoader = new XMLSchemaLoader();
        XSModel xsModel = xsLoader.loadURI(uri.toString());
        if (xsModel == null) {
            try {
                Grammar loadGrammar = xsLoader.loadGrammar(
                        new XMLInputSource(null, uri.toString(), null, new FileInputStream(f), "ISO-8859-1"));
                xsModel = ((XSGrammar) loadGrammar).toXSModel();
            } catch (XNIException e) {
                e.printStackTrace();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        ATreeNode complexTypesRoot = populateComplexTypeTree(xsModel);

        XSNamedMap map = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);

        ATreeNode root = new ATreeNode();
        root.setValue("ROOT");
        for (int i = 0; i < map.getLength(); i++) {
            ATreeNode node = new ATreeNode();
            XSElementDecl element = (XSElementDecl) map.item(i);

            String namespace = element.getNamespace();
            XSObject unique = element.getIdentityConstraints().itemByName(namespace, element.getName());
            if (unique instanceof UniqueOrKey) {
                node.getUniqueNames().clear();
                UniqueOrKey uniqueOrKey = (UniqueOrKey) unique;
                String uniqueName = "";
                StringList fieldStrs = uniqueOrKey.getFieldStrs();
                for (int j = 0; j < fieldStrs.getLength(); j++) {
                    uniqueName = fieldStrs.item(j);
                    if (uniqueName != null && !"".equals(uniqueName)) {
                        uniqueName = uniqueName.replace("/", "").replace(".", "");
                        node.getUniqueNames().add(uniqueName);
                    }
                }
            }
            ATreeNode namespaceNode = null;
            if (namespace != null) {
                namespaceNode = new ATreeNode();
                namespaceNode.setDataType(namespace);
                namespaceNode.setType(ATreeNode.NAMESPACE_TYPE);
                namespaceNode.setValue(namespace);
            }

            node.setValue(element.getName());
            node.setType(ATreeNode.ELEMENT_TYPE);
            node.setDataType(element.getName());

            if (element.getTypeDefinition() instanceof XSComplexTypeDecl) {
                XSComplexTypeDecl complexType = (XSComplexTypeDecl) element.getTypeDefinition();
                // If the complex type is explicitly defined, that is, it has name.
                if (complexType.getName() != null) {
                    node.setDataType(complexType.getName());
                    ATreeNode n = findComplexElement(complexTypesRoot, complexType.getName());
                    if (namespaceNode != null) {
                        node.addChild(namespaceNode);
                    }
                    if (n != null) {
                        node.addChild(n.getChildren());
                    }
                }
                // If the complex type is implicitly defined, that is, it has no name.
                else {
                    // If the complex type is implicitly defined , no need to check it in explicitly complex type
                    addParticleAndAttributeInfo(node, complexType, complexTypesRoot, new VisitingRecorder());
                }
            }
            String nodeType = node.getOriginalDataType();
            if (forMDM) {
                Object nodeValue = node.getValue();
                if (nodeValue != null && !attList.contains(nodeValue) && nodeType != null
                        && !attList.contains(nodeType)) {
                    continue;
                }
            } else {
                if (nodeType != null && !attList.contains(nodeType)) {
                    continue;
                }
            }
            if (forMDM && attList.size() == 1 && node.getValue().equals(attList.get(0))) {
                root = node;
            } else {
                root.addChild(node);
            }
        }

        // if no base element, display all complex types / attributes directly.
        if (map.getLength() == 0) {
            root.addChild(complexTypesRoot.getChildren());
        }

        populateRoot(root);
        return root;

    }

    /**
     * Return the root node of a schema tree.
     * 
     * @param fileName
     * @return
     * @throws OdaException
     * @throws MalformedURLException
     * @throws URISyntaxException
     */
    public static ATreeNode getSchemaTree(String fileName, boolean incAttr)
            throws OdaException, MalformedURLException, URISyntaxException {
        includeAttribute = incAttr;
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        URI uri = null;
        File f = new File(fileName);
        if (f.exists()) {
            uri = f.toURI();
        } else {
            URL url = new URL(fileName);
            uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(),
                    url.getQuery(), url.getRef());
        }

        // Then try to parse the input string as a url in web.
        if (uri == null) {
            uri = new URI(fileName);
        }

        // fixed a bug when parse one file contians Franch ,maybe need modification
        XMLSchemaLoader xsLoader = new XMLSchemaLoader();
        XSModel xsModel = xsLoader.loadURI(uri.toString());
        if (xsModel == null) {
            try {
                Grammar loadGrammar = xsLoader.loadGrammar(
                        new XMLInputSource(null, uri.toString(), null, new FileInputStream(f), "ISO-8859-1"));
                xsModel = ((XSGrammar) loadGrammar).toXSModel();
            } catch (XNIException e) {
                e.printStackTrace();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        ATreeNode complexTypesRoot = populateComplexTypeTree(xsModel);

        XSNamedMap map = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);

        ATreeNode root = new ATreeNode();

        root.setValue("ROOT");
        for (int i = 0; i < map.getLength(); i++) {
            ATreeNode node = new ATreeNode();
            XSElementDecl element = (XSElementDecl) map.item(i);

            String namespace = element.getNamespace();
            XSObject unique = element.getIdentityConstraints().itemByName(namespace, element.getName());
            if (unique instanceof UniqueOrKey) {
                node.getUniqueNames().clear();
                UniqueOrKey uniqueOrKey = (UniqueOrKey) unique;
                String uniqueName = "";
                StringList fieldStrs = uniqueOrKey.getFieldStrs();
                for (int j = 0; j < fieldStrs.getLength(); j++) {
                    uniqueName = fieldStrs.item(j);
                    if (uniqueName != null && !"".equals(uniqueName)) {
                        uniqueName = uniqueName.replace("/", "").replace(".", "");
                        node.getUniqueNames().add(uniqueName);
                    }
                }
            }
            ATreeNode namespaceNode = null;
            if (namespace != null) {
                namespaceNode = new ATreeNode();
                namespaceNode.setDataType(namespace);
                namespaceNode.setType(ATreeNode.NAMESPACE_TYPE);
                namespaceNode.setValue(namespace);
            }

            node.setValue(element.getName());
            node.setType(ATreeNode.ELEMENT_TYPE);
            node.setDataType(element.getName());
            if (element.getTypeDefinition() instanceof XSComplexTypeDecl) {
                XSComplexTypeDecl complexType = (XSComplexTypeDecl) element.getTypeDefinition();
                // If the complex type is explicitly defined, that is, it has name.
                if (complexType.getName() != null) {
                    node.setDataType(complexType.getName());
                    ATreeNode n = findComplexElement(complexTypesRoot, complexType.getName());
                    if (namespaceNode != null) {
                        node.addChild(namespaceNode);
                    }
                    if (n != null) {
                        node.addChild(n.getChildren());
                    }
                }
                // If the complex type is implicitly defined, that is, it has no name.
                else {

                    addParticleAndAttributeInfo(node, complexType, complexTypesRoot, new VisitingRecorder());
                }
            }
            root.addChild(node);
        }

        // if no base element, display all complex types / attributes directly.
        if (map.getLength() == 0) {
            root.addChild(complexTypesRoot.getChildren());
        }

        populateRoot(root);
        return root;

    }

    /**
     * DOC ycbai Comment method "getSchemaTree".
     * 
     * @param xsModel
     * @param selectedNode
     * @param incAttr
     * @return
     * @throws OdaException
     * @throws MalformedURLException
     * @throws URISyntaxException
     */
    public static ATreeNode getSchemaTree(XSModel xsModel, ATreeNode selectedNode, boolean incAttr)
            throws OdaException, MalformedURLException, URISyntaxException {
        includeAttribute = incAttr;

        ATreeNode complexTypesRoot = populateComplexTypeTree(xsModel, selectedNode);

        XSNamedMap map = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);

        ATreeNode root = new ATreeNode();

        root.setValue("ROOT");
        for (int i = 0; i < map.getLength(); i++) {
            ATreeNode node = new ATreeNode();
            XSElementDecl element = (XSElementDecl) map.item(i);
            String elementName = element.getName();
            if (elementName != null && !elementName.equals(selectedNode.getValue())
                    && !elementName.equals(selectedNode.getDataType())) {
                continue;
            }

            String namespace = element.getNamespace();
            ATreeNode namespaceNode = null;
            if (namespace != null) {
                namespaceNode = new ATreeNode();
                namespaceNode.setDataType(namespace);
                namespaceNode.setType(ATreeNode.NAMESPACE_TYPE);
                namespaceNode.setValue(namespace);
            }

            node.setValue(elementName);
            node.setType(ATreeNode.ELEMENT_TYPE);
            node.setDataType(elementName);
            if (element.getTypeDefinition() instanceof XSComplexTypeDecl) {
                XSComplexTypeDecl complexType = (XSComplexTypeDecl) element.getTypeDefinition();
                // If the complex type is explicitly defined, that is, it has name.
                if (complexType.getName() != null) {
                    node.setDataType(complexType.getName());
                    ATreeNode n = findComplexElement(complexTypesRoot, complexType.getName());
                    if (namespaceNode != null) {
                        node.addChild(namespaceNode);
                    }
                    if (n != null) {
                        node.addChild(n.getChildren());
                    }
                }
                // If the complex type is implicitly defined, that is, it has no name.
                else {

                    addParticleAndAttributeInfo(node, complexType, complexTypesRoot, new VisitingRecorder());
                }
            }
            root.addChild(node);
        }

        // if no base element, display all complex types / attributes directly.
        if (map.getLength() == 0) {
            Object[] children = complexTypesRoot.getChildren();
            if (children != null && children.length > 0) {
                for (Object obj : children) {
                    if (obj instanceof ATreeNode) {
                        ATreeNode treeNode = (ATreeNode) obj;
                        Object value = treeNode.getValue();
                        if (value != null && (value.equals(selectedNode.getValue())
                                || value.equals(selectedNode.getDataType()))) {
                            root.addChild(treeNode);
                            break;
                        }
                    }
                }
            }
        }

        populateRoot(root, selectedNode);
        return root;

    }

    public static XSModel getXSModel(String fileName) throws URISyntaxException, MalformedURLException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        URI uri = null;
        File f = new File(fileName);
        if (f.exists()) {
            uri = f.toURI();
        } else {
            URL url = new URL(fileName);
            uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(),
                    url.getQuery(), url.getRef());
        }

        // Then try to parse the input string as a url in web.
        if (uri == null) {
            uri = new URI(fileName);
        }

        // fixed a bug when parse one file contians Franch ,maybe need modification
        XMLSchemaLoader xsLoader = new XMLSchemaLoader();
        XSModel xsModel = xsLoader.loadURI(uri.toString());
        if (xsModel == null) {
            try {
                Grammar loadGrammar = xsLoader.loadGrammar(
                        new XMLInputSource(null, uri.toString(), null, new FileInputStream(f), "ISO-8859-1"));
                xsModel = ((XSGrammar) loadGrammar).toXSModel();
            } catch (XNIException e) {
                e.printStackTrace();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return xsModel;
    }

    /**
     * Add the particles and attributes that defined in an implicitly defined ComplexType to the node.
     * 
     * @param node
     * @param complexType
     * @throws OdaException
     */
    private static void addParticleAndAttributeInfo(ATreeNode node, XSComplexTypeDecl complexType,
            ATreeNode complexTypesRoot, VisitingRecorder vr) throws OdaException {
        if (!vr.visit(node.getValue().toString())) {
            return;
        }
        XSParticle particle = complexType.getParticle();
        if (particle != null) {
            addElementToNode(node, complexTypesRoot, (XSModelGroupImpl) particle.getTerm(), vr);
        }
        if (!includeAttribute) {
            return;
        }
        XSAttributeGroupDecl group = complexType.getAttrGrp();
        if (group != null) {
            XSObjectList list = group.getAttributeUses();
            for (int j = 0; j < list.getLength(); j++) {
                ATreeNode childNode = new ATreeNode();
                // add by wzhang, set the childNode type
                XSAttributeUseImpl attr = (XSAttributeUseImpl) list.item(j);
                String dataType = attr.fAttrDecl.getTypeDefinition().getName();
                // notes added by nma. two datatype definitions may need two different getting ways.
                if (dataType == null || dataType.length() == 0) {
                    dataType = ((XSAttributeUseImpl) list.item(j)).fAttrDecl.getTypeDefinition().getBaseType()
                            .getName();
                }
                if (dataType != null && dataType.length() > 0) {
                    childNode.setDataType(dataType);
                }
                childNode.setDataType(dataType);
                childNode.setValue(attr.getAttrDeclaration().getName());
                childNode.setType(ATreeNode.ATTRIBUTE_TYPE);
                node.addChild(childNode);
            }
        }
    }

    /**
     * 
     * @param node
     * @param complexTypesRoot
     * @param group
     * @throws OdaException
     */
    private static void addElementToNode(ATreeNode node, ATreeNode complexTypesRoot, XSModelGroupImpl group,
            VisitingRecorder vr) throws OdaException {
        if (!vr.visit(node.getValue().toString())) {
            return;
        }
        XSObjectList list = group.getParticles();
        for (int j = 0; j < list.getLength(); j++) {
            if (((XSParticleDecl) list.item(j)).getTerm() instanceof XSModelGroupImpl) {
                addElementToNode(node, complexTypesRoot,
                        (XSModelGroupImpl) ((XSParticleDecl) list.item(j)).getTerm(), vr);
                continue;
            }
            ATreeNode childNode = new ATreeNode();
            childNode.setValue(((XSParticleDecl) list.item(j)).getTerm().getName());
            XSElementDecl term = null;
            if (((XSParticleDecl) list.item(j)).getTerm() instanceof XSElementDecl) {
                term = (XSElementDecl) ((XSParticleDecl) list.item(j)).getTerm();
            } else {
                continue;
            }
            String dataType = term.getTypeDefinition().getName();
            if (dataType == null || dataType.length() == 0) {
                dataType = childNode.getValue().toString();
            }
            childNode.setDataType(dataType);
            childNode.setType(ATreeNode.ELEMENT_TYPE);
            XSTypeDefinition xstype = term.getTypeDefinition();
            // Populate the complex data types under node.
            if ((!dataType.equals("anyType")) && xstype instanceof XSComplexTypeDecl) {
                // First do a recursive call to populate all child complex type of current node.
                if (xstype.getName() == null) {
                    addParticleAndAttributeInfo(childNode, (XSComplexTypeDecl) xstype, complexTypesRoot, vr);
                }
                ATreeNode n = findComplexElement(complexTypesRoot, dataType);
                if (n != null) {
                    childNode.addChild(n.getChildren());
                }
            }
            node.addChild(childNode);
        }
    }

    /**
     * Return the tree node instance that represents to the ComplexElement that featured by the given value.
     * 
     * @param root the tree node from which the search begin
     * @param value the name of the ComplexElement
     * @return
     */
    private static ATreeNode findComplexElement(ATreeNode root, String value) {
        Object[] os = root.getChildren();
        for (Object element : os) {
            if (((ATreeNode) element).getValue().equals(value)) {
                return (ATreeNode) element;
            }
        }
        return null;
    }

    /**
     * Populate a tree of ComplexTypes defined in an XSD file.
     * 
     * @param xsModel
     * @return the root node of the tree.
     * @throws OdaException
     */
    private static ATreeNode populateComplexTypeTree(XSModel xsModel) throws OdaException {
        XSNamedMap map = xsModel.getComponents(XSTypeDefinition.COMPLEX_TYPE);

        ATreeNode root = new ATreeNode();

        root.setValue("ROOT");
        root.setDataType("");
        Map<ATreeNode, XSParticle> childWithParticles = new HashMap<ATreeNode, XSParticle>();
        for (int i = 0; i < map.getLength(); i++) {
            ATreeNode node = new ATreeNode();
            XSComplexTypeDecl element = (XSComplexTypeDecl) map.item(i);
            if (element.getName().equals("anyType")) {
                continue;
            }
            node.setValue(element.getName());
            node.setType(ATreeNode.ELEMENT_TYPE);
            node.setDataType(element.getTypeName());
            root.addChild(node);

            if (includeAttribute) {
                XSAttributeGroupDecl group = element.getAttrGrp();
                if (group != null) {
                    XSObjectList list = group.getAttributeUses();
                    for (int j = 0; j < list.getLength(); j++) {
                        ATreeNode childNode = new ATreeNode();
                        childNode.setValue(((XSAttributeUseImpl) list.item(j)).getAttrDeclaration().getName());
                        childNode.setType(ATreeNode.ATTRIBUTE_TYPE);
                        // two different datatype definitions need two getting ways, added by nma.
                        String dataType = ((XSAttributeUseImpl) list.item(j)).fAttrDecl.getTypeDefinition()
                                .getName();
                        if (dataType == null || dataType.length() == 0) {
                            dataType = ((XSAttributeUseImpl) list.item(j)).fAttrDecl.getTypeDefinition()
                                    .getBaseType().getName();
                        }
                        if (dataType != null && dataType.length() > 0) {
                            childNode.setDataType(dataType);
                        }
                        node.addChild(childNode);
                    }
                }
            }
            XSParticle particle = element.getParticle();
            if (particle != null) {
                childWithParticles.put(node, particle);
            }
        }

        for (ATreeNode node : childWithParticles.keySet()) {
            XSParticle particle = childWithParticles.get(node);
            if (particle != null) {
                XSObjectList list = ((XSModelGroupImpl) particle.getTerm()).getParticles();
                populateTreeNodeWithParticles(root, node, list);
            }
        }

        populateRoot(root);
        return root;
    }

    private static ATreeNode populateComplexTypeTree(XSModel xsModel, ATreeNode selectedNode) throws OdaException {
        XSNamedMap map = xsModel.getComponents(XSTypeDefinition.COMPLEX_TYPE);

        ATreeNode root = new ATreeNode();

        root.setValue("ROOT");
        root.setDataType("");
        Map<ATreeNode, XSParticle> childWithParticles = new HashMap<ATreeNode, XSParticle>();
        for (int i = 0; i < map.getLength(); i++) {
            ATreeNode node = new ATreeNode();
            XSComplexTypeDecl element = (XSComplexTypeDecl) map.item(i);
            if (element.getName().equals("anyType")) {
                continue;
            }
            node.setValue(element.getName());
            node.setType(ATreeNode.ELEMENT_TYPE);
            node.setDataType(element.getTypeName());
            root.addChild(node);

            if (includeAttribute) {
                XSAttributeGroupDecl group = element.getAttrGrp();
                if (group != null) {
                    XSObjectList list = group.getAttributeUses();
                    for (int j = 0; j < list.getLength(); j++) {
                        ATreeNode childNode = new ATreeNode();
                        childNode.setValue(((XSAttributeUseImpl) list.item(j)).getAttrDeclaration().getName());
                        childNode.setType(ATreeNode.ATTRIBUTE_TYPE);
                        // two different datatype definitions need two getting ways, added by nma.
                        String dataType = ((XSAttributeUseImpl) list.item(j)).fAttrDecl.getTypeDefinition()
                                .getName();
                        if (dataType == null || dataType.length() == 0) {
                            dataType = ((XSAttributeUseImpl) list.item(j)).fAttrDecl.getTypeDefinition()
                                    .getBaseType().getName();
                        }
                        if (dataType != null && dataType.length() > 0) {
                            childNode.setDataType(dataType);
                        }
                        node.addChild(childNode);
                    }
                }
            }
            XSParticle particle = element.getParticle();
            if (particle != null) {
                childWithParticles.put(node, particle);
            }
        }

        for (ATreeNode node : childWithParticles.keySet()) {
            XSParticle particle = childWithParticles.get(node);
            if (particle != null) {
                XSObjectList list = ((XSModelGroupImpl) particle.getTerm()).getParticles();
                populateTreeNodeWithParticles(root, node, list);
            }
        }

        populateRoot(root);
        return root;
    }

    /**
     * 
     * @param node the node to which the elements defined in particles should be populated into
     * @param node2
     * @param list the XSObjectList which lists all particles.
     * @throws OdaException
     */
    private static void populateTreeNodeWithParticles(ATreeNode root, ATreeNode node, XSObjectList list)
            throws OdaException {
        for (int j = 0; j < list.getLength(); j++) {
            ATreeNode childNode = new ATreeNode();
            childNode.setValue(((XSParticleDecl) list.item(j)).getTerm().getName());

            if (((XSParticleDecl) list.item(j)).getTerm() instanceof XSElementDecl) {
                XSElementDecl element = ((XSElementDecl) ((XSParticleDecl) list.item(j)).getTerm());
                if (element.getTypeDefinition() != null && element.getTypeDefinition().getBaseType() != null) {
                    String dataType = element.getTypeDefinition().getBaseType().getName();
                    if (dataType.equals("anyType")) {
                        dataType = ((XSElementDecl) ((XSParticleDecl) list.item(j)).getTerm()).getTypeDefinition()
                                .getName();
                    }
                    if (dataType != null && dataType.length() > 0) {
                        childNode.setDataType(dataType);
                    }
                }

                childNode.setType(ATreeNode.ELEMENT_TYPE);
                node.addChild(childNode);

                // added by nrousseau to fix bug of partial loading of XSD file -- bug:9269
                String namespace = element.getNamespace();
                ATreeNode namespaceNode = null;
                if (namespace != null) {
                    namespaceNode = new ATreeNode();
                    namespaceNode.setDataType(namespace);
                    namespaceNode.setType(ATreeNode.NAMESPACE_TYPE);
                    namespaceNode.setValue(namespace);
                }

                if (element.getTypeDefinition() instanceof XSComplexTypeDecl) {
                    XSComplexTypeDecl complexType = (XSComplexTypeDecl) element.getTypeDefinition();
                    // If the complex type is explicitly defined, that is, it has name.
                    if (complexType.getName() != null) {
                        childNode.setDataType(complexType.getName());
                        ATreeNode n = findComplexElement(childNode, complexType.getName());
                        if (n == null) {
                            n = findComplexElement(root, complexType.getName());
                        }
                        if (n != null) {
                            childNode.addChild(n.getChildren());
                        }
                    }
                    // If the complex type is implicitly defined, that is, it has no name.
                    else {
                        addParticleAndAttributeInfo(childNode, complexType, childNode, new VisitingRecorder());
                    }
                }
                // end of fix -- bug:9269
            }
            // If the particle itself is of XSModelGroupImpl type, which means that they have child types, then do
            // recursive job
            // until all the children are added to the node.
            else if (((XSParticleDecl) list.item(j)).getTerm() instanceof XSModelGroupImpl) {
                XSModelGroupImpl mGroup = (XSModelGroupImpl) ((XSParticleDecl) list.item(j)).getTerm();
                XSObjectList obs = mGroup.getParticles();
                populateTreeNodeWithParticles(root, node, obs);
            }
        }
    }
}

/**
 * Record the number of an element being visited.This is used to parse recursive elements.
 * 
 */
class VisitingRecorder {

    //
    private final Map map;

    /**
     * 
     * 
     */
    VisitingRecorder() {
        map = new HashMap();
    }

    /**
     * 
     * @param value
     * @return
     */
    public boolean visit(String value) {
        if (map.containsKey(value)) {
            int i = ((Integer) (map.get(value))).intValue();
            // If an element has been visited three times in a recursive call
            // assume it is a recursive element.
            if (i > 3) {
                return false;
            } else {
                map.put(value, new Integer(i + 1));
                return true;
            }
        } else {
            map.put(value, new Integer(0));
            return true;
        }
    }
}