org.cruxframework.crux.tools.schema.DefaultSchemaGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.cruxframework.crux.tools.schema.DefaultSchemaGenerator.java

Source

/*
 * Copyright 2011 cruxframework.org.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.cruxframework.crux.tools.schema;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cruxframework.crux.core.client.screen.DeviceAdaptive.Device;
import org.cruxframework.crux.core.config.ConfigurationFactory;
import org.cruxframework.crux.core.declarativeui.template.TemplateParser;
import org.cruxframework.crux.core.declarativeui.template.Templates;
import org.cruxframework.crux.core.i18n.MessagesFactory;
import org.cruxframework.crux.core.rebind.DevelopmentScanners;
import org.cruxframework.crux.core.rebind.screen.widget.EvtProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.WidgetConfig;
import org.cruxframework.crux.core.rebind.screen.widget.WidgetCreator;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.AllChildProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.AnyWidgetChildProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.ChoiceChildProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.SequenceChildProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.TextChildProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.WidgetChildProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.WidgetChildProcessor.AnyTag;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.WidgetChildProcessor.AnyWidget;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.WidgetChildProcessor.HTMLTag;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.DeclarativeFactory;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagAttribute;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagAttributeDeclaration;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagAttributes;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagAttributesDeclaration;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagChild;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagChildren;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagConstraints;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagEvent;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagEventDeclaration;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagEvents;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagEventsDeclaration;
import org.cruxframework.crux.core.server.CruxBridge;
import org.cruxframework.crux.core.server.classpath.ClassPathResolverInitializer;
import org.cruxframework.crux.core.utils.StreamUtils;
import org.cruxframework.crux.core.utils.ViewUtils;
import org.cruxframework.crux.scanner.ClasspathUrlFinder;
import org.cruxframework.crux.scanner.Scanners;
import org.w3c.dom.Document;

import com.google.gwt.resources.client.ResourcePrototype;

/**
 * @author Thiago da Rosa de Bustamante
 *
 */
public class DefaultSchemaGenerator implements CruxSchemaGenerator {
    private static final Log logger = LogFactory.getLog(DefaultSchemaGenerator.class);

    protected File destDir;
    protected Map<String, Class<?>> enumTypes;

    protected Map<String, File> namespacesForCatalog;
    protected File projectBaseDir;
    protected Stack<Class<? extends WidgetChildProcessor<?>>> subTagTypes;
    protected TemplateParser templateParser;

    protected SchemaMessages schemaMessages;

    private String XHTML_XSD = "xhtml.xsd";

    /**
     * 
     * @param destDir
     */
    public DefaultSchemaGenerator(File projectBaseDir, File destDir, File webDir) {
        this.projectBaseDir = projectBaseDir;
        this.destDir = destDir;
        this.destDir.mkdirs();
        this.enumTypes = new HashMap<String, Class<?>>();
        this.namespacesForCatalog = new HashMap<String, File>();
        this.subTagTypes = new Stack<Class<? extends WidgetChildProcessor<?>>>();
        this.templateParser = new TemplateParser();
        this.schemaMessages = MessagesFactory.getMessages(SchemaMessages.class);

        if (Boolean.valueOf(ConfigurationFactory.getConfigurations().useHTML5XSD())) {
            XHTML_XSD = "xhtml5.xsd";
        }

        initializeSchemaGenerator(projectBaseDir, webDir);
    }

    /**
     * 
     * @param destRelativeDir
     */
    public DefaultSchemaGenerator(String projectBaseDir, String destDir, String webDir) {
        this(new File(projectBaseDir), new File(destDir), new File(webDir));
    }

