Java tutorial
/* * 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; } } }