/* * 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.Iterator; 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.dataprovider.DataProvider; import org.cruxframework.crux.core.client.screen.DeviceAdaptive.Device; import org.cruxframework.crux.core.client.screen.DeviceAdaptive.Input; import org.cruxframework.crux.core.client.screen.DeviceAdaptive.Size; import org.cruxframework.crux.core.config.ConfigurationFactory; import org.cruxframework.crux.core.declarativeui.template.TemplateParser; import org.cruxframework.crux.core.i18n.MessagesFactory; import org.cruxframework.crux.core.rebind.screen.widget.EvtProcessor; import org.cruxframework.crux.core.rebind.screen.widget.WidgetCreator; import org.cruxframework.crux.core.rebind.screen.widget.WidgetLibraries; 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.rebind.screen.widget.declarative.TagAttribute.WidgetReference; 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.cruxframework.crux.tools.scanner.template.Templates; 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 SchemaMessages schemaMessages; protected Stack<Class<? extends WidgetChildProcessor<?>>> subTagTypes; protected TemplateParser templateParser; 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); } } @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); } } /** * @see org.cruxframework.crux.tools.schema.CruxSchemaGenerator#generateSchemas() */ public void generateSchemas() throws SchemaGeneratorException { try { Iterator<String> libraries = WidgetLibraries.getInstance().iterateRegisteredLibraries(); Set<String> templateLibraries = Templates.getRegisteredLibraries(); while (libraries.hasNext()) { String library = libraries.next(); logger.info("Generating xsd file for library ["+library+"]"); generateSchemaForLibrary(library, templateLibraries); } logger.info("Generating template.xsd file."); generateTemplateSchema(templateLibraries); for (String library : templateLibraries) { logger.info("Generating XSD file for library ["+library+"]"); generateSchemaForTemplateLibrary(library); } logger.info("Generating core.xsd file"); generateCoreSchema(templateLibraries); generateOfflineSchema(); generateXDeviceSchema(templateLibraries); generateViewSchema(templateLibraries); copyXHTMLSchema(); logger.info("XSD Files Generated."); } catch (Exception e) { throw new SchemaGeneratorException(e.getMessage(), e); } } protected 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 */ protected boolean factorySupportsInnerText(Class<? extends WidgetCreator<?>> widgetFactory) { try { return hasTextChild(widgetFactory); } catch (Exception e) { return false; } } /** * * @param out * @param attributes * @param children * @throws NoSuchMethodException */ protected 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 */ protected 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 */ protected 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 */ protected void generateAttributesForProcessor(PrintStream out, Class<?> processorClass, String library, Set<String> added) { try { TagConstraints attributes = ViewUtils.getChildTagConstraintsAnnotation(processorClass); if (attributes != null && attributes.applyDeviceFilters()) { out.println("<xs:attribute name=\"input\" type=\"c:Input\" />"); out.println("<xs:attribute name=\"size\" type=\"c:Size\" />"); } 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 */ protected 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 */ protected 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 */ protected 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 */ protected 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 [" + widgetFactory.getCanonicalName() + "].", e); } } /** * * @param out * @param widgetFactory * @param library */ protected 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 */ protected 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 */ protected 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 out */ protected 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 templateLibraries */ protected void generateCoreCrossDevWidgetsType(PrintStream out, Set<String> templateLibraries) { generateCoreWidgetsType(out, templateLibraries, "widgetsCrossDev", false, true); } /** * * @param templateLibraries */ protected void generateCoreSchema(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\" "); Iterator<String> libraries = WidgetLibraries.getInstance().iterateRegisteredLibraries(); while (libraries.hasNext()) { String lib = libraries.next(); 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 + "\" >"); generateCoreSchemaBody(templateLibraries, out); out.println("</xs:schema>"); out.close(); } catch (Exception e) { throw new SchemaGeneratorException(e.getMessage(), e); } } protected void generateCoreSchemaBody(Set<String> templateLibraries, PrintStream out) { generateCoreSchemasImport(templateLibraries, out); generateCoreSplashScreenElement(out); generateCoreScreenElement(out); generateCoreDataProviderElements(out); generateCoreCrossDeviceElement(out); generateCoreWidgetsType(out, templateLibraries); generateCoreCrossDevWidgetsType(out, templateLibraries); generateCoreTypesSupportingBinding(out); generateCoreIfDeviceElement(out); } protected void generateCoreIfDeviceElement(PrintStream out) { out.println("<xs:element name=\"ifDevice\" type=\"IfDevice\">"); out.println("<xs:annotation>"); out.println("<xs:documentation>"+"A rendering condition that allows developers to switch the implementation according to the device."+"</xs:documentation>"); out.println("</xs:annotation>"); out.println("</xs:element>"); out.println("<xs:complexType name=\"IfDevice\">"); out.println("<xs:sequence>"); out.println("<xs:element name=\"excludes\" type=\"IfDeviceExclusions\" minOccurs=\"0\" maxOccurs=\"1\">"); out.println("<xs:annotation>"); out.println("<xs:documentation>"+"Exclude any devices that will no make apart of the rule declared above."+"</xs:documentation>"); out.println("</xs:annotation>"); out.println("</xs:element>"); out.println("<xs:group ref=\"widgets\" minOccurs=\"0\" maxOccurs=\"unbounded\">"); out.println("<xs:annotation>"); out.println("<xs:documentation>Accepts any valid widget</xs:documentation>"); out.println("</xs:annotation>"); out.println("</xs:group>"); out.println("</xs:sequence>"); out.println("<xs:attribute name=\"input\" type=\"Input\" />"); out.println("<xs:attribute name=\"size\" type=\"Size\" />"); out.println("</xs:complexType>"); out.println("<xs:complexType name=\"IfDeviceExclusions\">"); out.println("<xs:sequence minOccurs=\"1\" maxOccurs=\"unbounded\">"); out.println("<xs:element name=\"exclude\" type=\"IfDeviceExclusion\">"); out.println("<xs:annotation>"); out.println("<xs:documentation>A exclusion to indicate that this device will not accept the above property.</xs:documentation>"); out.println("</xs:annotation>"); out.println("</xs:element>"); out.println("</xs:sequence>"); out.println("</xs:complexType>"); out.println("<xs:complexType name=\"IfDeviceExclusion\">"); out.println("<xs:attribute name=\"input\" type=\"Input\" />"); out.println("<xs:attribute name=\"size\" type=\"Size\" />"); out.println("</xs:complexType>"); out.println("<xs:simpleType name=\"Input\">"); out.println("<xs:restriction base=\"xs:string\">"); Input[] inputs = Input.values(); for (Input input : inputs) { out.println("<xs:enumeration value=\""+input.toString()+"\" />"); } out.println("</xs:restriction>"); out.println("</xs:simpleType>"); out.println("<xs:simpleType name=\"Size\">"); out.println("<xs:restriction base=\"xs:string\">"); Size[] sizes = Size.values(); for (Size size : sizes) { out.println("<xs:enumeration value=\""+size.toString()+"\" />"); } out.println("</xs:restriction>"); out.println("</xs:simpleType>"); } /** * * @param templateLibraries * @param out */ protected void generateCoreSchemasImport(Set<String> templateLibraries, PrintStream out) { Iterator<String> libraries = WidgetLibraries.getInstance().iterateRegisteredLibraries(); while (libraries.hasNext()) { String lib = libraries.next(); 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 */ protected 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 */ protected void generateCoreDataProviderElements(PrintStream out) { out.println("<xs:element name=\"dataProvider\" type=\"DataProvider\">"); out.println("<xs:annotation>"); out.println("<xs:documentation>"+StringEscapeUtils.escapeXml(schemaMessages.dataProviderDescription())+"</xs:documentation>"); out.println("</xs:annotation>"); out.println("</xs:element>"); out.println("<xs:complexType name=\"DataProvider\">"); generateCoreDataProviderCommonAttributes(out); out.println("<xs:attribute name=\"onLoadData\" type=\"xs:string\" use=\"required\" />"); out.println("<xs:attribute name=\"onDataFiltered\" type=\"xs:string\" />"); out.println("</xs:complexType>"); out.println("<xs:element name=\"lazyDataProvider\" type=\"LazyDataProvider\">"); out.println("<xs:annotation>"); out.println("<xs:documentation>"+StringEscapeUtils.escapeXml(schemaMessages.lazyDataProviderDescription())+"</xs:documentation>"); out.println("</xs:annotation>"); out.println("</xs:element>"); out.println("<xs:complexType name=\"LazyDataProvider\">"); generateCoreDataProviderCommonAttributes(out); out.println("<xs:attribute name=\"onMeasureData\" type=\"xs:string\" use=\"required\" />"); out.println("<xs:attribute name=\"onFetchData\" type=\"xs:string\" use=\"required\" />"); out.println("</xs:complexType>"); out.println("<xs:element name=\"streamingDataProvider\" type=\"StreamingDataProvider\">"); out.println("<xs:annotation>"); out.println("<xs:documentation>"+StringEscapeUtils.escapeXml(schemaMessages.streamingDataProviderDescription())+"</xs:documentation>"); out.println("</xs:annotation>"); out.println("</xs:element>"); out.println("<xs:complexType name=\"StreamingDataProvider\">"); out.println("<xs:attribute name=\"onFetchData\" type=\"xs:string\" use=\"required\" />"); generateCoreDataProviderCommonAttributes(out); out.println("</xs:complexType>"); out.println("<xs:simpleType name=\"SelectionMode\">"); out.println("<xs:restriction base=\"xs:string\">"); DataProvider.SelectionMode[] selectionModes = DataProvider.SelectionMode.values(); for (DataProvider.SelectionMode selectionMode : selectionModes) { out.println("<xs:enumeration value=\""+selectionMode.toString()+"\" />"); } out.println("</xs:restriction>"); out.println("</xs:simpleType>"); } private void generateCoreDataProviderCommonAttributes(PrintStream out) { out.println("<xs:attribute name=\"id\" type=\"xs:string\" use=\"required\" />"); out.println("<xs:attribute name=\"dataObject\" type=\"xs:string\" use=\"required\" />"); out.println("<xs:attribute name=\"pageSize\" type=\"xs:integer\" />"); out.println("<xs:attribute name=\"autoLoadData\" type=\"xs:boolean\" default=\"false\" />"); out.println("<xs:attribute name=\"selectionMode\" type=\"SelectionMode\" default=\"multiple\" />"); out.println("<xs:attribute name=\"input\" type=\"Input\" />"); out.println("<xs:attribute name=\"size\" type=\"Size\" />"); out.println("<xs:attribute name=\"onDataChanged\" type=\"xs:string\" />"); out.println("<xs:attribute name=\"onDataLoaded\" type=\"xs:string\" />"); out.println("<xs:attribute name=\"onDataSelected\" type=\"xs:string\" />"); out.println("<xs:attribute name=\"onDataSorted\" type=\"xs:string\" />"); out.println("<xs:attribute name=\"onLoadStopped\" type=\"xs:string\" />"); out.println("<xs:attribute name=\"onReset\" type=\"xs:string\" />"); out.println("<xs:attribute name=\"onTransactionEnd\" type=\"xs:string\" />"); out.println("<xs:attribute name=\"onTransactionStart\" type=\"xs:string\" />"); } /** * * @param out */ protected 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:simpleContent>"); out.println("<xs:extension base=\"xs:string\">"); out.println("<xs:attribute name=\"style\" type=\"xs:string\"/>"); out.println("<xs:attribute name=\"transactionDelay\" type=\"xs:integer\"/>"); out.println("</xs:extension>"); out.println("</xs:simpleContent>"); out.println("</xs:complexType>"); } /** * * @param out */ protected 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 templateLibraries */ protected void generateCoreWidgetsType(PrintStream out, Set<String> templateLibraries) { generateCoreWidgetsType(out, templateLibraries, "widgets", true, true); } /** * * @param out * @param templateLibraries * @param groupName * @param supportCrossDevice */ protected void generateCoreWidgetsType(PrintStream out, Set<String> templateLibraries, String groupName, boolean supportCrossDevice, boolean supportTemplates) { out.println("<xs:group name=\""+groupName+"\">"); out.println("<xs:choice>"); Iterator<String> libraries = WidgetLibraries.getInstance().iterateRegisteredLibraries(); while (libraries.hasNext()) { String lib = libraries.next(); Iterator<String> factories = WidgetLibraries.getInstance().iterateRegisteredLibraryWidgetCreators(lib); if (factories != null) { while (factories.hasNext()) { String factoryID = factories.next(); 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:element ref=\"ifDevice\" />"); } out.println("</xs:choice>"); out.println("</xs:group>"); } /** * * @param out * @param annot */ protected 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 */ protected 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\"/>"); out.println("<xs:attribute name=\"styleName\" type=\"xs:string\"/>"); } /** * * @param out * @param added * @param processorClass */ protected 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 */ protected 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 */ protected 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 */ protected 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\" 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 (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>"); } } /** * */ protected 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 out */ protected 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 library * @throws ClassNotFoundException */ @SuppressWarnings("unchecked") protected void generateSchemaForLibrary(String library, 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\" "); Iterator<String> libraries = WidgetLibraries.getInstance().iterateRegisteredLibraries(); while (libraries.hasNext()) { String lib = libraries.next(); 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, templateLibraries, out); Iterator<String> factories = WidgetLibraries.getInstance().iterateRegisteredLibraryWidgetCreators(library); while (factories.hasNext()) { String id = factories.next(); Class<? extends WidgetCreator<?>> widgetFactory = (Class<? extends WidgetCreator<?>>) Class.forName(WidgetLibraries.getInstance().getFactoryClass(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 */ protected 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 templateLibraries * @param out */ protected void generateSchemaImportsForLibrary(String library, Set<String> templateLibraries, PrintStream out) { out.println("<xs:import schemaLocation=\"core.xsd\" namespace=\"http://www.cruxframework.org/crux\"/>"); Iterator<String> libraries = WidgetLibraries.getInstance().iterateRegisteredLibraries(); while (libraries.hasNext()) { String lib = libraries.next(); 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 */ protected 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 */ protected 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 templateLibraries */ protected void generateTemplateSchema(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(templateLibraries, out); generateTemplateElement(out); generateTemplateSectionElement(out); out.println("</xs:schema>"); out.close(); } catch (Exception e) { throw new SchemaGeneratorException(e.getMessage(), e); } } /** * * @param out */ protected 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 */ protected 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 template */ protected 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") protected 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 */ protected 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 templateLibraries */ protected void generateViewSchema(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(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:element ref=\"c:dataProvider\" />"); out.println("<xs:element ref=\"c:lazyDataProvider\" />"); out.println("<xs:element ref=\"c:streamingDataProvider\" />"); 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 out */ protected void generateViewSchemasImport(PrintStream out) { out.println("<xs:import schemaLocation=\"core.xsd\" namespace=\"http://www.cruxframework.org/crux\"/>"); Iterator<String> libraries = WidgetLibraries.getInstance().iterateRegisteredLibraries(); while (libraries.hasNext()) { String lib = libraries.next(); out.println("<xs:import schemaLocation=\""+lib+".xsd\" namespace=\"http://www.cruxframework.org/crux/"+lib+"\"/>"); } } /** * * @param templateLibraries */ protected void generateXDeviceSchema(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(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 value * @return */ protected 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") protected String getSchemaType(Class<?> type, String library, boolean supportsBinding) { if (String.class.isAssignableFrom(type)) { return "xs:string"; } else if (WidgetReference.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 */ protected 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 projectBaseDir * @param webDir */ protected void initializeSchemaGenerator(File projectBaseDir, File webDir) { try { URL[] urls = ClasspathUrlFinder.findClassPaths(); Scanners.setSearchURLs(urls); if (webDir == null) { webDir = new File(projectBaseDir, "war/"); } } catch (Exception e) { throw new SchemaGeneratorException(e.getMessage(), e); } } /** * * @param tagChild * @return */ protected 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 */ protected boolean processorSupportsInnerText(Class<? extends WidgetChildProcessor<?>> processorClass) { try { return hasTextChild(processorClass); } catch (Exception e) { return false; } } /** * @param targetNS * @param coreFile */ protected void registerNamespaceForCatalog(String targetNS, File file) { this.namespacesForCatalog.put(targetNS, file); } }