    /**
     * @param out
     */
    public void generateCatalog() throws SchemaGeneratorException {
        try {
            File catalog = new File(destDir, "crux-catalog.xml");
            catalog.createNewFile();
            PrintStream stream = null;
            try {
                stream = new PrintStream(catalog);

                stream.println("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
                stream.println(
                        "<!DOCTYPE catalog PUBLIC \"-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN\" \"http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd\">");

                stream.println("<catalog xmlns=\"urn:oasis:names:tc:entity:xmlns:xml:catalog\">");
                for (Entry<String, File> entry : namespacesForCatalog.entrySet()) {
                    stream.println("\t<uri name=\"" + entry.getKey() + "\" uri=\"platform:/resource/"
                            + projectBaseDir.getName() + "/" + getRelativeName(entry.getValue()) + "\"/>");
                }
                stream.println("</catalog>");
            } finally {
                if (stream != null) {
                    stream.close();
                }
            }
        } catch (Exception e) {
            throw new SchemaGeneratorException(e.getMessage(), e);
        }
    }

    /**
     * @see org.cruxframework.crux.tools.schema.CruxSchemaGenerator#generateSchemas()
     */
    public void generateSchemas() throws SchemaGeneratorException {
        try {
            Set<String> libraries = WidgetConfig.getRegisteredLibraries();
            Set<String> templateLibraries = Templates.getRegisteredLibraries();
            for (String library : libraries) {
                logger.info("Generating xsd file for library [" + library + "]");
                generateSchemaForLibrary(library, libraries, templateLibraries);
            }

            logger.info("Generating template.xsd file.");
            generateTemplateSchema(libraries, templateLibraries);

            for (String library : templateLibraries) {
                logger.info("Generating XSD file for library [" + library + "]");
                generateSchemaForTemplateLibrary(library);
            }

            logger.info("Generating core.xsd file");
            generateCoreSchema(libraries, templateLibraries);
            generateOfflineSchema();
            generateXDeviceSchema(libraries, templateLibraries);
            generateViewSchema(libraries, templateLibraries);

            copyXHTMLSchema();

            logger.info("XSD Files Generated.");
        } catch (Exception e) {
            throw new SchemaGeneratorException(e.getMessage(), e);
        }
    }

    @Override
    public void generateDocumentation() throws SchemaGeneratorException {
        try {
            TransformerFactory factory = TransformerFactory.newInstance();
            StreamSource xslStream = new StreamSource(getClass().getResourceAsStream("/META-INF/xs3p.xsl"));
            Transformer transformer = factory.newTransformer(xslStream);

            for (File file : destDir.listFiles()) {
                String fileName = file.getName();
                logger.info("Generating Documentation for library [" + fileName + "].");
                if (fileName.endsWith("xsd")) {
                    transformer.setParameter("title", schemaMessages
                            .documentationTitle(fileName.substring(0, fileName.length() - 4).toUpperCase()));

                    if (fileName.endsWith("core.xsd") || fileName.endsWith("module.xsd")
                            || fileName.endsWith("offline.xsd") || fileName.endsWith("template.xsd")
                            || fileName.endsWith("view.xsd") || fileName.endsWith("xdevice.xsd")
                            || fileName.endsWith(XHTML_XSD)) {
                        transformer.setParameter("globalDeclarationsTitle",
                                schemaMessages.globalDeclarationsTitle());
                    } else {
                        transformer.setParameter("globalDeclarationsTitle",
                                schemaMessages.globalDeclarationsWidgetsTitle());
                    }
                    transformer.setParameter("printLegend", false);
                    transformer.setParameter("printGlossary", false);
                    StreamSource in = new StreamSource(new FileInputStream(file));
                    StreamResult out = new StreamResult(new File(destDir, file.getName() + ".html"));
                    transformer.transform(in, out);
                }
            }
            logger.info("All documentation generated.");
        } catch (Exception e) {
            throw new SchemaGeneratorException("Error generation HTML documentation for XSD files", e);
        }
    }

    /**
     * @param projectBaseDir
     * @param webDir
     */
    protected void initializeSchemaGenerator(File projectBaseDir, File webDir) {
        try {
            CruxBridge.getInstance().setSingleVM(true);
            ConfigurationFactory.getConfigurations().setEnableHotDeploymentForWebDirs(false);
            URL[] urls = ClasspathUrlFinder.findClassPaths();
            Scanners.setSearchURLs(urls);
            DevelopmentScanners.initializeScanners();

            if (webDir == null) {
                webDir = new File(projectBaseDir, "war/");
            }

            ClassPathResolverInitializer.getClassPathResolver()
                    .setWebInfClassesPath(new File(webDir, "WEB-INF/classes/").toURI().toURL());
            ClassPathResolverInitializer.getClassPathResolver()
                    .setWebInfLibPath(new File(webDir, "WEB-INF/lib/").toURI().toURL());
        } catch (Exception e) {
            throw new SchemaGeneratorException(e.getMessage(), e);
        }
    }

    /**
     * @param targetNS
     * @param coreFile
     */
    protected void registerNamespaceForCatalog(String targetNS, File file) {
        this.namespacesForCatalog.put(targetNS, file);
    }

    private void copyXHTMLSchema() {
        try {
            File xhtmlFile = new File(destDir, XHTML_XSD);
            if (xhtmlFile.exists()) {
                xhtmlFile.delete();
            }
            xhtmlFile.createNewFile();
            FileOutputStream out = new FileOutputStream(xhtmlFile);

            String targetNS = "http://www.w3.org/1999/xhtml";
            registerNamespaceForCatalog(targetNS, xhtmlFile);

            StreamUtils.write(getClass().getResourceAsStream("/META-INF/" + XHTML_XSD), out, true);
        } catch (Exception e) {
            throw new SchemaGeneratorException(e.getMessage(), e);
        }
    }

    /**
     * 
     * @param widgetFactory
     * @return
     */
    private boolean factorySupportsInnerText(Class<? extends WidgetCreator<?>> widgetFactory) {
        try {
            return hasTextChild(widgetFactory);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 
     * @param out
     * @param attributes
     * @param children
     * @throws NoSuchMethodException
     */
    private void generateAllChild(PrintStream out, TagConstraints attributes, TagChildren children, String library)
            throws NoSuchMethodException {
        out.print("<xs:all ");
        if (attributes != null) {
            out.print("minOccurs=\"" + attributes.minOccurs() + "\" ");
            out.print("maxOccurs=\"" + attributes.maxOccurs() + "\" ");
        }
        out.println(">");
        for (TagChild child : children.value()) {
            generateChild(out, child, true, library);
        }
        out.println("</xs:all>");
    }

    /**
     * 
     * @param out
     * @param library
     * @param added
     * @param processorClass
     */
    private void generateAttributes(PrintStream out, String library, Set<String> added, Class<?> processorClass) {
        TagAttributesDeclaration attrsDecl = processorClass.getAnnotation(TagAttributesDeclaration.class);
        if (attrsDecl != null) {
            for (TagAttributeDeclaration attr : attrsDecl.value()) {
                if (!added.contains(attr.value())) {
                    out.print("<xs:attribute name=\"" + attr.value() + "\" type=\""
                            + getSchemaType(attr.type(), library, attr.supportsDataBinding()) + "\" ");
                    if (attr.required()) {
                        out.print("use=\"required\" ");
                    } else {
                        String defaultValue = attr.defaultValue();
                        if (defaultValue.length() > 0)
                            out.print("default=\"" + defaultValue + "\" ");
                    }
                    out.println(">");

                    String attrDescription = attr.description();
                    if (attrDescription != null && attrDescription.length() > 0) {
                        out.println("<xs:annotation>");
                        out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(attrDescription)
                                + "</xs:documentation>");
                        out.println("</xs:annotation>");
                    }
                    out.println("</xs:attribute>");

                    added.add(attr.value());
                }
            }
        }
        TagAttributes attrs = processorClass.getAnnotation(TagAttributes.class);
        if (attrs != null) {
            for (TagAttribute attr : attrs.value()) {
                if (!added.contains(attr.value()) && !attr.xsdIgnore()) {
                    out.print("<xs:attribute name=\"" + attr.value() + "\" type=\""
                            + getSchemaType(attr.type(), library, attr.supportsDataBinding()) + "\" ");
                    if (attr.required()) {
                        out.print("use=\"required\" ");
                    } else {
                        String defaultValue = attr.defaultValue();
                        if (defaultValue.length() > 0)
                            out.print("default=\"" + defaultValue + "\" ");
                    }

                    out.println(">");
                    String attrDescription = attr.description();
                    if (attrDescription != null && attrDescription.length() > 0) {
                        out.println("<xs:annotation>");
                        out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(attrDescription)
                                + "</xs:documentation>");
                        out.println("</xs:annotation>");
                    }
                    out.println("</xs:attribute>");
                    added.add(attr.value());
                }
            }
        }
    }

    /**
     * 
     * @param out
     * @param widgetFactory
     */
    private void generateAttributesForFactory(PrintStream out, Class<?> widgetFactory, String library,
            Set<String> added) {
        generateAttributes(out, library, added, widgetFactory);

        Class<?> superclass = widgetFactory.getSuperclass();
        if (superclass != null && !superclass.equals(Object.class)) {
            generateAttributesForFactory(out, superclass, library, added);
        }
        Class<?>[] interfaces = widgetFactory.getInterfaces();
        for (Class<?> interfaceClass : interfaces) {
            generateAttributesForFactory(out, interfaceClass, library, added);
        }
    }

    /**
     * 
     * @param out
     * @param processorClass
     * @param library
     * @param added
     */
    private void generateAttributesForProcessor(PrintStream out, Class<?> processorClass, String library,
            Set<String> added) {
        try {
            generateAttributes(out, library, added, processorClass);

            Class<?> superclass = processorClass.getSuperclass();
            if (superclass != null && !superclass.equals(Object.class)) {
                generateAttributesForProcessor(out, superclass, library, added);
            }
        } catch (Exception e) {
            logger.error("Error creating XSD File: Error generating attributes for Processor.", e);
        }
    }

    /**
     * 
     * @param out
     * @param template
     */
    private void generateAttributesForTemplate(PrintStream out, Document template) {
        Set<String> attributesForTemplate = templateParser.getParameters(template);
        for (String attrValue : attributesForTemplate) {
            out.println("<xs:attribute name=\"" + attrValue + "\" type=\"xs:string\" use=\"required\" />");
        }
    }

    /**
     * 
     * @param out
     * @param tagChild
     * @param parentIsAnAgregator
     * @param library
     * @throws SecurityException
     * @throws NoSuchMethodException
     */
    private void generateChild(PrintStream out, TagChild tagChild, boolean parentIsAnAgregator, String library)
            throws SecurityException, NoSuchMethodException {
        Class<? extends WidgetChildProcessor<?>> processorClass = tagChild.value();
        TagConstraints attributes = ViewUtils.getChildTagConstraintsAnnotation(processorClass);
        TagChildren children = processorClass.getAnnotation(TagChildren.class);

        if (ChoiceChildProcessor.class.isAssignableFrom(processorClass)) {
            generateChoiceChild(out, attributes, children, library);
        } else if (SequenceChildProcessor.class.isAssignableFrom(processorClass)) {
            generateSequenceChild(out, attributes, children, library);
        } else if (AllChildProcessor.class.isAssignableFrom(processorClass)) {
            generateAllChild(out, attributes, children, library);
        } else {
            if (attributes != null) {
                generateGenericChildWithAttributes(out, library, processorClass, attributes);
            } else if (AnyWidgetChildProcessor.class.isAssignableFrom(processorClass)) {
                out.println("<xs:group ref=\"c:widgets\" >");
                out.println("<xs:annotation>");
                out.println(
                        "<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.anyWidgetsDescription())
                                + "</xs:documentation>");
                out.println("</xs:annotation>");
                out.println("</xs:group>");
            }
        }
    }

    /**
     * 
     * @param out
     * @param library
     * @param processorClass
     * @throws NoSuchMethodException
     */
    private void generateChildren(PrintStream out, String library, Class<?> processorClass)
            throws NoSuchMethodException {
        TagChildren annot = processorClass.getAnnotation(TagChildren.class);
        if (annot != null) {
            if (annot.value().length > 1) {
                out.println("<xs:sequence>");
                for (TagChild tagChild : annot.value()) {
                    generateChild(out, tagChild, true, library);
                }
                out.println("</xs:sequence>");
            } else if (annot.value().length == 1) {
                TagChild tagChild = annot.value()[0];
                if (isChildAnAgregator(tagChild)) {
                    generateChild(out, tagChild, true, library);
                } else {
                    out.println("<xs:sequence>");
                    generateChild(out, tagChild, true, library);
                    out.println("</xs:sequence>");
                }
            }
        }
    }

    /**
     * 
     * @param out
     * @param widgetFactory
     */
    private void generateChildrenForFactory(PrintStream out, Class<? extends WidgetCreator<?>> widgetFactory,
            String library) {
        try {
            generateChildren(out, library, widgetFactory);
        } catch (Exception e) {
            logger.error("Error creating XSD File: Error generating children for Processor.", e);
        }
    }

    /**
     * 
     * @param out
     * @param widgetFactory
     * @param library
     */
    private void generateChildrenForProcessor(PrintStream out,
            Class<? extends WidgetChildProcessor<?>> processorClass, String library) {
        try {
            generateChildren(out, library, processorClass);
        } catch (Exception e) {
            logger.error("Error creating XSD File: ProcessChildren method not found.", e);
        }
    }

    /**
     * 
     * @param out
     * @param template
     */
    private void generateChildrenForTemplate(PrintStream out, Document template) {
        Set<String> childrenForTemplate = templateParser.getSections(template);

        if (childrenForTemplate.size() > 0) {
            out.println("<xs:all>");
            for (String section : childrenForTemplate) {
                out.println("<xs:element type=\"xs:anyType\" name=\"" + section + "\" />");
            }
            out.println("</xs:all>");
        }
    }

    /**
     * 
     * @param out
     * @param attributes
     * @param children
     * @throws NoSuchMethodException
     */
    private void generateChoiceChild(PrintStream out, TagConstraints attributes, TagChildren children,
            String library) throws NoSuchMethodException {
        out.print("<xs:choice ");
        if (attributes != null) {
            out.print("minOccurs=\"" + attributes.minOccurs() + "\" ");
            out.print("maxOccurs=\"" + attributes.maxOccurs() + "\" ");
        }
        out.println(">");
        for (TagChild child : children.value()) {
            generateChild(out, child, true, library);
        }
        out.println("</xs:choice>");
    }

    /**
     * 
     * @param libraries
     * @param templateLibraries 
     */
    private void generateCoreSchema(Set<String> libraries, Set<String> templateLibraries) {
        try {
            File coreFile = new File(destDir, "core.xsd");
            if (coreFile.exists()) {
                coreFile.delete();
            }
            coreFile.createNewFile();

            String targetNS = "http://www.cruxframework.org/crux";
            registerNamespaceForCatalog(targetNS, coreFile);

            PrintStream out = new PrintStream(coreFile);
            out.println("<xs:schema ");
            out.println("xmlns=\"http://www.cruxframework.org/crux\" ");
            out.println("xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" ");
            for (String lib : libraries) {
                out.println("xmlns:_" + lib + "=\"http://www.cruxframework.org/crux/" + lib + "\" ");
            }
            for (String lib : templateLibraries) {
                out.println("xmlns:_" + lib + "=\"http://www.cruxframework.org/templates/" + lib + "\" ");
            }
            out.println("attributeFormDefault=\"unqualified\" ");
            out.println("elementFormDefault=\"qualified\" ");
            out.println("targetNamespace=\"" + targetNS + "\" >");

            generateCoreSchemasImport(libraries, templateLibraries, out);
            generateCoreSplashScreenElement(out);
            generateCoreScreenElement(out);
            generateCoreCrossDeviceElement(out);
            generateCoreWidgetsType(out, libraries, templateLibraries);
            generateCoreCrossDevWidgetsType(out, libraries, templateLibraries);
            generateCoreTypesSupportingBinding(out);

            out.println("</xs:schema>");
            out.close();
        } catch (Exception e) {
            throw new SchemaGeneratorException(e.getMessage(), e);
        }
    }

    /**
     * 
     * @param libraries
     * @param templateLibraries 
     */
    private void generateOfflineSchema() {
        try {
            File coreFile = new File(destDir, "offline.xsd");
            if (coreFile.exists()) {
                coreFile.delete();
            }
            coreFile.createNewFile();

            String targetNS = "http://www.cruxframework.org/offline";
            registerNamespaceForCatalog(targetNS, coreFile);

            PrintStream out = new PrintStream(coreFile);
            out.println("<xs:schema ");
            out.println("xmlns=\"http://www.cruxframework.org/offline\" ");
            out.println("xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" ");
            out.println("targetNamespace=\"" + targetNS + "\" >");

            generateOfflineScreenElement(out);

            out.println("</xs:schema>");
            out.close();
        } catch (Exception e) {
            throw new SchemaGeneratorException(e.getMessage(), e);
        }
    }

    /**
     * 
     * @param libraries
     * @param templateLibraries 
     * @param out
     */
    private void generateCoreSchemasImport(Set<String> libraries, Set<String> templateLibraries, PrintStream out) {
        for (String lib : libraries) {
            out.println("<xs:import schemaLocation=\"" + lib
                    + ".xsd\" namespace=\"http://www.cruxframework.org/crux/" + lib + "\"/>");
        }
        for (String lib : templateLibraries) {
            out.println("<xs:import schemaLocation=\"" + lib
                    + ".xsd\" namespace=\"http://www.cruxframework.org/templates/" + lib + "\"/>");
        }
    }

    /**
     * 
     * @param out
     */
    private void generateCoreScreenElement(PrintStream out) {
        out.println("<xs:element name=\"screen\" type=\"Screen\">");
        out.println("<xs:annotation>");
        out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.screenDescription())
                + "</xs:documentation>");
        out.println("</xs:annotation>");
        out.println("</xs:element>");

        out.println("<xs:group name=\"ScreenContent\">");
        out.println("<xs:choice>");
        out.println("<xs:any minOccurs=\"0\" maxOccurs=\"unbounded\"/>");
        out.println("</xs:choice>");
        out.println("</xs:group>");

        out.println("<xs:complexType name=\"Screen\" mixed=\"true\">");
        out.println("<xs:group ref=\"ScreenContent\" />");
        generateElementAttributesForAllViewElements(out);
        out.println("<xs:attribute name=\"smallViewport\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"largeViewport\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"disableRefresh\" type=\"xs:boolean\" default=\"false\" />");
        out.println("</xs:complexType>");
    }

    /**
     * 
     * @param out
     */
    private void generateOfflineScreenElement(PrintStream out) {
        out.println("<xs:element name=\"includes\" type=\"Includes\">");
        out.println("<xs:annotation>");
        out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.offlineIncludesDescription())
                + "</xs:documentation>");
        out.println("</xs:annotation>");
        out.println("</xs:element>");

        out.println("<xs:element name=\"excludes\" type=\"Excludes\">");
        out.println("<xs:annotation>");
        out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.offlineExcludesDescription())
                + "</xs:documentation>");
        out.println("</xs:annotation>");
        out.println("</xs:element>");

        out.println("<xs:element name=\"include\" type=\"Include\">");
        out.println("<xs:annotation>");
        out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.offlineIncludesDescription())
                + "</xs:documentation>");
        out.println("</xs:annotation>");
        out.println("</xs:element>");

        out.println("<xs:element name=\"exclude\" type=\"Exclude\">");
        out.println("<xs:annotation>");
        out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.offlineExcludesDescription())
                + "</xs:documentation>");
        out.println("</xs:annotation>");
        out.println("</xs:element>");

        out.println("<xs:complexType name=\"Includes\" mixed=\"true\">");
        out.println("<xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">");
        out.println("<xs:element ref=\"include\" />");
        out.println("</xs:choice>");
        out.println("</xs:complexType>");

        out.println("<xs:complexType name=\"Excludes\" mixed=\"true\">");
        out.println("<xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">");
        out.println("<xs:element ref=\"exclude\" />");
        out.println("</xs:choice>");
        out.println("</xs:complexType>");

        out.println("<xs:complexType name=\"Include\" mixed=\"true\">");
        out.println("<xs:attribute name=\"path\" type=\"xs:string\" use=\"required\"/>");
        out.println("</xs:complexType>");

        out.println("<xs:complexType name=\"Exclude\" mixed=\"true\">");
        out.println("<xs:attribute name=\"path\" type=\"xs:string\" use=\"required\"/>");
        out.println("</xs:complexType>");

        out.println("<xs:element name=\"offlineScreen\" type=\"OfflineScreen\">");
        out.println("<xs:annotation>");
        out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.offlineScreenDescription())
                + "</xs:documentation>");
        out.println("</xs:annotation>");
        out.println("</xs:element>");
        out.println("<xs:complexType name=\"OfflineScreen\" mixed=\"true\">");
        out.println("<xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">");
        out.println("<xs:element ref=\"includes\" />");
        out.println("<xs:element ref=\"excludes\" />");
        out.println("</xs:choice>");
        out.println("<xs:attribute name=\"moduleName\" type=\"xs:string\" use=\"required\"/>");
        out.println("<xs:attribute name=\"screenId\" type=\"xs:string\" use=\"required\"/>");
        out.println("</xs:complexType>");
    }

    /**
     * 
     * @param out
     */
    private void generateElementAttributesForAllViewElements(PrintStream out) {
        out.println("<xs:attribute name=\"title\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"fragment\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"useController\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"useResource\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"useFormatter\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"useDataSource\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"useView\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"onClosing\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"onClose\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"onResized\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"onLoad\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"onActivate\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"onHistoryChanged\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"width\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"height\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"dataObject\" type=\"xs:string\"/>");
    }

    /**
     * 
     * @param out
     */
    private void generateCoreSplashScreenElement(PrintStream out) {
        out.println("<xs:element name=\"splashScreen\" type=\"SplashScreen\">");
        out.println("<xs:annotation>");
        out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.splashScreenDescription())
                + "</xs:documentation>");
        out.println("</xs:annotation>");
        out.println("</xs:element>");

        out.println("<xs:complexType name=\"SplashScreen\">");
        out.println("<xs:attribute name=\"style\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"transactionDelay\" type=\"xs:integer\"/>");
        out.println("</xs:complexType>");
    }

    /**
     * 
     * @param out
     */
    private void generateCoreCrossDeviceElement(PrintStream out) {
        out.println("<xs:element name=\"crossDevice\" type=\"CrossDevice\">");
        out.println("<xs:annotation>");
        out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.crossDeviceDescription())
                + "</xs:documentation>");
        out.println("</xs:annotation>");
        out.println("</xs:element>");
        out.println("<xs:complexType name=\"CrossDevice\">");
        out.println("<xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">");
        out.println("<xs:group ref=\"widgetsCrossDev\" />");
        out.println("<xs:element name=\"conditions\" type=\"CrossDeviceConditions\" >");
        out.println("<xs:annotation>");
        out.println("<xs:documentation>"
                + StringEscapeUtils.escapeXml(schemaMessages.crossDeviceConditionsDescription())
                + "</xs:documentation>");
        out.println("</xs:annotation>");
        out.println("</xs:element>");
        out.println("</xs:choice>");
        out.println("</xs:complexType>");

        out.println("<xs:complexType name=\"CrossDeviceConditions\">");
        out.println("<xs:sequence minOccurs=\"1\" maxOccurs=\"unbounded\">");
        out.println("<xs:element name=\"condition\" type=\"CrossDeviceCondition\" >");
        out.println("<xs:annotation>");
        out.println(
                "<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.crossDeviceConditionDescription())
                        + "</xs:documentation>");
        out.println("</xs:annotation>");
        out.println("</xs:element>");
        out.println("</xs:sequence>");
        out.println("</xs:complexType>");

        out.println("<xs:complexType name=\"CrossDeviceCondition\">");
        out.println("<xs:choice minOccurs=\"1\" maxOccurs=\"unbounded\">");
        out.println("<xs:element name=\"parameter\" type=\"CrossDeviceParameterCondition\" >");
        out.println("<xs:annotation>");
        out.println("<xs:documentation>"
                + StringEscapeUtils.escapeXml(schemaMessages.crossDeviceConditionParameterDescription())
                + "</xs:documentation>");
        out.println("</xs:annotation>");
        out.println("</xs:element>");
        out.println("</xs:choice>");
        out.println("<xs:attribute name=\"when\" type=\"DeviceType\" use=\"required\"/>");
        out.println("</xs:complexType>");

        out.println("<xs:complexType name=\"CrossDeviceParameterCondition\">");
        out.println("<xs:attribute name=\"name\" type=\"xs:string\" use=\"required\"/>");
        out.println("<xs:attribute name=\"value\" type=\"xs:string\" use=\"required\"/>");
        out.println("</xs:complexType>");

        out.println("<xs:simpleType name=\"DeviceType\">");
        out.println("<xs:restriction base=\"xs:string\">");
        Device[] values = Device.values();
        for (Device device : values) {
            out.println("<xs:enumeration value=\"" + device.toString() + "\" />");
        }
        out.println("</xs:restriction>");
        out.println("</xs:simpleType>");
    }

    /**
     * 
     * @param out
     * @param libraries
     * @param templateLibraries 
     */
    private void generateCoreWidgetsType(PrintStream out, Set<String> libraries, Set<String> templateLibraries) {
        generateCoreWidgetsType(out, libraries, templateLibraries, "widgets", true, true);
    }

    /**
     * 
     * @param out
     * @param libraries
     * @param templateLibraries 
     */
    private void generateCoreCrossDevWidgetsType(PrintStream out, Set<String> libraries,
            Set<String> templateLibraries) {
        generateCoreWidgetsType(out, libraries, templateLibraries, "widgetsCrossDev", false, true);
    }

    /**
     * 
     * @param out
     * @param libraries
     * @param templateLibraries
     * @param groupName
     * @param supportCrossDevice
     */
    private void generateCoreWidgetsType(PrintStream out, Set<String> libraries, Set<String> templateLibraries,
            String groupName, boolean supportCrossDevice, boolean supportTemplates) {
        out.println("<xs:group name=\"" + groupName + "\">");
        out.println("<xs:choice>");

        for (String lib : libraries) {
            Set<String> factories = WidgetConfig.getRegisteredLibraryFactories(lib);
            if (factories != null) {
                for (String factoryID : factories) {
                    out.println("<xs:element ref=\"_" + lib + ":" + factoryID + "\" />");
                }
            }
        }

        if (supportTemplates) {
            for (String lib : templateLibraries) {
                Set<String> templates = Templates.getRegisteredLibraryWidgetTemplates(lib);
                if (templates != null) {
                    for (String templateID : templates) {
                        out.println("<xs:element ref=\"_" + lib + ":" + templateID + "\" />");
                    }
                }
            }
        }

        if (supportCrossDevice) {
            out.println("<xs:element ref=\"crossDevice\" />");
        }
        out.println("</xs:choice>");
        out.println("</xs:group>");
    }

    /**
     * 
     * @param out
     */
    private void generateCoreTypesSupportingBinding(PrintStream out) {
        out.println("<xs:simpleType name=\"_bindableInt\">");
        out.println("<xs:restriction base=\"xs:string\">");
        out.println("<xs:pattern value=\"" + StringEscapeUtils.escapeXml("-?[0-9]+|@\\{.+\\}") + "\"/>");
        out.println("</xs:restriction>");
        out.println("</xs:simpleType>");

        out.println("<xs:simpleType name=\"_bindableBoolean\">");
        out.println("<xs:restriction base=\"xs:string\">");
        out.println("<xs:pattern value=\"" + StringEscapeUtils.escapeXml("false|true|@\\{.+\\}") + "\"/>");
        out.println("</xs:restriction>");
        out.println("</xs:simpleType>");

        out.println("<xs:simpleType name=\"_bindableFloat\">");
        out.println("<xs:restriction base=\"xs:string\">");
        out.println(
                "<xs:pattern value=\"" + StringEscapeUtils.escapeXml("-?[0-9]+(\\.[0-9]+)?|@\\{.+\\}") + "\"/>");
        out.println("</xs:restriction>");
        out.println("</xs:simpleType>");
    }

    /**
     * 
     * @param out
     * @param added
     * @param processorClass
     */
    private void generateEvents(PrintStream out, Set<String> added, Class<?> processorClass) {
        TagEvents evts = processorClass.getAnnotation(TagEvents.class);
        if (evts != null) {
            for (TagEvent evt : evts.value()) {
                Class<? extends EvtProcessor> evtBinder = evt.value();
                try {
                    String eventName = evtBinder.getConstructor(WidgetCreator.class)
                            .newInstance((WidgetCreator<?>) null).getEventName();
                    if (!added.contains(eventName)) {
                        out.print("<xs:attribute name=\"" + eventName + "\" ");
                        if (evt.required()) {
                            out.print("use=\"required\" ");
                        }
                        out.println(" >");

                        String attrDescription = evt.description();
                        if (attrDescription != null && attrDescription.length() > 0) {
                            out.println("<xs:annotation>");
                            out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(attrDescription)
                                    + "</xs:documentation>");
                            out.println("</xs:annotation>");
                        }
                        out.println("</xs:attribute>");
                        added.add(eventName);
                    }
                } catch (Exception e) {
                    logger.error("Error creating XSD File: Error generating events for Processor.", e);
                }
            }
        }
        TagEventsDeclaration evtsDecl = processorClass.getAnnotation(TagEventsDeclaration.class);
        if (evtsDecl != null) {
            for (TagEventDeclaration evt : evtsDecl.value()) {
                out.println("<xs:attribute name=\"" + evt.value() + "\" ");
                if (evt.required()) {
                    out.print("use=\"required\" ");
                }
                out.println(" >");
                String attrDescription = evt.description();
                if (attrDescription != null && attrDescription.length() > 0) {
                    out.println("<xs:annotation>");
                    out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(attrDescription)
                            + "</xs:documentation>");
                    out.println("</xs:annotation>");
                }
                out.println("</xs:attribute>");
            }
        }
    }

    /**
     * 
     * @param out
     * @param widgetFactory
     */
    private void generateEventsForFactory(PrintStream out, Class<?> widgetFactory, Set<String> added) {
        generateEvents(out, added, widgetFactory);
        Class<?> superclass = widgetFactory.getSuperclass();
        if (superclass != null && !superclass.equals(Object.class)) {
            generateEventsForFactory(out, superclass, added);
        }
        Class<?>[] interfaces = widgetFactory.getInterfaces();
        for (Class<?> interfaceClass : interfaces) {
            generateEventsForFactory(out, interfaceClass, added);
        }
    }

    /**
     * 
     * @param out
     * @param processorClass
     */
    private void generateEventsForProcessor(PrintStream out, Class<?> processorClass, Set<String> added) {
        try {
            generateEvents(out, added, processorClass);
            Class<?> superclass = processorClass.getSuperclass();
            if (superclass != null && !superclass.equals(Object.class)) {
                generateEventsForProcessor(out, superclass, added);
            }
        } catch (Exception e) {
            logger.error("Error creating XSD File: Error generating events for Processor.", e);
        }
    }

    /**
     * 
     * @param out
     * @param library
     * @param processorClass
     * @param attributes
     */
    private void generateGenericChildWithAttributes(PrintStream out, String library,
            Class<? extends WidgetChildProcessor<?>> processorClass, TagConstraints attributes) {
        Class<?> type = attributes.type();
        String tagName = attributes.tagName();
        String tagDescription = attributes.description();

        if (AnyWidgetChildProcessor.class.isAssignableFrom(processorClass)) {
            out.println("<xs:group ref=\"c:widgets\"  minOccurs=\"" + attributes.minOccurs() + "\" maxOccurs=\""
                    + attributes.maxOccurs() + "\">");
            out.println("<xs:annotation>");
            out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.anyWidgetsDescription())
                    + "</xs:documentation>");
            out.println("</xs:annotation>");
            out.println("</xs:group>");
        } else if (type.equals(Void.class)) {
            if (tagName.length() == 0) {
                logger.error("Error creating XSD File: Tag Name expected in processor class: ["
                        + processorClass.getName() + "].");
            } else {
                out.println("<xs:element name=\"" + tagName + "\" minOccurs=\"" + attributes.minOccurs()
                        + "\" maxOccurs=\"" + attributes.maxOccurs() + "\" type=\""
                        + getSchemaType(processorClass, library, false) + "\">");
                if (tagDescription != null && tagDescription.length() > 0) {
                    out.println("<xs:annotation>");
                    out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(tagDescription)
                            + "</xs:documentation>");
                    out.println("</xs:annotation>");
                }
                out.println("</xs:element>");
            }
        } else if (AnyWidget.class.isAssignableFrom(type)) {
            out.println("<xs:group ref=\"c:widgets\" >");
            out.println("<xs:annotation>");
            out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.anyWidgetsDescription())
                    + "</xs:documentation>");
            out.println("</xs:annotation>");
            out.println("</xs:group>");
        } else if (AnyTag.class.isAssignableFrom(type) && tagName.length() == 0) {
            out.println("<xs:any minOccurs=\"" + attributes.minOccurs() + "\" maxOccurs=\"" + attributes.maxOccurs()
                    + "\" />");
        } else if (HTMLTag.class.isAssignableFrom(type) && tagName.length() == 0) {
            out.println("<xs:any minOccurs=\"" + attributes.minOccurs() + "\" maxOccurs=\"" + attributes.maxOccurs()
                    + "\" namespace=\"http://www.w3.org/1999/xhtml\"/>");
        } else {
            if ((tagName.length() == 0) && (WidgetCreator.class.isAssignableFrom(type))) {
                DeclarativeFactory annot = type.getAnnotation(DeclarativeFactory.class);
                if (annot != null) {
                    tagName = annot.id();
                }
            }
            out.println(
                    "<xs:element name=\"" + tagName + "\" minOccurs=\"" + attributes.minOccurs() + "\" maxOccurs=\""
                            + attributes.maxOccurs() + "\" type=\"" + getSchemaType(type, library, false) + "\">");
            if (tagDescription != null && tagDescription.length() > 0) {
                out.println("<xs:annotation>");
                out.println(
                        "<xs:documentation>" + StringEscapeUtils.escapeXml(tagDescription) + "</xs:documentation>");
                out.println("</xs:annotation>");
            }
            out.println("</xs:element>");
        }
    }

    /**
     * 
     * @param library
     * @throws ClassNotFoundException 
     */
    @SuppressWarnings("unchecked")
    private void generateSchemaForLibrary(String library, Set<String> allLibraries, Set<String> templateLibraries) {
        try {
            File coreFile = new File(destDir, library + ".xsd");
            if (coreFile.exists()) {
                coreFile.delete();
            }
            coreFile.createNewFile();
            PrintStream out = new PrintStream(coreFile);

            String targetNS = "http://www.cruxframework.org/crux/" + library;
            registerNamespaceForCatalog(targetNS, coreFile);

            out.println("<xs:schema ");
            out.println("xmlns=\"http://www.cruxframework.org/crux/" + library + "\" ");
            out.println("xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" ");
            out.println("xmlns:c=\"http://www.cruxframework.org/crux\" ");
            for (String lib : allLibraries) {
                if (!lib.equals(library)) {
                    out.println("xmlns:_" + lib + "=\"http://www.cruxframework.org/crux/" + lib + "\" ");
                }
            }
            out.println("attributeFormDefault=\"unqualified\" ");
            out.println("elementFormDefault=\"qualified\" ");
            out.println("targetNamespace=\"" + targetNS + "\" >");

            generateSchemaImportsForLibrary(library, allLibraries, templateLibraries, out);

            Set<String> factories = WidgetConfig.getRegisteredLibraryFactories(library);
            for (String id : factories) {
                Class<? extends WidgetCreator<?>> widgetFactory = (Class<? extends WidgetCreator<?>>) Class
                        .forName(WidgetConfig.getClientClass(library, id));
                generateTypeForFactory(out, widgetFactory, library);
            }

            generateTypesForSubTags(out, library);
            generateTypesForEnumerations(out);

            out.println("</xs:schema>");
            out.close();
        } catch (Exception e) {
            throw new SchemaGeneratorException(e.getMessage(), e);
        }
    }

    /**
     * 
     * @param library
     */
    private void generateSchemaForTemplateLibrary(String library) {
        try {
            File coreFile = new File(destDir, library + ".xsd");
            if (coreFile.exists()) {
                coreFile.delete();
            }
            coreFile.createNewFile();
            PrintStream out = new PrintStream(coreFile);

            String targetNS = "http://www.cruxframework.org/templates/" + library;
            registerNamespaceForCatalog(targetNS, coreFile);

            out.println("<xs:schema ");
            out.println("xmlns=\"http://www.cruxframework.org/templates/" + library + "\" ");
            out.println("xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" ");
            out.println("xmlns:c=\"http://www.cruxframework.org/crux\" ");
            out.println("attributeFormDefault=\"unqualified\" ");
            out.println("elementFormDefault=\"qualified\" ");
            out.println("targetNamespace=\"" + targetNS + "\" >");

            Set<String> templates = Templates.getRegisteredLibraryTemplates(library);
            for (String id : templates) {
                Document template = Templates.getTemplate(library, id);
                generateTypeForTemplate(out, template, id);
            }
            out.println("</xs:schema>");
            out.close();
        } catch (Exception e) {
            throw new SchemaGeneratorException(e.getMessage(), e);
        }
    }

    /**
     * 
     * @param library
     * @param allLibraries
     * @param out
     */
    private void generateSchemaImportsForLibrary(String library, Set<String> allLibraries,
            Set<String> templateLibraries, PrintStream out) {
        out.println("<xs:import schemaLocation=\"core.xsd\" namespace=\"http://www.cruxframework.org/crux\"/>");
        for (String lib : allLibraries) {
            if (!lib.equals(library)) {
                out.println("<xs:import schemaLocation=\"" + lib
                        + ".xsd\" namespace=\"http://www.cruxframework.org/crux/" + lib + "\"/>");
            }
        }
        for (String lib : templateLibraries) {
            if (!lib.equals(library)) {
                out.println("<xs:import schemaLocation=\"" + lib
                        + ".xsd\" namespace=\"http://www.cruxframework.org/templates/" + lib + "\"/>");
            }
        }
    }

    /**
     * 
     * @param out
     * @param attributes
     * @param children
     */
    private void generateSequenceChild(PrintStream out, TagConstraints attributes, TagChildren children,
            String library) {
        try {
            out.print("<xs:sequence ");
            if (attributes != null) {
                out.print("minOccurs=\"" + attributes.minOccurs() + "\" ");
                out.print("maxOccurs=\"" + attributes.maxOccurs() + "\" ");
            }
            out.println(">");
            for (TagChild child : children.value()) {
                generateChild(out, child, true, library);
            }
            out.println("</xs:sequence>");
        } catch (Exception e) {
            throw new SchemaGeneratorException(e.getMessage(), e);
        }
    }

    /**
     * 
     * @param out
     */
    private void generateTemplateElement(PrintStream out) {
        out.println("<xs:element name=\"template\" type=\"Template\" >");
        out.println("<xs:annotation>");
        out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.templateDescription())
                + "</xs:documentation>");
        out.println("</xs:annotation>");
        out.println("</xs:element>");
        out.println("<xs:complexType name=\"Template\">");
        out.println("<xs:choice>");
        out.println("<xs:any minOccurs=\"0\" maxOccurs=\"unbounded\"/>");
        out.println("</xs:choice>");
        out.println("<xs:attribute name=\"library\" type=\"xs:string\" use=\"required\"/>");
        out.println("<xs:attribute name=\"useController\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"useResource\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"useFormatter\" type=\"xs:string\"/>");
        out.println("<xs:attribute name=\"useDataSource\" type=\"xs:string\"/>");
        out.println("</xs:complexType>");
    }

    /**
     * 
     * @param libraries
     * @param templateLibraries
     */
    private void generateXDeviceSchema(Set<String> libraries, Set<String> templateLibraries) {
        try {
            File coreFile = new File(destDir, "xdevice.xsd");
            if (coreFile.exists()) {
                coreFile.delete();
            }
            coreFile.createNewFile();

            String targetNS = "http://www.cruxframework.org/xdevice";
            registerNamespaceForCatalog(targetNS, coreFile);

            PrintStream out = new PrintStream(coreFile);
            out.println("<xs:schema ");
            out.println("xmlns=\"http://www.cruxframework.org/xdevice\" ");
            out.println("xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" ");
            out.println("xmlns:c=\"http://www.cruxframework.org/crux\" ");
            out.println("attributeFormDefault=\"unqualified\" ");
            out.println("elementFormDefault=\"qualified\" ");
            out.println("targetNamespace=\"" + targetNS + "\" >");

            generateViewSchemasImport(libraries, out);

            out.println("<xs:element name=\"xdevice\" type=\"XDevice\" >");
            out.println("<xs:annotation>");
            out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.xdeviceDescription())
                    + "</xs:documentation>");
            out.println("</xs:annotation>");
            out.println("</xs:element>");
            out.println("<xs:complexType name=\"XDevice\">");
            out.println("<xs:choice maxOccurs=\"unbounded\">");
            out.println("<xs:group ref=\"c:widgetsCrossDev\" >");
            out.println("<xs:annotation>");
            out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.anyWidgetsDescription())
                    + "</xs:documentation>");
            out.println("</xs:annotation>");
            out.println("</xs:group>");
            out.println("<xs:any namespace=\"http://www.w3.org/1999/xhtml\"/>");
            out.println("</xs:choice>");
            out.println("<xs:attribute name=\"useController\" type=\"xs:string\" use=\"required\"/>");
            out.println("<xs:attribute name=\"useResource\" type=\"xs:string\"/>");
            out.println("<xs:attribute name=\"width\" type=\"xs:string\"/>");
            out.println("<xs:attribute name=\"height\" type=\"xs:string\"/>");
            out.println("</xs:complexType>");

            out.println("</xs:schema>");
            out.close();
        } catch (Exception e) {
            throw new SchemaGeneratorException(e.getMessage(), e);
        }
    }

    /**
     * 
     * @param libraries
     * @param templateLibraries
     */
    private void generateViewSchema(Set<String> libraries, Set<String> templateLibraries) {
        try {
            File coreFile = new File(destDir, "view.xsd");
            if (coreFile.exists()) {
                coreFile.delete();
            }
            coreFile.createNewFile();

            String targetNS = "http://www.cruxframework.org/view";
            registerNamespaceForCatalog(targetNS, coreFile);

            PrintStream out = new PrintStream(coreFile);
            out.println("<xs:schema ");
            out.println("xmlns=\"http://www.cruxframework.org/view\" ");
            out.println("xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" ");
            out.println("xmlns:c=\"http://www.cruxframework.org/crux\" ");
            out.println("attributeFormDefault=\"unqualified\" ");
            out.println("elementFormDefault=\"qualified\" ");
            out.println("targetNamespace=\"" + targetNS + "\" >");

            generateViewSchemasImport(libraries, out);

            out.println("<xs:element name=\"view\" type=\"View\" >");
            out.println("<xs:annotation>");
            out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.viewDescription())
                    + "</xs:documentation>");
            out.println("</xs:annotation>");
            out.println("</xs:element>");
            out.println("<xs:complexType name=\"View\">");
            out.println("<xs:choice maxOccurs=\"unbounded\">");
            out.println("<xs:group ref=\"c:widgets\" >");
            out.println("<xs:annotation>");
            out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.anyWidgetsDescription())
                    + "</xs:documentation>");
            out.println("</xs:annotation>");
            out.println("</xs:group>");
            out.println("<xs:any namespace=\"http://www.w3.org/1999/xhtml\"/>");
            out.println("</xs:choice>");
            generateElementAttributesForAllViewElements(out);
            out.println("<xs:attribute name=\"onUnload\" type=\"xs:string\"/>");
            out.println("<xs:attribute name=\"onDeactivate\" type=\"xs:string\"/>");
            out.println("</xs:complexType>");

            out.println("</xs:schema>");
            out.close();
        } catch (Exception e) {
            throw new SchemaGeneratorException(e.getMessage(), e);
        }
    }

    /**
     * 
     * @param libraries
     * @param templateLibraries 
     * @param out
     */
    private void generateViewSchemasImport(Set<String> libraries, PrintStream out) {
        out.println("<xs:import schemaLocation=\"core.xsd\" namespace=\"http://www.cruxframework.org/crux\"/>");
        for (String lib : libraries) {
            out.println("<xs:import schemaLocation=\"" + lib
                    + ".xsd\" namespace=\"http://www.cruxframework.org/crux/" + lib + "\"/>");
        }
    }

    /**
     * 
     * @param libraries
     * @param templateLibraries
     */
    private void generateTemplateSchema(Set<String> libraries, Set<String> templateLibraries) {
        try {
            File coreFile = new File(destDir, "template.xsd");
            if (coreFile.exists()) {
                coreFile.delete();
            }
            coreFile.createNewFile();

            String targetNS = "http://www.cruxframework.org/templates";
            registerNamespaceForCatalog(targetNS, coreFile);

            PrintStream out = new PrintStream(coreFile);
            out.println("<xs:schema ");
            out.println("xmlns=\"http://www.cruxframework.org/templates\" ");
            out.println("xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" ");
            out.println("attributeFormDefault=\"unqualified\" ");
            out.println("elementFormDefault=\"qualified\" ");
            out.println("targetNamespace=\"" + targetNS + "\" >");

            generateCoreSchemasImport(libraries, templateLibraries, out);

            generateTemplateElement(out);
            generateTemplateSectionElement(out);

            out.println("</xs:schema>");
            out.close();
        } catch (Exception e) {
            throw new SchemaGeneratorException(e.getMessage(), e);
        }
    }

    /**
     * 
     * @param out
     */
    private void generateTemplateSectionElement(PrintStream out) {
        out.println("<xs:element name=\"section\" type=\"Section\" >");
        out.println("<xs:annotation>");
        out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(schemaMessages.templateSectionDescription())
                + "</xs:documentation>");
        out.println("</xs:annotation>");
        out.println("</xs:element>");
        out.println("<xs:complexType name=\"Section\">");
        out.println("<xs:choice>");
        out.println("<xs:any minOccurs=\"0\" maxOccurs=\"unbounded\"/>");
        out.println("</xs:choice>");
        out.println("<xs:attribute name=\"name\" type=\"xs:string\" use=\"required\"/>");
        out.println("</xs:complexType>");
    }

    /**
     * 
     * @param out
     * @param widgetFactory
     */
    private void generateTypeForFactory(PrintStream out, Class<? extends WidgetCreator<?>> widgetFactory,
            String library) {
        DeclarativeFactory annot = widgetFactory.getAnnotation(DeclarativeFactory.class);
        String elementName = annot.id();

        out.println("<xs:element name=\"" + elementName + "\" type=\"T" + elementName + "\">");

        generateDocumentationForTypeFactory(out, annot);

        out.println("</xs:element>");
        out.println("<xs:complexType name=\"T" + elementName + "\">");
        boolean hasTextChild = factorySupportsInnerText(widgetFactory);
        if (hasTextChild) {
            out.println("<xs:simpleContent>");
            out.println("<xs:extension base=\"xs:string\">");
        } else {
            generateChildrenForFactory(out, widgetFactory, library);
        }
        generateAttributesForFactory(out, widgetFactory, library, new HashSet<String>());
        generateEventsForFactory(out, widgetFactory, new HashSet<String>());
        if (hasTextChild) {
            out.println("</xs:extension>");
            out.println("</xs:simpleContent>");
        }
        out.println("</xs:complexType>");
    }

    /**
     * 
     * @param out
     * @param annot
     */
    private void generateDocumentationForTypeFactory(PrintStream out, DeclarativeFactory annot) {
        String elementDescription = annot.description();
        String demoURL = annot.infoURL();
        String illustration = annot.illustration();
        if ((elementDescription != null && elementDescription.length() > 0)
                || (demoURL != null && demoURL.length() > 0)
                || (illustration != null && illustration.length() > 0)) {
            out.println("<xs:annotation>");
            if (elementDescription != null && elementDescription.length() > 0) {
                out.println("<xs:documentation>" + StringEscapeUtils.escapeXml(elementDescription)
                        + "</xs:documentation>");
            }
            if (demoURL != null && demoURL.length() > 0) {
                out.println("<xs:appinfo source=\"" + StringEscapeUtils.escapeXml(demoURL) + "\">"
                        + StringEscapeUtils.escapeXml(schemaMessages.moreInfoDescription()) + "</xs:appinfo>");
            }
            if (illustration != null && illustration.length() > 0) {
                out.println("<xs:appinfo source=\"" + StringEscapeUtils.escapeXml(illustration) + "\">"
                        + StringEscapeUtils.escapeXml(schemaMessages.illustrationDescription()) + "</xs:appinfo>");
            }
            out.println("</xs:annotation>");
        }
    }

    /**
     * 
     * @param out
     * @param template
     */
    private void generateTypeForTemplate(PrintStream out, Document template, String templateName) {
        out.println("<xs:element name=\"" + templateName + "\" type=\"T" + templateName + "\" />");
        out.println("<xs:complexType name=\"T" + templateName + "\">");

        generateChildrenForTemplate(out, template);
        generateAttributesForTemplate(out, template);

        out.println("</xs:complexType>");
    }

    /**
     * 
     * @param out
     */
    @SuppressWarnings("unchecked")
    private void generateTypesForEnumerations(PrintStream out) {
        for (String enumType : enumTypes.keySet()) {
            out.println("<xs:simpleType name=\"" + enumType + "\">");
            out.println("<xs:restriction base=\"xs:string\">");

            Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) enumTypes.get(enumType);
            Enum<?>[] enumConstants = enumClass.getEnumConstants();
            for (Enum<?> enumConstant : enumConstants) {
                out.println("<xs:enumeration value=\"" + enumConstant.toString() + "\" />");
            }

            out.println("</xs:restriction>");
            out.println("</xs:simpleType>");

        }
        enumTypes.clear();
    }

    /**
     * 
     * @param out
     * @param library
     */
    private void generateTypesForSubTags(PrintStream out, String library) {
        Set<String> added = new HashSet<String>();
        while (subTagTypes.size() > 0) {
            Class<? extends WidgetChildProcessor<?>> type = subTagTypes.pop();
            String elementName = type.getCanonicalName().replace('.', '_');
            if (!added.contains(elementName)) {
                out.println("<xs:complexType name=\"" + elementName + "\">");

                boolean hasTextChild = processorSupportsInnerText(type);
                if (hasTextChild) {
                    out.println("<xs:simpleContent>");
                    out.println("<xs:extension base=\"xs:string\">");
                } else {
                    generateChildrenForProcessor(out, type, library);
                }
                generateAttributesForProcessor(out, type, library, new HashSet<String>());
                generateEventsForProcessor(out, type, new HashSet<String>());
                if (hasTextChild) {
                    out.println("</xs:extension>");
                    out.println("</xs:simpleContent>");
                }
                out.println("</xs:complexType>");
                added.add(elementName);
            }
        }
    }

    /**
     * @param value
     * @return
     */
    private String getRelativeName(File value) {
        String absolutePath = value.toURI().getPath();
        String projectPath = projectBaseDir.toURI().getPath();
        return absolutePath.substring(projectPath.length());
    }

    /**
     * 
     * @param type
     * @param library
     * @return
     */
    @SuppressWarnings("unchecked")
    private String getSchemaType(Class<?> type, String library, boolean supportsBinding) {
        if (String.class.isAssignableFrom(type)) {
            return "xs:string";
        } else if (Boolean.class.isAssignableFrom(type)) {
            return supportsBinding ? "c:_bindableBoolean" : "xs:boolean";
        } else if (Integer.class.isAssignableFrom(type)) {
            return supportsBinding ? "c:_bindableInt" : "xs:int";
        } else if (Short.class.isAssignableFrom(type)) {
            return supportsBinding ? "c:_bindableInt" : "xs:short";
        } else if (Long.class.isAssignableFrom(type)) {
            return supportsBinding ? "c:_bindableInt" : "xs:long";
        } else if (Double.class.isAssignableFrom(type)) {
            return supportsBinding ? "c:_bindableFloat" : "xs:double";
        } else if (Float.class.isAssignableFrom(type)) {
            return supportsBinding ? "c:_bindableFloat" : "xs:float";
        } else if (Character.class.isAssignableFrom(type)) {
            return "xs:string";
        } else if (type.isEnum()) {
            String typeName = type.getCanonicalName().replace('.', '_');
            this.enumTypes.put(typeName, type);
            return typeName;
        } else if (WidgetCreator.class.isAssignableFrom(type)) {
            DeclarativeFactory annot = type.getAnnotation(DeclarativeFactory.class);
            if (annot != null) {
                if (annot.library().equals(library)) {
                    return "T" + annot.id();
                } else {
                    return "_" + annot.library() + ":T" + annot.id();
                }
            }
        } else if (WidgetChildProcessor.class.isAssignableFrom(type)) {
            String typeName = type.getCanonicalName().replace('.', '_');
            this.subTagTypes.add((Class<? extends WidgetChildProcessor<?>>) type);
            return typeName;
        } else if (AnyTag.class.isAssignableFrom(type)) {
            return "xs:anyType";
        } else if (HTMLTag.class.isAssignableFrom(type)) {
            return "xs:anyType";
        } else if (ResourcePrototype.class.isAssignableFrom(type)) {
            return "xs:string";
        }
        return null;
    }

    /**
     * 
     * @param processorClass
     * @return
     */
    private boolean hasTextChild(Class<?> processorClass) {
        TagChildren tagChildren = processorClass.getAnnotation(TagChildren.class);
        return (tagChildren != null && tagChildren.value().length == 1
                && TextChildProcessor.class.isAssignableFrom(tagChildren.value()[0].value()));
    }

    /**
     * 
     * @param tagChild
     * @return
     */
    private boolean isChildAnAgregator(TagChild tagChild) {
        Class<? extends WidgetChildProcessor<?>> processorClass = tagChild.value();
        return processorClass != null && (ChoiceChildProcessor.class.isAssignableFrom(processorClass)
                || SequenceChildProcessor.class.isAssignableFrom(processorClass)
                || AllChildProcessor.class.isAssignableFrom(processorClass));
    }

    /**
     * 
     * @param type
     * @return
     */
    private boolean processorSupportsInnerText(Class<? extends WidgetChildProcessor<?>> processorClass) {
        try {
            return hasTextChild(processorClass);
        } catch (Exception e) {
            return false;
        }
    }
}