Java tutorial
/* * * Artistic License * * Preamble * * The intent of this document is to state the conditions under which a Package may be copied, such that * the Copyright Holder maintains some semblance of artistic control over the development of the * package, while giving the users of the package the right to use and distribute the Package in a * more-or-less customary fashion, plus the right to make reasonable modifications. * * Definitions: * * "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives * of that collection of files created through textual modification. * * "Standard Version" refers to such a Package if it has not been modified, or has been modified * in accordance with the wishes of the Copyright Holder. * * "Copyright Holder" is whoever is named in the copyright or copyrights for the package. * * "You" is you, if you're thinking about copying or distributing this Package. * * "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication * charges, time of people involved, and so on. (You will not be required to justify it to the * Copyright Holder, but only to the computing community at large as a market that must bear the * fee.) * * "Freely Available" means that no fee is charged for the item itself, though there may be fees * involved in handling the item. It also means that recipients of the item may redistribute it under * the same conditions they received it. * * 1. You may make and give away verbatim copies of the source form of the Standard Version of this * Package without restriction, provided that you duplicate all of the original copyright notices and * associated disclaimers. * * 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain * or from the Copyright Holder. A Package modified in such a way shall still be considered the * Standard Version. * * 3. You may otherwise modify your copy of this Package in any way, provided that you insert a * prominent notice in each changed file stating how and when you changed that file, and provided that * you do at least ONE of the following: * * a) place your modifications in the Public Domain or otherwise make them Freely * Available, such as by posting said modifications to Usenet or an equivalent medium, or * placing the modifications on a major archive site such as ftp.uu.net, or by allowing the * Copyright Holder to include your modifications in the Standard Version of the Package. * * b) use the modified Package only within your corporation or organization. * * c) rename any non-standard executables so the names do not conflict with standard * executables, which must also be provided, and provide a separate manual page for each * non-standard executable that clearly documents how it differs from the Standard * Version. * * d) make other distribution arrangements with the Copyright Holder. * * 4. You may distribute the programs of this Package in object code or executable form, provided that * you do at least ONE of the following: * * a) distribute a Standard Version of the executables and library files, together with * instructions (in the manual page or equivalent) on where to get the Standard Version. * * b) accompany the distribution with the machine-readable source of the Package with * your modifications. * * c) accompany any non-standard executables with their corresponding Standard Version * executables, giving the non-standard executables non-standard names, and clearly * documenting the differences in manual pages (or equivalent), together with instructions * on where to get the Standard Version. * * d) make other distribution arrangements with the Copyright Holder. * * 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge * any fee you choose for support of this Package. You may not charge a fee for this Package itself. * However, you may distribute this Package in aggregate with other (possibly commercial) programs as * part of a larger (possibly commercial) software distribution provided that you do not advertise this * Package as a product of your own. * * 6. The scripts and library files supplied as input to or produced as output from the programs of this * Package do not automatically fall under the copyright of this Package, but belong to whomever * generated them, and may be sold commercially, and may be aggregated with this Package. * * 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of * this Package. * * 8. The name of the Copyright Holder may not be used to endorse or promote products derived from * this software without specific prior written permission. * * 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * */ package org.chiba.tools.schemabuilder; import org.apache.commons.jxpath.JXPathContext; import org.apache.commons.jxpath.Pointer; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.Layout; import org.apache.log4j.SimpleLayout; import org.apache.xerces.xs.*; import org.chiba.xml.util.DOMUtil; import org.chiba.xml.xforms.NamespaceCtx; import org.w3c.dom.*; import org.w3c.dom.bootstrap.DOMImplementationRegistry; import org.xml.sax.InputSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.IOException; import java.io.StringWriter; import java.util.*; /* * Search for TODO for things remaining to-do in this implementation. * * TODO: Support configuration mechanism to allow properties to be set without programming. * TODO: i18n/l10n of messages, hints, captions. Possibly leverage org.chiba.i18n classes. * TODO: When Chiba supports itemset, use schema keyref and key constraints for validation. * TODO: Support namespaces in instance documents. Currently can't do this due to Chiba bugs. * TODO: Place default values for list and enumeration types at the beginning of the item list. * */ /** * An abstract implementation of the SchemaFormBuilder interface allowing * an XForm to be automatically generated for an XML Schema definition. * This abstract class implements the buildForm and buildFormAsString methods * and associated helper but relies on concrete subclasses to implement other * required interface methods (createXXX, startXXX, and endXXX methods). * * @author $Author: joernt $ * @version $Id: AbstractSchemaFormBuilder.java,v 1.22 2004/12/06 23:41:52 joernt Exp $ */ public abstract class AbstractSchemaFormBuilder implements SchemaFormBuilder { private static final String PROPERTY_PREFIX = "http://www.chiba.org/properties/schemaFormBuilder/"; /** * Property to control the cascading style sheet used for the XForm - corresponds to envelope@chiba:css-style. */ public static final String CSS_STYLE_PROP = PROPERTY_PREFIX + "envelope@css-style"; private static final String DEFAULT_CSS_STYLE_PROP = "style.css"; /** * Property to control the selection of UI control for a selectOne control. * If a selectOne control has >= the number of values specified in this property, * it is considered a <b>long</b> list, and the UI control specified by * SELECTONE_UI_CONTROL_LONG_PROP is used. Otherwise, the value of SELECTONE_UI_CONTROL_SHORT_PROP * is used. */ public static final String SELECTONE_LONG_LIST_SIZE_PROP = PROPERTY_PREFIX + "select1@longListSize"; /** * Property to specify the selectMany UI control to be used when there are releatively few items * to choose from. */ public static final String SELECTONE_UI_CONTROL_SHORT_PROP = PROPERTY_PREFIX + "select1@appearance/short"; /** * Property to specify the selectMany UI control to be used when there are large numbers of items * to choose from. */ public static final String SELECTONE_UI_CONTROL_LONG_PROP = PROPERTY_PREFIX + "select1@appearance/long"; private static final String DEFAULT_SELECTONE_UI_CONTROL_SHORT_PROP = "full"; private static final String DEFAULT_SELECTONE_UI_CONTROL_LONG_PROP = "minimal"; /** * Property to control the selection of UI control for a selectMany control. * If a selectMany control has >= the number of values specified in this property, * it is considered a <b>long</b> list, and the UI control specified by * SELECTMANY_UI_CONTROL_LONG_PROP is used. Otherwise, the value of SELECTMANY_UI_CONTROL_SHORT_PROP * is used. */ public static final String SELECTMANY_LONG_LIST_SIZE_PROP = PROPERTY_PREFIX + "select@longListSize"; /** * Property to specify the selectMany UI control to be used when there are releatively few items * to choose from. */ public static final String SELECTMANY_UI_CONTROL_SHORT_PROP = PROPERTY_PREFIX + "select@appearance/short"; /** * Property to specify the selectMany UI control to be used when there are large numbers of items * to choose from. */ public static final String SELECTMANY_UI_CONTROL_LONG_PROP = PROPERTY_PREFIX + "select@appearance/long"; private static final String DEFAULT_SELECTMANY_UI_CONTROL_SHORT_PROP = "full"; private static final String DEFAULT_SELECTMANY_UI_CONTROL_LONG_PROP = "compact"; private static final String DEFAULT_LONG_LIST_MAX_SIZE = "6"; /** * Property to control the box alignment of a group - corresponds to xforms:group@chiba:box-align. * There are four valid values for this property - right, left, top, and bottom. * The default value is <b>right</b>. */ public static final String GROUP_BOX_ALIGN_PROP = PROPERTY_PREFIX + "group@box-align"; private static final String DEFAULT_GROUP_BOX_ALIGN = "right"; /** * Property to control the box orientation of a group - corresponds to xforms:group@chiba:box-orient. * There are two valid values for this property - vertical and horizontal. * The default value is <b>vertical</b>. */ public static final String GROUP_BOX_ORIENT_PROP = PROPERTY_PREFIX + "group@box-orient"; private static final String DEFAULT_GROUP_BOX_ORIENT = "vertical"; /** * Property to control the width of a group - corresponds to xforms:group/@chiba:width. * This value may be expressed as a percentage value or as an absolute size. */ public static final String GROUP_WIDTH_PROP = PROPERTY_PREFIX + "group@width"; private static final String DEFAULT_GROUP_WIDTH = "60%"; /** * Property to control the caption width of a group - corresponds to xforms:group/@chiba:caption-width. * This value may be expressed as a percentage value or as an absolute size. */ public static final String GROUP_CAPTION_WIDTH_PROP = PROPERTY_PREFIX + "group@caption-width"; private static final String DEFAULT_GROUP_CAPTION_WIDTH = "30%"; /** * Property to control the border of a group - corresponds to xforms:group/@chiba:border. * A value of <b>0</b> indicates no border, a value of <b>1</b> indicates a border is provided. */ public static final String GROUP_BORDER_PROP = PROPERTY_PREFIX + "group@border"; private static final String DEFAULT_GROUP_BORDER = "0"; /** * Prossible values of the "@method" on the "submission" element */ public static final String SUBMIT_METHOD_POST = "post"; /** * __UNDOCUMENTED__ */ public static final String SUBMIT_METHOD_PUT = "put"; /** * __UNDOCUMENTED__ */ public static final String SUBMIT_METHOD_GET = "get"; /** * __UNDOCUMENTED__ */ public static final String SUBMIT_METHOD_FORM_DATA_POST = "form-data-post"; /** * __UNDOCUMENTED__ */ public static final String SUBMIT_METHOD_URLENCODED_POST = "urlencoded-post"; /** * possible instance modes */ public static final int INSTANCE_MODE_NONE = 0; /** * __UNDOCUMENTED__ */ public static final int INSTANCE_MODE_INCLUDED = 1; /** * __UNDOCUMENTED__ */ public static final int INSTANCE_MODE_HREF = 2; /** * __UNDOCUMENTED__ */ protected Source _instanceSource; /** * __UNDOCUMENTED__ */ protected String _action; /** * __UNDOCUMENTED__ */ protected String _instanceHref; /** * Properties choosed by the user */ protected String _rootTagName; /** * __UNDOCUMENTED__ */ protected String _stylesheet; /** * __UNDOCUMENTED__ */ protected String _submitMethod; /** * __UNDOCUMENTED__ */ protected String _base; /** * __UNDOCUMENTED__ */ protected WrapperElementsBuilder _wrapper; /** * __UNDOCUMENTED__ */ protected int _instanceMode = 0; /** * __UNDOCUMENTED__ */ protected boolean _useSchemaTypes = false; private DocumentBuilder documentBuilder; /** * generic counter -> replaced by an hashMap with: * keys: name of the elements * values: "Long" representing the counter for this element */ //private long refCounter; private HashMap counter; private Properties properties; protected XSModel schema; private String targetNamespace; private Map namespacePrefixes = new HashMap(); // typeTree // each entry is keyed by the type name // value is an ArrayList that contains the XSTypeDefinition's which // are compatible with the specific type. Compatible means that // can be used as a substituted type using xsi:type // In order for it to be compatible, it cannot be abstract, and // it must be derived by extension. // The ArrayList does not contain its own type + has the other types only once // private TreeMap typeTree; /** * configure LOGGER */ static { Layout layout = new SimpleLayout(); LOGGER.addAppender(new ConsoleAppender(layout)); } /** * Creates a new instance of AbstractSchemaFormBuilder */ public AbstractSchemaFormBuilder(String rootTagName) { this._rootTagName = rootTagName; typeTree = new TreeMap(); properties = new Properties(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { factory.setNamespaceAware(true); factory.setValidating(false); documentBuilder = factory.newDocumentBuilder(); } catch (ParserConfigurationException x) { x.printStackTrace(); } //default wrapper: HTML //_wrapper = new BaseWrapperElementsBuilder(); _wrapper = new XHTMLWrapperElementsBuilder(); reset(); } /** * Creates a new AbstractSchemaFormBuilder object. * * @param rootTagName __UNDOCUMENTED__ * @param instanceSource __UNDOCUMENTED__ * @param action __UNDOCUMENTED__ * @param submitMethod __UNDOCUMENTED__ * @param wrapper __UNDOCUMENTED__ * @param stylesheet __UNDOCUMENTED__ */ public AbstractSchemaFormBuilder(String rootTagName, Source instanceSource, String action, String submitMethod, WrapperElementsBuilder wrapper, String stylesheet, String base, boolean userSchemaTypes) { this(rootTagName); this._instanceSource = instanceSource; if (instanceSource != null) { this._instanceMode = AbstractSchemaFormBuilder.INSTANCE_MODE_INCLUDED; } this._action = action; this._stylesheet = stylesheet; this._base = base; this._useSchemaTypes = userSchemaTypes; //control if it is one of the SUBMIT_METHOD attributes? this._submitMethod = submitMethod; this._wrapper = wrapper; } /** * Creates a new AbstractSchemaFormBuilder object. * * @param rootTagName __UNDOCUMENTED__ * @param instanceHref __UNDOCUMENTED__ * @param action __UNDOCUMENTED__ * @param submitMethod __UNDOCUMENTED__ * @param wrapper __UNDOCUMENTED__ * @param stylesheet __UNDOCUMENTED__ */ public AbstractSchemaFormBuilder(String rootTagName, String instanceHref, String action, String submitMethod, WrapperElementsBuilder wrapper, String stylesheet, String base, boolean userSchemaTypes) { this(rootTagName); this._instanceHref = instanceHref; if ((instanceHref != null) && !instanceHref.equals("")) { this._instanceMode = AbstractSchemaFormBuilder.INSTANCE_MODE_HREF; } this._action = action; this._stylesheet = stylesheet; this._base = base; this._useSchemaTypes = userSchemaTypes; //control if it is one of the SUBMIT_METHOD attributes? this._submitMethod = submitMethod; this._wrapper = wrapper; } /** * __UNDOCUMENTED__ * * @return __UNDOCUMENTED__ */ public String getAction() { return _action; } /** * __UNDOCUMENTED__ * * @return __UNDOCUMENTED__ */ public String getInstanceHref() { return _instanceHref; } /** * __UNDOCUMENTED__ * * @return __UNDOCUMENTED__ */ public int getInstanceMode() { return _instanceMode; } /** * __UNDOCUMENTED__ * * @return __UNDOCUMENTED__ */ public Source getInstanceSource() { return _instanceSource; } /** * __UNDOCUMENTED__ * * @return __UNDOCUMENTED__ */ public Properties getProperties() { return properties; } /** * __UNDOCUMENTED__ * * @param key __UNDOCUMENTED__ * @param value __UNDOCUMENTED__ */ public void setProperty(String key, String value) { getProperties().setProperty(key, value); } /** * __UNDOCUMENTED__ * * @param key __UNDOCUMENTED__ * @return __UNDOCUMENTED__ */ public String getProperty(String key) { return getProperties().getProperty(key); } /** * __UNDOCUMENTED__ * * @param key __UNDOCUMENTED__ * @param defaultValue __UNDOCUMENTED__ * @return __UNDOCUMENTED__ */ public String getProperty(String key, String defaultValue) { return getProperties().getProperty(key, defaultValue); } /** * __UNDOCUMENTED__ * * @return __UNDOCUMENTED__ */ public String getRootTagName() { return _rootTagName; } /** * __UNDOCUMENTED__ * * @return __UNDOCUMENTED__ */ public String getStylesheet() { return _stylesheet; } /** * __UNDOCUMENTED__ * * @return __UNDOCUMENTED__ */ public String getSubmitMethod() { return _submitMethod; } private void loadSchema(String inputURI) throws java.lang.ClassNotFoundException, java.lang.InstantiationException, java.lang.IllegalAccessException { // Get DOM Implementation using DOM Registry System.setProperty(DOMImplementationRegistry.PROPERTY, "org.apache.xerces.dom.DOMXSImplementationSourceImpl"); DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); Object o = registry.getDOMImplementation("XS-Loader"); if (o instanceof XSImplementation) { XSImplementation impl = (XSImplementation) o; XSLoader schemaLoader = impl.createXSLoader(null); schema = schemaLoader.loadURI(inputURI); } else if (o != null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("DOMImplementation is not a XSImplementation: " + o.getClass().getName()); } System.exit(1); } } /** * builds a form from a XML schema. * * @param inputURI the URI of the Schema to be used * @return __UNDOCUMENTED__ * @throws FormBuilderException __UNDOCUMENTED__ */ public Document buildForm(String inputURI) throws FormBuilderException { try { this.loadSchema(inputURI); buildTypeTree(schema); //refCounter = 0; counter = new HashMap(); Document xForm = createFormTemplate(_rootTagName, _rootTagName + " Form", getProperty(CSS_STYLE_PROP, DEFAULT_CSS_STYLE_PROP)); //this.buildInheritenceTree(schema); Element envelopeElement = xForm.getDocumentElement(); //Element formSection = (Element) envelopeElement.getElementsByTagNameNS(CHIBA_NS, "form").item(0); //Element formSection =(Element) envelopeElement.getElementsByTagName("body").item(0); //find form element: last element created NodeList children = xForm.getDocumentElement().getChildNodes(); int nb = children.getLength(); Element formSection = (Element) children.item(nb - 1); Element modelSection = (Element) envelopeElement.getElementsByTagNameNS(XFORMS_NS, "model").item(0); //add XMLSchema if we use schema types if (_useSchemaTypes && modelSection != null) { modelSection.setAttributeNS(XFORMS_NS, this.getXFormsNSPrefix() + "schema", inputURI); } //change stylesheet String stylesheet = this.getStylesheet(); if ((stylesheet != null) && !stylesheet.equals("")) { envelopeElement.setAttributeNS(CHIBA_NS, this.getChibaNSPrefix() + "stylesheet", stylesheet); } // TODO: Commented out because comments aren't output properly by the Transformer. //String comment = "This XForm was automatically generated by " + this.getClass().getName() + " on " + (new Date()) + System.getProperty("line.separator") + " from the '" + rootElementName + "' element from the '" + schema.getSchemaTargetNS() + "' XML Schema."; //xForm.insertBefore(xForm.createComment(comment),envelopeElement); //xxx XSDNode node = findXSDNodeByName(rootElementTagName,schemaNode.getElementSet()); //check if target namespace //no way to do this with XS API ? load DOM document ? //TODO: find a better way to find the targetNamespace try { Document domDoc = DOMUtil.parseXmlFile(inputURI, true, false); if (domDoc != null) { Element root = domDoc.getDocumentElement(); targetNamespace = root.getAttribute("targetNamespace"); if (targetNamespace != null && targetNamespace.equals("")) targetNamespace = null; } } catch (Exception ex) { LOGGER.error("Schema not loaded as DOM document: " + ex.getMessage()); } //if target namespace & we use the schema types: add it to form ns declarations if (_useSchemaTypes && targetNamespace != null && !targetNamespace.equals("")) { envelopeElement.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:schema", targetNamespace); } //TODO: WARNING: in Xerces 2.6.1, parameters are switched !!! (name, namespace) //XSElementDeclaration rootElementDecl =schema.getElementDeclaration(targetNamespace, _rootTagName); XSElementDeclaration rootElementDecl = schema.getElementDeclaration(_rootTagName, targetNamespace); if (rootElementDecl == null) { //DEBUG rootElementDecl = schema.getElementDeclaration(_rootTagName, targetNamespace); if (rootElementDecl != null && LOGGER.isDebugEnabled()) LOGGER.debug("getElementDeclaration: inversed parameters OK !!!"); throw new FormBuilderException("Invalid root element tag name [" + _rootTagName + ", targetNamespace=" + targetNamespace + "]"); } Element instanceElement = (Element) modelSection .appendChild(xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "instance")); this.setXFormsId(instanceElement); Element rootElement; if (_instanceMode == AbstractSchemaFormBuilder.INSTANCE_MODE_NONE) { rootElement = (Element) instanceElement.appendChild( xForm.createElementNS(targetNamespace, getElementName(rootElementDecl, xForm))); String prefix = xmlSchemaInstancePrefix.substring(0, xmlSchemaInstancePrefix.length() - 1); rootElement.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:" + prefix, XMLSCHEMA_INSTANCE_NAMESPACE_URI); } else if (_instanceMode == AbstractSchemaFormBuilder.INSTANCE_MODE_INCLUDED) //get the instance element { boolean ok = true; try { /*DOMResult result = new DOMResult(); TransformerFactory trFactory = TransformerFactory.newInstance(); Transformer tr = trFactory.newTransformer(); tr.transform(_instanceSource, result); Document instanceDoc = (Document) result.getNode();*/ DocumentBuilderFactory docFact = DocumentBuilderFactory.newInstance(); docFact.setNamespaceAware(true); docFact.setValidating(false); DocumentBuilder parser = docFact.newDocumentBuilder(); Document instanceDoc = parser.parse(new InputSource(_instanceSource.getSystemId())); //possibility abandonned for the moment: //modify the instance to add the correct "xsi:type" attributes wherever needed //Document instanceDoc=this.setXMLSchemaAndPSVILoad(inputURI, _instanceSource, targetNamespace); if (instanceDoc != null) { Element instanceInOtherDoc = instanceDoc.getDocumentElement(); if (instanceInOtherDoc.getNodeName().equals(_rootTagName)) { rootElement = (Element) xForm.importNode(instanceInOtherDoc, true); instanceElement.appendChild(rootElement); //add XMLSchema instance NS String prefix = xmlSchemaInstancePrefix.substring(0, xmlSchemaInstancePrefix.length() - 1); if (!rootElement.hasAttributeNS(XMLNS_NAMESPACE_URI, prefix)) rootElement.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:" + prefix, XMLSCHEMA_INSTANCE_NAMESPACE_URI); //possibility abandonned for the moment: //modify the instance to add the correct "xsi:type" attributes wherever needed //this.addXSITypeAttributes(rootElement); } else { ok = false; } } else { ok = false; } } catch (Exception ex) { ex.printStackTrace(); //if there is an exception we put the empty root element ok = false; } //if there was a problem if (!ok) { rootElement = (Element) instanceElement.appendChild(xForm.createElement(_rootTagName)); } } else if (_instanceMode == AbstractSchemaFormBuilder.INSTANCE_MODE_HREF) //add the xlink:href attribute { instanceElement.setAttributeNS(SchemaFormBuilder.XLINK_NS, this.getXLinkNSPrefix() + "href", _instanceHref); } Element formContentWrapper = _wrapper.createGroupContentWrapper(formSection); addElement(xForm, modelSection, formContentWrapper, rootElementDecl, rootElementDecl.getTypeDefinition(), "/" + getElementName(rootElementDecl, xForm)); Element submitInfoElement = (Element) modelSection .appendChild(xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "submission")); //submitInfoElement.setAttributeNS(XFORMS_NS,getXFormsNSPrefix()+"id","save"); String submissionId = this.setXFormsId(submitInfoElement); //action if (_action == null) { submitInfoElement.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "action", ""); } else { submitInfoElement.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "action", _action); } //method if ((_submitMethod != null) && !_submitMethod.equals("")) { submitInfoElement.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "method", _submitMethod); } else { //default is "post" submitInfoElement.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "method", AbstractSchemaFormBuilder.SUBMIT_METHOD_POST); } //Element submitButton = (Element) formSection.appendChild(xForm.createElementNS(XFORMS_NS,getXFormsNSPrefix()+"submit")); Element submitButton = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "submit"); Element submitControlWrapper = _wrapper.createControlsWrapper(submitButton); formContentWrapper.appendChild(submitControlWrapper); submitButton.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "submission", submissionId); this.setXFormsId(submitButton); Element submitButtonCaption = (Element) submitButton .appendChild(xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "label")); submitButtonCaption.appendChild(xForm.createTextNode("Submit")); this.setXFormsId(submitButtonCaption); return xForm; } catch (ParserConfigurationException x) { throw new FormBuilderException(x); } catch (java.lang.ClassNotFoundException x) { throw new FormBuilderException(x); } catch (java.lang.InstantiationException x) { throw new FormBuilderException(x); } catch (java.lang.IllegalAccessException x) { throw new FormBuilderException(x); } } /** * method to load the instance document using PSVI, by first setting the location of the schema, * resaving it in XML and reloading it with Xerces XMLSchema's API * ABANDONNED -> NOT TESTED **/ /*private Document setXMLSchemaAndPSVILoad(String inputURI, Source instanceSource, String targetNamespace){ Document newDoc=null; try{ //load doc DocumentBuilderFactory domfact=DocumentBuilderFactory.newInstance(); domfact.setNamespaceAware(true); DocumentBuilder builder=domfact.newDocumentBuilder(); Document doc =builder.parse(new InputSource(instanceSource.getSystemId())); Element rootElement=doc.getDocumentElement(); //set namespace & schema location String prefix=xmlSchemaInstancePrefix.substring(0, xmlSchemaInstancePrefix.length()-1); String attr_ns=rootElement.getAttributeNS(XMLNS_NAMESPACE_URI, prefix); if(attr_ns==null || attr_ns.equals("")){ rootElement.setAttributeNS(XMLNS_NAMESPACE_URI,"xmlns:"+prefix,XMLSCHEMA_INSTANCE_NAMESPACE_URI); } String attr_val1=rootElement.getAttributeNS(XMLSCHEMA_INSTANCE_NAMESPACE_URI, "noNamespaceSchemaLocation"); String attr_val2=rootElement.getAttributeNS(XMLSCHEMA_INSTANCE_NAMESPACE_URI, "schemaLocation"); if( (attr_val1==null || attr_val1.equals("") ) && (attr_val2==null || attr_val2.equals("") ) ){ if(targetNamespace==null || targetNamespace.equals("")) rootElement.setAttributeNS(XMLSCHEMA_INSTANCE_NAMESPACE_URI, xmlSchemaInstancePrefix+"noNamespaceSchemaLocation", inputURI); else rootElement.setAttributeNS(XMLSCHEMA_INSTANCE_NAMESPACE_URI, xmlSchemaInstancePrefix+"schemaLocation", inputURI); } //save the file DOMSource source=new DOMSource(doc); File f=File.createTempFile("instance", ".xml"); StreamResult result2=new StreamResult(f); TransformerFactory trFactory =TransformerFactory.newInstance(); Transformer tr = trFactory.newTransformer(); tr.transform(source, result2); //reload it with PSVI newDoc=this.PSVIParse(f); } catch(javax.xml.transform.TransformerConfigurationException ex){ LOGGER.error("TransformerConfigurationException in setXMLSchemaAndPSVILoad", ex); } catch(javax.xml.transform.TransformerException ex){ LOGGER.error("TransformerException in setXMLSchemaAndPSVILoad", ex); } catch(java.io.IOException ex){ LOGGER.error("IOException in setXMLSchemaAndPSVILoad", ex); } catch(javax.xml.parsers.ParserConfigurationException ex){ LOGGER.error("ParserConfigurationException in setXMLSchemaAndPSVILoad", ex); } catch(org.xml.sax.SAXException ex){ LOGGER.error("SAXException in setXMLSchemaAndPSVILoad", ex); } return newDoc; }*/ /* *Method to parse a file with PSVI * ABANDONNED -> NOT TESTED */ /* public Document PSVIParse(File f){ Document doc=null; try{ DOMParser parser=new DOMParser(); parser.setFeature("http://xml.org/sax/features/validation", true); parser.setFeature("http://apache.org/xml/features/validation/schema", true); parser.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true); parser.setFeature("http://apache.org/xml/features/continue-after-fatal-error", true); parser.setProperty("http://apache.org/xml/properties/dom/document-class-name", "org.apache.xerces.dom.PSVIDocumentImpl"); FileInputStream fin=new FileInputStream(f); InputSource source=new InputSource(fin); parser.parse(source); doc=parser.getDocument(); }catch(org.xml.sax.SAXNotRecognizedException ex){ LOGGER.error("SAXNotRecognizedException for parameter in PSVIParse", ex); } catch(java.io.FileNotFoundException ex){ LOGGER.error("FileNotFoundException for "+f.getAbsolutePath()+" in PSVIParse", ex); } catch(org.xml.sax.SAXException ex){ LOGGER.error("SAXException while loading "+f.getAbsolutePath()+" in PSVIParse", ex); } catch(java.io.IOException ex){ LOGGER.error("IOException while loading "+f.getAbsolutePath()+" in PSVIParse", ex); } return doc; } */ /** * add a "@xsi:type" attribute for all elements of type that are extended * with default value = the type if not abstract, otherwise the first non abstract subtype * ABANDONNED -> NOT TESTED **/ /*private void addXSITypeAttributes(Element element){ if(element!=null){ String ns=element.getNamespaceURI(); //set it on this element if it's not already here String attr=element.getAttributeNS(XMLSCHEMA_INSTANCE_NAMESPACE_URI, "type"); if(attr==null || attr.equals("")){ ElementPSVI el=(ElementPSVI) element; if(el!=null){ if(LOGGER.isDebugEnabled()) LOGGER.debug("PSVIElement found for "+element.getNodeName()+". Testing type"); XSElementDeclaration elDecl=el.getElementDeclaration(); XSTypeDefinition type=elDecl.getTypeDefinition(); if(type.getTypeCategory()==XSTypeDefinition.COMPLEX_TYPE){ XSComplexTypeDefinition complexType=(XSComplexTypeDefinition) type; TreeSet compatibleTypes =(TreeSet) typeTree.get(complexType.getName()); if(compatibleTypes!=null && !compatibleTypes.isEmpty()){ if(LOGGER.isDebugEnabled()) LOGGER.debug("compatible type found for "+element.getNodeName()+" -> setting xsi:type attribute"); //add xsi:type attribute //find value of attribute String value=null; if(!complexType.getAbstract()) value=complexType.getName(); else{ //find the first non abstract subtype Iterator it=compatibleTypes.iterator(); while(it.hasNext() && value==null){ String subTypeName=(String) it.next(); //find type definition XSTypeDefinition subType=schema.getTypeDefinition(subTypeName, ns); if(subType!=null && subType.getTypeCategory()==XSTypeDefinition.COMPLEX_TYPE){ XSComplexTypeDefinition subComplexType=(XSComplexTypeDefinition) subType; if(!subComplexType.getAbstract()) value=subComplexType.getName(); } } } //set attribute element.setAttributeNS(XMLSCHEMA_INSTANCE_NAMESPACE_URI, this.xmlSchemaInstancePrefix+"type", value); } } } } //recursive call NodeList children=element.getChildNodes(); int nb=children.getLength(); for(int i=0;i<nb;i++){ Node child=children.item(i); if(child.getNodeType()==Node.ELEMENT_NODE){ this.addXSITypeAttributes((Element) child); } } } }*/ /** * __UNDOCUMENTED__ * * @param inputURI the URI of the XML Schema to be used * @return __UNDOCUMENTED__ * @throws FormBuilderException __UNDOCUMENTED__ */ public String buildFormAsString(String inputURI) throws FormBuilderException { Document xForm = buildForm(inputURI); if (xForm != null) { StringWriter stringWriter = new StringWriter(); try { TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(); StreamResult result = new StreamResult(stringWriter); transformer.transform(new DOMSource(xForm), result); return stringWriter.getBuffer().toString(); } catch (TransformerConfigurationException x) { throw new FormBuilderException(x); } catch (TransformerException x) { throw new FormBuilderException(x); } } return ""; } /** * This method is invoked after the form builder is finished creating and processing * a form control. Implementations may choose to use this method to add/inspect/modify * the controlElement prior to the builder moving onto the next control. * * @param controlElement The form control element that was created. * @param controlType The XML Schema type for which <b>controlElement</b> was created. */ public void endFormControl(Element controlElement, XSTypeDefinition controlType, int minOccurs, int maxOccurs) { } /** * __UNDOCUMENTED__ */ public void reset() { //refCounter = 0; counter = new HashMap(); setProperty(CSS_STYLE_PROP, DEFAULT_CSS_STYLE_PROP); setProperty(SELECTMANY_LONG_LIST_SIZE_PROP, DEFAULT_LONG_LIST_MAX_SIZE); setProperty(SELECTMANY_UI_CONTROL_SHORT_PROP, DEFAULT_SELECTMANY_UI_CONTROL_SHORT_PROP); setProperty(SELECTMANY_UI_CONTROL_LONG_PROP, DEFAULT_SELECTMANY_UI_CONTROL_LONG_PROP); setProperty(SELECTONE_LONG_LIST_SIZE_PROP, DEFAULT_LONG_LIST_MAX_SIZE); setProperty(SELECTONE_UI_CONTROL_SHORT_PROP, DEFAULT_SELECTONE_UI_CONTROL_SHORT_PROP); setProperty(SELECTONE_UI_CONTROL_LONG_PROP, DEFAULT_SELECTONE_UI_CONTROL_LONG_PROP); setProperty(GROUP_BOX_ALIGN_PROP, DEFAULT_GROUP_BOX_ALIGN); setProperty(GROUP_BOX_ORIENT_PROP, DEFAULT_GROUP_BOX_ORIENT); setProperty(GROUP_CAPTION_WIDTH_PROP, DEFAULT_GROUP_CAPTION_WIDTH); setProperty(GROUP_WIDTH_PROP, DEFAULT_GROUP_WIDTH); setProperty(GROUP_BORDER_PROP, DEFAULT_GROUP_BORDER); } /** * Returns the most-specific built-in base type for the provided type. */ protected short getBuiltInType(XSTypeDefinition type) { if (type.getName().equals("anyType")) { return XSConstants.ANYSIMPLETYPE_DT; } else { XSSimpleTypeDefinition simpleType = (XSSimpleTypeDefinition) type; //get built-in type //only working method found: getBuiltInKind, but it returns a short ! //XSTypeDefinition builtIn = simpleType.getPrimitiveType(); /*XSTypeDefinition builtIn = type.getBaseType(); if (builtIn == null) { // always null for a ListType if (simpleType.getItemType() != null) //if not null it's a list return getBuiltInType(simpleType.getItemType()); else return simpleType; } else if(LOGGER.isDebugEnabled()) LOGGER.debug(" -> builtinType="+builtIn.getName()); return builtIn;*/ short result = simpleType.getBuiltInKind(); if (result == XSConstants.LIST_DT) { result = getBuiltInType(simpleType.getItemType()); } return result; } } /** * get the name of a datatype defined by its value in XSConstants * TODO: find an automatic way to do this ! * * @param dt the short representating this datatype from XSConstants * @return the name of the datatype */ public String getDataTypeName(short dt) { String name = ""; switch (dt) { case XSConstants.ANYSIMPLETYPE_DT: name = "anyType"; break; case XSConstants.ANYURI_DT: name = "anyURI"; break; case XSConstants.BASE64BINARY_DT: name = "base64Binary"; break; case XSConstants.BOOLEAN_DT: name = "boolean"; break; case XSConstants.BYTE_DT: name = "byte"; break; case XSConstants.DATE_DT: name = "date"; break; case XSConstants.DATETIME_DT: name = "dateTime"; break; case XSConstants.DECIMAL_DT: name = "decimal"; break; case XSConstants.DOUBLE_DT: name = "double"; break; case XSConstants.DURATION_DT: name = "duration"; break; case XSConstants.ENTITY_DT: name = "ENTITY"; break; case XSConstants.FLOAT_DT: name = "float"; break; case XSConstants.GDAY_DT: name = "gDay"; break; case XSConstants.GMONTH_DT: name = "gMonth"; break; case XSConstants.GMONTHDAY_DT: name = "gMonthDay"; break; case XSConstants.GYEAR_DT: name = "gYear"; break; case XSConstants.GYEARMONTH_DT: name = "gYearMonth"; break; case XSConstants.ID_DT: name = "ID"; break; case XSConstants.IDREF_DT: name = "IDREF"; break; case XSConstants.INT_DT: name = "int"; break; case XSConstants.INTEGER_DT: name = "integer"; break; case XSConstants.LANGUAGE_DT: name = "language"; break; case XSConstants.LONG_DT: name = "long"; break; case XSConstants.NAME_DT: name = "Name"; break; case XSConstants.NCNAME_DT: name = "NCName"; break; case XSConstants.NEGATIVEINTEGER_DT: name = "negativeInteger"; break; case XSConstants.NMTOKEN_DT: name = "NMTOKEN"; break; case XSConstants.NONNEGATIVEINTEGER_DT: name = "nonNegativeInteger"; break; case XSConstants.NONPOSITIVEINTEGER_DT: name = "nonPositiveInteger"; break; case XSConstants.NORMALIZEDSTRING_DT: name = "normalizedString"; break; case XSConstants.NOTATION_DT: name = "NOTATION"; break; case XSConstants.POSITIVEINTEGER_DT: name = "positiveInteger"; break; case XSConstants.QNAME_DT: name = "QName"; break; case XSConstants.SHORT_DT: name = "short"; break; case XSConstants.STRING_DT: name = "string"; break; case XSConstants.TIME_DT: name = "time"; break; case XSConstants.TOKEN_DT: name = "TOKEN"; break; case XSConstants.UNSIGNEDBYTE_DT: name = "unsignedByte"; break; case XSConstants.UNSIGNEDINT_DT: name = "unsignedInt"; break; case XSConstants.UNSIGNEDLONG_DT: name = "unsignedLong"; break; case XSConstants.UNSIGNEDSHORT_DT: name = "unsignedShort"; break; } return name; } /** * Returns the prefix for the Chiba namespace. */ protected String getChibaNSPrefix() { return chibaNSPrefix; } /* Increments the xforms:ref attribute counter. * */ /*protected long incRefCounter() { return refCounter++; }*/ protected String setXFormsId(Element el) { //remove the eventuel "id" attribute if (el.hasAttributeNS(SchemaFormBuilder.XFORMS_NS, "id")) { el.removeAttributeNS(SchemaFormBuilder.XFORMS_NS, "id"); } //long count=this.incIdCounter(); long count = 0; String name = el.getLocalName(); Long l = (Long) counter.get(name); if (l != null) { count = l.longValue(); } String id = name + "_" + count; //increment the counter counter.put(name, new Long(count + 1)); el.setAttributeNS(SchemaFormBuilder.XFORMS_NS, this.getXFormsNSPrefix() + "id", id); return id; } /** * method to set an Id to this element and to all XForms descendants of this element */ private void resetXFormIds(Element newControl) { if (newControl.getNamespaceURI() != null && newControl.getNamespaceURI().equals(XFORMS_NS)) this.setXFormsId(newControl); //recursive call NodeList children = newControl.getChildNodes(); int nb = children.getLength(); for (int i = 0; i < nb; i++) { Node child = children.item(i); if (child.getNodeType() == Node.ELEMENT_NODE) this.resetXFormIds((Element) child); } } /** * Returns the prefix for the XForms namespace. */ protected String getXFormsNSPrefix() { return xformsNSPrefix; } /** * Returns the prefix for the XLink namespace. */ protected String getXLinkNSPrefix() { return xlinkNSPrefix; } /** * __UNDOCUMENTED__ * * @param xForm __UNDOCUMENTED__ * @param choicesElement __UNDOCUMENTED__ * @param choiceValues __UNDOCUMENTED__ */ protected void addChoicesForSelectControl(Document xForm, Element choicesElement, Vector choiceValues) { // sort the enums values and then add them as choices // // TODO: Should really put the default value (if any) at the top of the list. // List sortedList = choiceValues.subList(0, choiceValues.size()); Collections.sort(sortedList); Iterator iterator = sortedList.iterator(); while (iterator.hasNext()) { String textValue = (String) iterator.next(); Element item = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "item"); this.setXFormsId(item); choicesElement.appendChild(item); Element captionElement = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "label"); this.setXFormsId(captionElement); item.appendChild(captionElement); captionElement.appendChild(xForm.createTextNode(createCaption(textValue))); Element value = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "value"); this.setXFormsId(value); item.appendChild(value); value.appendChild(xForm.createTextNode(textValue)); } } //protected void addChoicesForSelectSwitchControl(Document xForm, Element choicesElement, Vector choiceValues, String bindIdPrefix) { protected void addChoicesForSelectSwitchControl(Document xForm, Element choicesElement, Vector choiceValues, HashMap case_types) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("addChoicesForSelectSwitchControl, values="); Iterator it = choiceValues.iterator(); while (it.hasNext()) { //String name=(String) it.next(); XSTypeDefinition type = (XSTypeDefinition) it.next(); String name = type.getName(); LOGGER.debug(" - " + name); } } // sort the enums values and then add them as choices // // TODO: Should really put the default value (if any) at the top of the list. // /*List sortedList = choiceValues.subList(0, choiceValues.size()); Collections.sort(sortedList); Iterator iterator = sortedList.iterator();*/ // -> no, already sorted Iterator iterator = choiceValues.iterator(); while (iterator.hasNext()) { XSTypeDefinition type = (XSTypeDefinition) iterator.next(); String textValue = type.getName(); //String textValue = (String) iterator.next(); if (LOGGER.isDebugEnabled()) LOGGER.debug("addChoicesForSelectSwitchControl, processing " + textValue); Element item = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "item"); this.setXFormsId(item); choicesElement.appendChild(item); Element captionElement = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "label"); this.setXFormsId(captionElement); item.appendChild(captionElement); captionElement.appendChild(xForm.createTextNode(createCaption(textValue))); Element value = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "value"); this.setXFormsId(value); item.appendChild(value); value.appendChild(xForm.createTextNode(textValue)); /// action in the case Element action = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "action"); this.setXFormsId(action); item.appendChild(action); action.setAttributeNS(XMLEVENTS_NS, xmleventsNSPrefix + "event", "xforms-select"); Element toggle = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "toggle"); this.setXFormsId(toggle); //build the case element Element caseElement = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "case"); String case_id = this.setXFormsId(caseElement); case_types.put(textValue, caseElement); toggle.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "case", case_id); //toggle.setAttributeNS(XFORMS_NS,getXFormsNSPrefix() + "case",bindIdPrefix + "_" + textValue +"_case"); action.appendChild(toggle); } } /** * __UNDOCUMENTED__ * * @param xForm __UNDOCUMENTED__ * @param annotation __UNDOCUMENTED__ * @return __UNDOCUMENTED__ */ protected Element addHintFromDocumentation(Document xForm, XSAnnotation annotation) { if (annotation != null) { Element hintElement = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "hint"); this.setXFormsId(hintElement); Text hintText = (Text) hintElement.appendChild(xForm.createTextNode("")); //write annotation to empty doc Document doc = DOMUtil.newDocument(true, false); annotation.writeAnnotation(doc, XSAnnotation.W3C_DOM_DOCUMENT); //get "annotation" element NodeList annots = doc.getElementsByTagNameNS("http://www.w3.org/2001/XMLSchema", "annotation"); if (annots.getLength() > 0) { Element annotEl = (Element) annots.item(0); //documentation NodeList docos = annotEl.getElementsByTagNameNS("http://www.w3.org/2001/XMLSchema", "documentation"); int nbDocos = docos.getLength(); for (int j = 0; j < nbDocos; j++) { Element doco = (Element) docos.item(j); //get text value String text = DOMUtil.getTextNodeAsString(doco); hintText.appendData(text); if (j < nbDocos - 1) { hintText.appendData(" "); } } return hintElement; } return null; } return null; } private XSModel getSchema() { return schema; } public XSParticle findCorrespondingParticleInComplexType(XSElementDeclaration elDecl) { XSParticle thisParticle = null; XSComplexTypeDefinition complexType = elDecl.getEnclosingCTDefinition(); if (complexType != null) { XSParticle particle = complexType.getParticle(); XSTerm term = particle.getTerm(); XSObjectList particles; if (term instanceof XSModelGroup) { XSModelGroup group = (XSModelGroup) term; particles = group.getParticles(); if (particles != null) { int nb = particles.getLength(); int i = 0; while (i < nb && thisParticle == null) { XSParticle part = (XSParticle) particles.item(i); //test term XSTerm thisTerm = part.getTerm(); if (thisTerm == elDecl) thisParticle = part; i++; } } } } return thisParticle; } /** * finds the minOccurs and maxOccurs of an element declaration * * @return a table containing minOccurs and MaxOccurs */ public int[] getOccurance(XSElementDeclaration elDecl) { int minOccurs = 1; int maxOccurs = 1; //get occurance on encosing element declaration XSParticle particle = this.findCorrespondingParticleInComplexType(elDecl); if (particle != null) { minOccurs = particle.getMinOccurs(); if (particle.getMaxOccursUnbounded()) maxOccurs = -1; else maxOccurs = particle.getMaxOccurs(); } //if not set, get occurance of model group content //no -> this is made in "addGroup" directly ! /*if (minOccurs == 1 && maxOccurs == 1) { XSTypeDefinition type = elDecl.getTypeDefinition(); if (type != null && type.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { XSComplexTypeDefinition complexType = (XSComplexTypeDefinition) type; XSParticle thisParticle = complexType.getParticle(); if (thisParticle != null) { minOccurs = thisParticle.getMinOccurs(); if (thisParticle.getMaxOccursUnbounded()) maxOccurs = -1; else maxOccurs = thisParticle.getMaxOccurs(); } } }*/ if (LOGGER.isDebugEnabled()) LOGGER.debug("getOccurance for " + elDecl.getName() + ", minOccurs=" + minOccurs + ", maxOccurs=" + maxOccurs); int[] result = new int[2]; result[0] = minOccurs; result[1] = maxOccurs; return result; } private void addAnyType(Document xForm, Element modelSection, Element formSection, XSTypeDefinition controlType, XSElementDeclaration owner, String pathToRoot) { int[] occurance = this.getOccurance(owner); addSimpleType(xForm, modelSection, formSection, controlType, owner.getName(), owner, pathToRoot, occurance[0], occurance[1]); } private void addAttributeSet(Document xForm, Element modelSection, Element formSection, XSComplexTypeDefinition controlType, XSElementDeclaration owner, String pathToRoot, boolean checkIfExtension) { XSObjectList attrUses = controlType.getAttributeUses(); if (attrUses != null) { int nbAttr = attrUses.getLength(); for (int i = 0; i < nbAttr; i++) { XSAttributeUse currentAttributeUse = (XSAttributeUse) attrUses.item(i); XSAttributeDeclaration currentAttribute = currentAttributeUse.getAttrDeclaration(); //test if extended ! if (checkIfExtension && this.doesAttributeComeFromExtension(currentAttributeUse, controlType)) { if (LOGGER.isDebugEnabled()) { LOGGER.debug( "This attribute comes from an extension: recopy form controls. \n Model section: "); DOMUtil.prettyPrintDOM(modelSection); } String attributeName = currentAttributeUse.getName(); if (attributeName == null || attributeName.equals("")) attributeName = currentAttributeUse.getAttrDeclaration().getName(); //find the existing bind Id //(modelSection is the enclosing bind of the element) NodeList binds = modelSection.getElementsByTagNameNS(XFORMS_NS, "bind"); int j = 0; int nb = binds.getLength(); String bindId = null; while (j < nb && bindId == null) { Element bind = (Element) binds.item(j); String nodeset = bind.getAttributeNS(XFORMS_NS, "nodeset"); if (nodeset != null) { String name = nodeset.substring(1); //remove "@" in nodeset if (name.equals(attributeName)) bindId = bind.getAttributeNS(XFORMS_NS, "id"); } j++; } //find the control Element control = null; if (bindId != null) { if (LOGGER.isDebugEnabled()) LOGGER.debug("bindId found: " + bindId); JXPathContext context = JXPathContext.newContext(formSection.getOwnerDocument()); Pointer pointer = context .getPointer("//*[@" + this.getXFormsNSPrefix() + "bind='" + bindId + "']"); if (pointer != null) control = (Element) pointer.getNode(); } //copy it if (control == null) { LOGGER.warn("Corresponding control not found"); } else { Element newControl = (Element) control.cloneNode(true); //set new Ids to XForm elements this.resetXFormIds(newControl); formSection.appendChild(newControl); } } else { String newPathToRoot; if ((pathToRoot == null) || pathToRoot.equals("")) { newPathToRoot = "@" + currentAttribute.getName(); } else if (pathToRoot.endsWith("/")) { newPathToRoot = pathToRoot + "@" + currentAttribute.getName(); } else { newPathToRoot = pathToRoot + "/@" + currentAttribute.getName(); } XSSimpleTypeDefinition simpleType = currentAttribute.getTypeDefinition(); //TODO SRA: UrType ? /*if(simpleType==null){ simpleType=new UrType(); }*/ addSimpleType(xForm, modelSection, formSection, simpleType, currentAttributeUse, newPathToRoot); } } } } private void addComplexType(Document xForm, Element modelSection, Element formSection, XSComplexTypeDefinition controlType, XSElementDeclaration owner, String pathToRoot, boolean relative, boolean checkIfExtension) { if (controlType != null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("addComplexType for " + controlType.getName()); if (owner != null) LOGGER.debug(" owner=" + owner.getName()); } // add a group node and recurse // Element groupElement = createGroup(xForm, modelSection, formSection, owner); Element groupWrapper = groupElement; if (groupElement != modelSection) { groupWrapper = _wrapper.createGroupContentWrapper(groupElement); } int occurance[] = this.getOccurance(owner); int minOccurs = occurance[0]; int maxOccurs = occurance[1]; Element repeatSection = addRepeatIfNecessary(xForm, modelSection, groupWrapper, controlType, minOccurs, maxOccurs, pathToRoot); Element repeatContentWrapper = repeatSection; /*if(repeatSection!=groupWrapper) //we have a repeat { repeatContentWrapper=_wrapper.createGroupContentWrapper(repeatSection); addComplexTypeChildren(xForm,modelSection,repeatContentWrapper,controlType,owner,pathToRoot, true); } else addComplexTypeChildren(xForm,modelSection,repeatContentWrapper,controlType,owner,pathToRoot, false); */ if (repeatSection != groupWrapper) { //we have a repeat repeatContentWrapper = _wrapper.createGroupContentWrapper(repeatSection); relative = true; } addComplexTypeChildren(xForm, modelSection, repeatContentWrapper, controlType, owner, pathToRoot, relative, checkIfExtension); Element realModel = modelSection; if (relative) { //modelSection: find the last element put in the modelSection = bind realModel = DOMUtil.getLastChildElement(modelSection); } endFormGroup(groupElement, controlType, minOccurs, maxOccurs, realModel); } else if (LOGGER.isDebugEnabled()) LOGGER.debug("addComplexType: control type is null for pathToRoot=" + pathToRoot); } private void addComplexTypeChildren(Document xForm, Element modelSection, Element formSection, XSComplexTypeDefinition controlType, XSElementDeclaration owner, String pathToRoot, boolean relative, boolean checkIfExtension) { if (controlType != null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("addComplexTypeChildren for " + controlType.getName()); if (owner != null) LOGGER.debug(" owner=" + owner.getName()); } //no need to add base type when extension-> with Xerces XMLSchema API, //the elements of the base type are directly in the ModelGroup /*XSTypeDefinition base = controlType.getBaseType(); if (base != null && base != controlType) { if (LOGGER.isDebugEnabled()) LOGGER.debug(" base type=" + base.getName()); if (base.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { if (base.getName() == null || !base.getName().equals("anyType")) { if (LOGGER.isDebugEnabled()) LOGGER.debug( " recursive call for base type " + base.getName()); addComplexTypeChildren( xForm, modelSection, formSection, (XSComplexTypeDefinition) base, owner, pathToRoot, relative); } } else { addSimpleType( xForm, modelSection, formSection, (XSSimpleTypeDefinition) base, owner, pathToRoot); } }*/ /*if(!checkIfExtension){ //add it anyway XSTypeDefinition base = controlType.getBaseType(); if (base != null && base != controlType) { if (LOGGER.isDebugEnabled()) LOGGER.debug(" base type=" + base.getName()); if (base.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { if (base.getName() == null || !base.getName().equals("anyType")) { if (LOGGER.isDebugEnabled()) LOGGER.debug( " recursive call for base type " + base.getName()); addComplexTypeChildren( xForm, modelSection, formSection, (XSComplexTypeDefinition) base, owner, pathToRoot, relative, false); } } else { addSimpleType( xForm, modelSection, formSection, (XSSimpleTypeDefinition) base, owner, pathToRoot); } } } //we need the call for base type when type is a mixed content //WARNING: simple type given for simple type + attribute (should be mixed ?) else */ if (controlType.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_MIXED || (controlType.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE && controlType.getAttributeUses() != null && controlType.getAttributeUses().getLength() > 0)) { XSTypeDefinition base = controlType.getBaseType(); if (LOGGER.isDebugEnabled()) LOGGER.debug(" Control type is mixed . base type=" + base.getName()); if (base != null && base != controlType) { if (base.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) { addSimpleType(xForm, modelSection, formSection, (XSSimpleTypeDefinition) base, owner, pathToRoot); } else LOGGER.warn("addComplexTypeChildren for mixed type with basic type complex !"); } } else if (LOGGER.isDebugEnabled()) LOGGER.debug(" Content type = " + controlType.getContentType()); // check for compatible subtypes // of controlType. // add a type switch if there are any // compatible sub-types (i.e. anything // that derives from controlType) // add child elements if (relative) { pathToRoot = ""; //modelSection: find the last element put in the modelSection = bind modelSection = DOMUtil.getLastChildElement(modelSection); } //attributes addAttributeSet(xForm, modelSection, formSection, controlType, owner, pathToRoot, checkIfExtension); //process group XSParticle particle = controlType.getParticle(); if (particle != null) { XSTerm term = particle.getTerm(); if (term instanceof XSModelGroup) { if (LOGGER.isDebugEnabled()) LOGGER.debug(" Particle of " + controlType.getName() + " is a group --->"); XSModelGroup group = (XSModelGroup) term; //get maxOccurs int maxOccurs = particle.getMaxOccurs(); if (particle.getMaxOccursUnbounded()) { maxOccurs = -1; } int minOccurs = particle.getMinOccurs(); //call addGroup on this group this.addGroup(xForm, modelSection, formSection, group, controlType, owner, pathToRoot, minOccurs, maxOccurs, checkIfExtension); } else if (LOGGER.isDebugEnabled()) LOGGER.debug(" Particle of " + controlType.getName() + " is not a group: " + term.getClass().getName()); } if (LOGGER.isDebugEnabled()) LOGGER.debug("--->end of addComplexTypeChildren for " + controlType.getName()); } } /** * add an element to the XForms document: the bind + the control * (only the control if "withBind" is false) */ private void addElement(Document xForm, Element modelSection, Element formSection, XSElementDeclaration elementDecl, XSTypeDefinition controlType, String pathToRoot) { if (controlType == null) { // TODO!!! Figure out why this happens... for now just warn... // seems to happen when there is an element of type IDREFS LOGGER.warn("WARNING!!! controlType is null for " + elementDecl + ", " + elementDecl.getName()); return; } switch (controlType.getTypeCategory()) { case XSTypeDefinition.SIMPLE_TYPE: { addSimpleType(xForm, modelSection, formSection, (XSSimpleTypeDefinition) controlType, elementDecl, pathToRoot); break; } case XSTypeDefinition.COMPLEX_TYPE: { if (controlType.getName() != null && controlType.getName().equals("anyType")) { addAnyType(xForm, modelSection, formSection, (XSComplexTypeDefinition) controlType, elementDecl, pathToRoot); break; } else { // find the types which are compatible(derived from) the parent type. // // This is used if we encounter a XML Schema that permits the xsi:type // attribute to specify subtypes for the element. // // For example, the <address> element may be typed to permit any of // the following scenarios: // <address xsi:type="USAddress"> // </address> // <address xsi:type="CanadianAddress"> // </address> // <address xsi:type="InternationalAddress"> // </address> // // What we want to do is generate an XForm' switch element with cases // representing any valid non-abstract subtype. // // <xforms:select1 xforms:bind="xsi_type_13" // <xforms:label>Address</xforms:label> // <xforms:choices> // <xforms:item> // <xforms:label>US Address Type</xforms:label> // <xforms:value>USAddressType</xforms:value> // <xforms:action ev:event="xforms-select"> // <xforms:toggle xforms:case="USAddressType-case"/> // </xforms:action> // </xforms:item> // <xforms:item> // <xforms:label>Canadian Address Type</xforms:label> // <xforms:value>CanadianAddressType</xforms:value> // <xforms:action ev:event="xforms-select"> // <xforms:toggle xforms:case="CanadianAddressType-case"/> // </xforms:action> // </xforms:item> // <xforms:item> // <xforms:label>International Address Type</xforms:label> // <xforms:value>InternationalAddressType</xforms:value> // <xforms:action ev:event="xforms-select"> // <xforms:toggle xforms:case="InternationalAddressType-case"/> // </xforms:action> // </xforms:item> // // </xforms:choices> // <xforms:select1> // <xforms:trigger> // <xforms:label>validate Address type</xforms:label> // <xforms:action> // <xforms:dispatch id="dispatcher" xforms:name="xforms-activate" xforms:target="select1_0"/> // </xforms:action> //</xforms:trigger> // // <xforms:switch id="address_xsi_type_switch"> // <xforms:case id="USAddressType-case" selected="false"> // <!-- US Address Type sub-elements here--> // </xforms:case> // <xforms:case id="CanadianAddressType-case" selected="false"> // <!-- US Address Type sub-elements here--> // </xforms:case> // ... // </xforms:switch> // // + change bindings to add: // - a bind for the "@xsi:type" attribute // - for each possible element that can be added through the use of an inheritance, add a "relevant" attribute: // ex: xforms:relevant="../@xsi:type='USAddress'" // look for compatible types // String typeName = controlType.getName(); boolean relative = true; if (typeName != null) { TreeSet compatibleTypes = (TreeSet) typeTree.get(controlType.getName()); //TreeSet compatibleTypes = (TreeSet) typeTree.get(controlType); if (compatibleTypes != null) { relative = false; if (LOGGER.isDebugEnabled()) { LOGGER.debug("compatible types for " + typeName + ":"); Iterator it1 = compatibleTypes.iterator(); while (it1.hasNext()) { //String name = (String) it1.next(); XSTypeDefinition compType = (XSTypeDefinition) it1.next(); LOGGER.debug(" compatible type name=" + compType.getName()); } } Element control = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "select1"); String select1_id = this.setXFormsId(control); Element choices = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "choices"); this.setXFormsId(choices); //get possible values Vector enumValues = new Vector(); //add the type (if not abstract) if (!((XSComplexTypeDefinition) controlType).getAbstract()) enumValues.add(controlType); //enumValues.add(typeName); //add compatible types Iterator it = compatibleTypes.iterator(); while (it.hasNext()) { enumValues.add(it.next()); } if (enumValues.size() > 1) { String caption = createCaption(elementDecl.getName() + " Type"); Element controlCaption = (Element) control .appendChild(xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "label")); this.setXFormsId(controlCaption); controlCaption.appendChild(xForm.createTextNode(caption)); // multiple compatible types for this element exist // in the schema - allow the user to choose from // between compatible non-abstract types Element bindElement = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "bind"); String bindId = this.setXFormsId(bindElement); bindElement.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "nodeset", pathToRoot + "/@xsi:type"); modelSection.appendChild(bindElement); control.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "bind", bindId); //add the "element" bind, in addition Element bindElement2 = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "bind"); String bindId2 = this.setXFormsId(bindElement2); bindElement2.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "nodeset", pathToRoot); modelSection.appendChild(bindElement2); if (enumValues.size() < Long.parseLong(getProperty(SELECTONE_LONG_LIST_SIZE_PROP))) { control.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "appearance", getProperty(SELECTONE_UI_CONTROL_SHORT_PROP)); } else { control.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "appearance", getProperty(SELECTONE_UI_CONTROL_LONG_PROP)); // add the "Please select..." instruction item for the combobox // and set the isValid attribute on the bind element to check for the "Please select..." // item to indicate that is not a valid value // String pleaseSelect = "[Select1 " + caption + "]"; Element item = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "item"); this.setXFormsId(item); choices.appendChild(item); Element captionElement = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "label"); this.setXFormsId(captionElement); item.appendChild(captionElement); captionElement.appendChild(xForm.createTextNode(pleaseSelect)); Element value = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "value"); this.setXFormsId(value); item.appendChild(value); value.appendChild(xForm.createTextNode(pleaseSelect)); // not(purchaseOrder/state = '[Choose State]') //String isValidExpr = "not(" + bindElement.getAttributeNS(XFORMS_NS, "nodeset") + " = '" + pleaseSelect + "')"; // ->no, not(. = '[Choose State]') String isValidExpr = "not( . = '" + pleaseSelect + "')"; //check if there was a constraint String constraint = bindElement.getAttributeNS(XFORMS_NS, "constraint"); if ((constraint != null) && !constraint.equals("")) { constraint = constraint + " && " + isValidExpr; } else { constraint = isValidExpr; } bindElement.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "constraint", constraint); } Element choicesControlWrapper = _wrapper.createControlsWrapper(choices); control.appendChild(choicesControlWrapper); Element controlWrapper = _wrapper.createControlsWrapper(control); formSection.appendChild(controlWrapper); ///////////////// /////////////// // add content to select1 HashMap case_types = new HashMap(); addChoicesForSelectSwitchControl(xForm, choices, enumValues, case_types); ///////////////// //add a trigger for this control (is there a way to not need it ?) Element trigger = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "trigger"); formSection.appendChild(trigger); this.setXFormsId(trigger); Element label_trigger = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "label"); this.setXFormsId(label_trigger); trigger.appendChild(label_trigger); String trigger_caption = createCaption("validate choice"); label_trigger.appendChild(xForm.createTextNode(trigger_caption)); Element action_trigger = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "action"); this.setXFormsId(action_trigger); trigger.appendChild(action_trigger); Element dispatch_trigger = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "dispatch"); this.setXFormsId(dispatch_trigger); action_trigger.appendChild(dispatch_trigger); dispatch_trigger.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "name", "DOMActivate"); dispatch_trigger.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "target", select1_id); ///////////////// //add switch Element switchElement = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "switch"); this.setXFormsId(switchElement); Element switchControlWrapper = _wrapper.createControlsWrapper(switchElement); formSection.appendChild(switchControlWrapper); //formSection.appendChild(switchElement); /////////////// add this type ////////////// Element firstCaseElement = (Element) case_types.get(controlType.getName()); switchElement.appendChild(firstCaseElement); addComplexType(xForm, modelSection, firstCaseElement, (XSComplexTypeDefinition) controlType, elementDecl, pathToRoot, true, false); /////////////// add sub types ////////////// it = compatibleTypes.iterator(); // add each compatible type within // a case statement while (it.hasNext()) { /*String compatibleTypeName = (String) it.next(); //WARNING: order of parameters inversed from the doc for 2.6.0 !!! XSTypeDefinition type =getSchema().getTypeDefinition( compatibleTypeName, targetNamespace);*/ XSTypeDefinition type = (XSTypeDefinition) it.next(); String compatibleTypeName = type.getName(); if (LOGGER.isDebugEnabled()) { if (type == null) LOGGER.debug(">>>addElement: compatible type is null!! type=" + compatibleTypeName + ", targetNamespace=" + targetNamespace); else LOGGER.debug(" >>>addElement: adding compatible type " + type.getName()); } if (type != null && type.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { //Element caseElement = (Element) xForm.createElementNS(XFORMS_NS,getXFormsNSPrefix() + "case"); //caseElement.setAttributeNS(XFORMS_NS,getXFormsNSPrefix() + "id",bindId + "_" + type.getName() +"_case"); //String case_id=this.setXFormsId(caseElement); Element caseElement = (Element) case_types.get(type.getName()); switchElement.appendChild(caseElement); addComplexType(xForm, modelSection, caseElement, (XSComplexTypeDefinition) type, elementDecl, pathToRoot, true, true); ////// // modify bind to add a "relevant" attribute that checks the value of @xsi:type // if (LOGGER.isDebugEnabled()) DOMUtil.prettyPrintDOM(bindElement2); NodeList binds = bindElement2.getElementsByTagNameNS(XFORMS_NS, "bind"); Element thisBind = null; int nb_binds = binds.getLength(); int i = 0; while (i < nb_binds && thisBind == null) { Element subBind = (Element) binds.item(i); String name = subBind.getAttributeNS(XFORMS_NS, "nodeset"); if (LOGGER.isDebugEnabled()) LOGGER.debug("Testing sub-bind with nodeset " + name); if (this.isElementDeclaredIn(name, (XSComplexTypeDefinition) type, false) || this.isAttributeDeclaredIn(name, (XSComplexTypeDefinition) type, false)) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Element/Attribute " + name + " declared in type " + type.getName() + ": adding relevant attribute"); //test sub types of this type TreeSet subCompatibleTypes = (TreeSet) typeTree.get(type.getName()); //TreeSet subCompatibleTypes = (TreeSet) typeTree.get(type); String newRelevant = null; if (subCompatibleTypes == null || subCompatibleTypes.isEmpty()) { //just add ../@xsi:type='type' newRelevant = "../@xsi:type='" + type.getName() + "'"; } else { //add ../@xsi:type='type' or ../@xsi:type='otherType' or ... newRelevant = "../@xsi:type='" + type.getName() + "'"; Iterator it_ct = subCompatibleTypes.iterator(); while (it_ct.hasNext()) { //String otherTypeName = (String) it_ct.next(); XSTypeDefinition otherType = (XSTypeDefinition) it_ct.next(); String otherTypeName = otherType.getName(); newRelevant = newRelevant + " or ../@xsi:type='" + otherTypeName + "'"; } } //change relevant attribute String relevant = subBind.getAttributeNS(XFORMS_NS, "relevant"); if (relevant != null && !relevant.equals("")) { newRelevant = "(" + relevant + ") and " + newRelevant; } if (newRelevant != null && !newRelevant.equals("")) subBind.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "relevant", newRelevant); } i++; } } } /*if (LOGGER.isDebugEnabled()) { LOGGER.debug( "###addElement for derived type: bind created:"); DOMUtil.prettyPrintDOM(bindElement2); }*/ // we're done // break; } else if (enumValues.size() == 1) { // only one compatible type, set the controlType value // and fall through // //controlType = getSchema().getComplexType((String)enumValues.get(0)); controlType = getSchema().getTypeDefinition((String) enumValues.get(0), targetNamespace); } } else if (LOGGER.isDebugEnabled()) LOGGER.debug("No compatible type found for " + typeName); //name not null but no compatibleType? relative = true; } if (relative) //create the bind in case it is a repeat { if (LOGGER.isDebugEnabled()) LOGGER.debug(">>>Adding empty bind for " + typeName); // create the <xforms:bind> element and add it to the model. Element bindElement = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "bind"); String bindId = this.setXFormsId(bindElement); bindElement.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "nodeset", pathToRoot); modelSection.appendChild(bindElement); } else if (LOGGER.isDebugEnabled()) { LOGGER.debug("addElement: bind is not relative for " + elementDecl.getName()); } //addComplexType(xForm,modelSection, formSection,(ComplexType)controlType,elementDecl,pathToRoot, relative); addComplexType(xForm, modelSection, formSection, (XSComplexTypeDefinition) controlType, elementDecl, pathToRoot, true, false); break; } } default: // TODO: add wildcard support LOGGER.warn("\nWARNING!!! - Unsupported type [" + elementDecl.getType() + "] for node [" + controlType.getName() + "]"); } } /** * check that the element defined by this name is declared directly in the type **/ /*private boolean isElementDeclaredIn(String name, XSComplexTypeDefinition type){ boolean found=false; XSParticle particle=type.getParticle(); XSTerm term=particle.getTerm(); if(term instanceof XSModelGroup){ XSModelGroup group=(XSModelGroup) term; XSObjectList particles=group.getParticles(); int i=0; int nb=particles.getLength(); while(i<nb){ XSParticle subPart=(XSParticle) particles.item(i); XSTerm subTerm=subPart.getTerm(); if(subTerm instanceof XSElementDeclaration){ XSElementDeclaration elDecl=(XSElementDeclaration) subTerm; if(name.equals(elDecl.getName())) found=true; } i++; } } return found; }*/ /** * check that the element defined by this name is declared directly in the type */ private boolean isElementDeclaredIn(String name, XSComplexTypeDefinition type, boolean recursive) { boolean found = false; if (LOGGER.isDebugEnabled()) LOGGER.debug("isElement " + name + " declared in " + type.getName()); //test if extension + declared in parent + not recursive -> NOK if (!recursive && type.getDerivationMethod() == XSConstants.DERIVATION_EXTENSION) { XSComplexTypeDefinition parent = (XSComplexTypeDefinition) type.getBaseType(); if (LOGGER.isDebugEnabled()) LOGGER.debug("testing if it is not on parent " + parent.getName()); if (this.isElementDeclaredIn(name, parent, true)) return false; } XSParticle particle = type.getParticle(); if (particle != null) { XSTerm term = particle.getTerm(); if (term instanceof XSModelGroup) { XSModelGroup group = (XSModelGroup) term; found = this.isElementDeclaredIn(name, group); } } if (LOGGER.isDebugEnabled()) LOGGER.debug("isElement " + name + " declared in " + type.getName() + ": " + found); return found; } /** * private recursive method called by isElementDeclaredIn(String name, XSComplexTypeDefinition type) */ private boolean isElementDeclaredIn(String name, XSModelGroup group) { if (LOGGER.isDebugEnabled()) LOGGER.debug("isElement " + name + " declared in group " + group.getName()); boolean found = false; XSObjectList particles = group.getParticles(); int i = 0; int nb = particles.getLength(); while (i < nb) { XSParticle subPart = (XSParticle) particles.item(i); XSTerm subTerm = subPart.getTerm(); if (subTerm instanceof XSElementDeclaration) { XSElementDeclaration elDecl = (XSElementDeclaration) subTerm; if (name.equals(elDecl.getName())) found = true; } else if (subTerm instanceof XSModelGroup) { //recursive found = this.isElementDeclaredIn(name, (XSModelGroup) subTerm); } i++; } if (LOGGER.isDebugEnabled()) LOGGER.debug("isElement " + name + " declared in group " + group.getName() + ": " + found); return found; } private boolean doesElementComeFromExtension(XSElementDeclaration element, XSComplexTypeDefinition controlType) { if (LOGGER.isDebugEnabled()) LOGGER.debug("doesElementComeFromExtension for " + element.getName() + " and controlType=" + controlType.getName()); boolean comesFromExtension = false; if (controlType.getDerivationMethod() == XSConstants.DERIVATION_EXTENSION) { XSTypeDefinition baseType = controlType.getBaseType(); if (baseType.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { XSComplexTypeDefinition complexType = (XSComplexTypeDefinition) baseType; if (this.isElementDeclaredIn(element.getName(), complexType, true)) { if (LOGGER.isDebugEnabled()) LOGGER.debug("doesElementComeFromExtension: yes"); comesFromExtension = true; } else { //recursive call if (LOGGER.isDebugEnabled()) LOGGER.debug("doesElementComeFromExtension: recursive call on previous level"); comesFromExtension = this.doesElementComeFromExtension(element, complexType); } } } return comesFromExtension; } /** * check that the element defined by this name is declared directly in the type */ private boolean isAttributeDeclaredIn(XSAttributeUse attr, XSComplexTypeDefinition type, boolean recursive) { boolean found = false; if (LOGGER.isDebugEnabled()) LOGGER.debug("is Attribute " + attr.getAttrDeclaration().getName() + " declared in " + type.getName()); //check on parent if not recursive if (!recursive && type.getDerivationMethod() == XSConstants.DERIVATION_EXTENSION) { XSComplexTypeDefinition parent = (XSComplexTypeDefinition) type.getBaseType(); if (LOGGER.isDebugEnabled()) LOGGER.debug("testing if it is not on parent " + parent.getName()); if (this.isAttributeDeclaredIn(attr, parent, true)) return false; } //check on this type (also checks recursively) XSObjectList attrs = type.getAttributeUses(); int nb = attrs.getLength(); int i = 0; while (i < nb && !found) { XSAttributeUse anAttr = (XSAttributeUse) attrs.item(i); if (anAttr == attr) found = true; i++; } //recursive call /*if(!found && recursive && type.getDerivationMethod()==XSConstants.DERIVATION_EXTENSION){ XSComplexTypeDefinition base=(XSComplexTypeDefinition) type.getBaseType(); if(base!=null && base!=type) found = this.isAttributeDeclaredIn(attr, base, true); }*/ if (LOGGER.isDebugEnabled()) LOGGER.debug("is Attribute " + attr.getName() + " declared in " + type.getName() + ": " + found); return found; } /** * check that the element defined by this name is declared directly in the type * -> idem with string */ private boolean isAttributeDeclaredIn(String attrName, XSComplexTypeDefinition type, boolean recursive) { boolean found = false; if (LOGGER.isDebugEnabled()) LOGGER.debug("is Attribute " + attrName + " declared in " + type.getName()); if (attrName.startsWith("@")) attrName = attrName.substring(1); //check on parent if not recursive if (!recursive && type.getDerivationMethod() == XSConstants.DERIVATION_EXTENSION) { XSComplexTypeDefinition parent = (XSComplexTypeDefinition) type.getBaseType(); if (LOGGER.isDebugEnabled()) LOGGER.debug("testing if it is not on parent " + parent.getName()); if (this.isAttributeDeclaredIn(attrName, parent, true)) return false; } //check on this type (also checks recursively) XSObjectList attrs = type.getAttributeUses(); int nb = attrs.getLength(); int i = 0; while (i < nb && !found) { XSAttributeUse anAttr = (XSAttributeUse) attrs.item(i); if (anAttr != null) { String name = anAttr.getName(); if (name == null || name.equals("")) name = anAttr.getAttrDeclaration().getName(); if (attrName.equals(name)) found = true; } i++; } //recursive call -> no need /*if(!found && recursive && type.getDerivationMethod()==XSConstants.DERIVATION_EXTENSION){ XSComplexTypeDefinition base=(XSComplexTypeDefinition) type.getBaseType(); if(base!=null && base!=type) found = this.isAttributeDeclaredIn(attrName, base, true); }*/ if (LOGGER.isDebugEnabled()) LOGGER.debug("is Attribute " + attrName + " declared in " + type.getName() + ": " + found); return found; } private boolean doesAttributeComeFromExtension(XSAttributeUse attr, XSComplexTypeDefinition controlType) { if (LOGGER.isDebugEnabled()) LOGGER.debug("doesAttributeComeFromExtension for " + attr.getAttrDeclaration().getName() + " and controlType=" + controlType.getName()); boolean comesFromExtension = false; if (controlType.getDerivationMethod() == XSConstants.DERIVATION_EXTENSION) { XSTypeDefinition baseType = controlType.getBaseType(); if (baseType.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { XSComplexTypeDefinition complexType = (XSComplexTypeDefinition) baseType; if (this.isAttributeDeclaredIn(attr, complexType, true)) { if (LOGGER.isDebugEnabled()) LOGGER.debug("doesAttributeComeFromExtension: yes"); comesFromExtension = true; } else { //recursive call if (LOGGER.isDebugEnabled()) LOGGER.debug("doesAttributeComeFromExtension: recursive call on previous level"); comesFromExtension = this.doesAttributeComeFromExtension(attr, complexType); } } } return comesFromExtension; } /** * checkIfExtension: if false, addElement is called wether it is an extension or not * if true, if it is an extension, element is recopied (and no additional bind) */ private void addGroup(Document xForm, Element modelSection, Element formSection, XSModelGroup group, XSComplexTypeDefinition controlType, XSElementDeclaration owner, String pathToRoot, int minOccurs, int maxOccurs, boolean checkIfExtension) { if (group != null) { Element repeatSection = addRepeatIfNecessary(xForm, modelSection, formSection, owner.getTypeDefinition(), minOccurs, maxOccurs, pathToRoot); Element repeatContentWrapper = repeatSection; if (repeatSection != formSection) { //selector -> no more needed? //this.addSelector(xForm, repeatSection); //group wrapper repeatContentWrapper = _wrapper.createGroupContentWrapper(repeatSection); } if (LOGGER.isDebugEnabled()) LOGGER.debug( "addGroup from owner=" + owner.getName() + " and controlType=" + controlType.getName()); XSObjectList particles = group.getParticles(); for (int counter = 0; counter < particles.getLength(); counter++) { XSParticle currentNode = (XSParticle) particles.item(counter); XSTerm term = currentNode.getTerm(); if (LOGGER.isDebugEnabled()) LOGGER.debug(" : next term = " + term.getName()); int childMaxOccurs = currentNode.getMaxOccurs(); if (currentNode.getMaxOccursUnbounded()) childMaxOccurs = -1; int childMinOccurs = currentNode.getMinOccurs(); if (term instanceof XSModelGroup) { if (LOGGER.isDebugEnabled()) LOGGER.debug(" term is a group"); addGroup(xForm, modelSection, repeatContentWrapper, ((XSModelGroup) term), controlType, owner, pathToRoot, childMinOccurs, childMaxOccurs, checkIfExtension); } else if (term instanceof XSElementDeclaration) { XSElementDeclaration element = (XSElementDeclaration) term; if (LOGGER.isDebugEnabled()) LOGGER.debug(" term is an element declaration: " + term.getName()); //special case for types already added because used in an extension //do not add it when it comes from an extension !!! //-> make a copy from the existing form control if (checkIfExtension && this.doesElementComeFromExtension(element, controlType)) { if (LOGGER.isDebugEnabled()) { LOGGER.debug( "This element comes from an extension: recopy form controls.\n Model Section="); DOMUtil.prettyPrintDOM(modelSection); } //find the existing bind Id //(modelSection is the enclosing bind of the element) NodeList binds = modelSection.getElementsByTagNameNS(XFORMS_NS, "bind"); int i = 0; int nb = binds.getLength(); String bindId = null; while (i < nb && bindId == null) { Element bind = (Element) binds.item(i); String nodeset = bind.getAttributeNS(XFORMS_NS, "nodeset"); if (nodeset != null && nodeset.equals(element.getName())) bindId = bind.getAttributeNS(XFORMS_NS, "id"); i++; } //find the control Element control = null; if (bindId != null) { if (LOGGER.isDebugEnabled()) LOGGER.debug("bindId found: " + bindId); JXPathContext context = JXPathContext.newContext(formSection.getOwnerDocument()); Pointer pointer = context .getPointer("//*[@" + this.getXFormsNSPrefix() + "bind='" + bindId + "']"); if (pointer != null) control = (Element) pointer.getNode(); } //copy it if (control == null) { LOGGER.warn("Corresponding control not found"); } else { Element newControl = (Element) control.cloneNode(true); //set new Ids to XForm elements this.resetXFormIds(newControl); repeatContentWrapper.appendChild(newControl); } } else { //add it normally String elementName = getElementName(element, xForm); String path = pathToRoot + "/" + elementName; if (pathToRoot.equals("")) { //relative path = elementName; } addElement(xForm, modelSection, repeatContentWrapper, element, element.getTypeDefinition(), path); } } else { //XSWildcard -> ignore ? //LOGGER.warn("XSWildcard found in group from "+owner.getName()+" for pathToRoot="+pathToRoot); } } if (LOGGER.isDebugEnabled()) LOGGER.debug("--- end of addGroup from owner=" + owner.getName()); } } /** * Add a repeat section if maxOccurs > 1. */ private Element addRepeatIfNecessary(Document xForm, Element modelSection, Element formSection, XSTypeDefinition controlType, int minOccurs, int maxOccurs, String pathToRoot) { Element repeatSection = formSection; // add xforms:repeat section if this element re-occurs // if (maxOccurs != 1) { if (LOGGER.isDebugEnabled()) LOGGER.debug("DEBUG: AddRepeatIfNecessary for multiple element for type " + controlType.getName() + ", maxOccurs=" + maxOccurs); //repeatSection = (Element) formSection.appendChild(xForm.createElementNS(XFORMS_NS,getXFormsNSPrefix() + "repeat")); repeatSection = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "repeat"); //bind instead of repeat //repeatSection.setAttributeNS(XFORMS_NS,getXFormsNSPrefix() + "nodeset",pathToRoot); // bind -> last element in the modelSection Element bind = DOMUtil.getLastChildElement(modelSection); String bindId = null; if ((bind != null) && (bind.getLocalName() != null) && bind.getLocalName().equals("bind")) { bindId = bind.getAttributeNS(SchemaFormBuilder.XFORMS_NS, "id"); } else { LOGGER.warn("addRepeatIfNecessary: bind not found: " + bind + " (model selection name=" + modelSection.getNodeName() + ")"); //if no bind is found -> modelSection is already a bind, get its parent last child bind = DOMUtil.getLastChildElement(modelSection.getParentNode()); if ((bind != null) && (bind.getLocalName() != null) && bind.getLocalName().equals("bind")) { bindId = bind.getAttributeNS(SchemaFormBuilder.XFORMS_NS, "id"); } else { LOGGER.warn("addRepeatIfNecessary: bind really not found"); } } repeatSection.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "bind", bindId); this.setXFormsId(repeatSection); //appearance=full is more user friendly repeatSection.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "appearance", "full"); //triggers this.addTriggersForRepeat(xForm, formSection, repeatSection, minOccurs, maxOccurs, bindId); Element controlWrapper = _wrapper.createControlsWrapper(repeatSection); formSection.appendChild(controlWrapper); //add a group inside the repeat? Element group = xForm.createElementNS(XFORMS_NS, this.getXFormsNSPrefix() + "group"); this.setXFormsId(group); repeatSection.appendChild(group); repeatSection = group; } return repeatSection; } /** * if "createBind", a bind is created, otherwise bindId is used */ private void addSimpleType(Document xForm, Element modelSection, Element formSection, XSTypeDefinition controlType, String owningElementName, XSObject owner, String pathToRoot, int minOccurs, int maxOccurs) { if (LOGGER.isDebugEnabled()) LOGGER.debug("addSimpleType for " + controlType.getName() + " (owningElementName=" + owningElementName + ")"); // create the <xforms:bind> element and add it to the model. Element bindElement = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "bind"); String bindId = this.setXFormsId(bindElement); bindElement.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "nodeset", pathToRoot); bindElement = (Element) modelSection.appendChild(bindElement); bindElement = startBindElement(bindElement, controlType, minOccurs, maxOccurs); // add a group if a repeat ! if (owner instanceof XSElementDeclaration && maxOccurs != 1) { Element groupElement = createGroup(xForm, modelSection, formSection, (XSElementDeclaration) owner); //set content Element groupWrapper = groupElement; if (groupElement != modelSection) { groupWrapper = _wrapper.createGroupContentWrapper(groupElement); } formSection = groupWrapper; } //eventual repeat Element repeatSection = addRepeatIfNecessary(xForm, modelSection, formSection, controlType, minOccurs, maxOccurs, pathToRoot); // create the form control element //put a wrapper for the repeat content, but only if it is really a repeat Element contentWrapper = repeatSection; if (repeatSection != formSection) { //content of repeat contentWrapper = _wrapper.createGroupContentWrapper(repeatSection); //if there is a repeat -> create another bind with "." Element bindElement2 = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "bind"); String bindId2 = this.setXFormsId(bindElement2); bindElement2.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "nodeset", "."); //recopy other attributes: required and type // ->no, attributes shouldn't be copied /*String required = "required"; String type = "type"; if (bindElement.hasAttributeNS(XFORMS_NS, required)) { bindElement2.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + required, bindElement.getAttributeNS(XFORMS_NS, required)); } if (bindElement.hasAttributeNS(XFORMS_NS, type)) { bindElement2.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + type, bindElement.getAttributeNS(XFORMS_NS, type)); }*/ bindElement.appendChild(bindElement2); bindId = bindId2; } String caption = createCaption(owningElementName); //Element formControl = (Element) contentWrapper.appendChild(createFormControl(xForm,caption,controlType,bindId,bindElement,minOccurs,maxOccurs)); Element formControl = createFormControl(xForm, caption, controlType, bindId, bindElement, minOccurs, maxOccurs); Element controlWrapper = _wrapper.createControlsWrapper(formControl); contentWrapper.appendChild(controlWrapper); // if this is a repeatable then set ref to point to current element // not sure if this is a workaround or this is just the way XForms works... // if (!repeatSection.equals(formSection)) { formControl.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "ref", "."); } Element hint = createHint(xForm, owner); if (hint != null) { formControl.appendChild(hint); } //add selector if repeat //if (repeatSection != formSection) //this.addSelector(xForm, (Element) formControl.getParentNode()); // // TODO: Generate help message based on datatype and restrictions endFormControl(formControl, controlType, minOccurs, maxOccurs); endBindElement(bindElement); } private void addSimpleType(Document xForm, Element modelSection, Element formSection, XSSimpleTypeDefinition controlType, XSElementDeclaration owner, String pathToRoot) { int[] occurance = this.getOccurance(owner); addSimpleType(xForm, modelSection, formSection, controlType, owner.getName(), owner, pathToRoot, occurance[0], occurance[1]); } private void addSimpleType(Document xForm, Element modelSection, Element formSection, XSSimpleTypeDefinition controlType, XSAttributeUse owningAttribute, String pathToRoot) { addSimpleType(xForm, modelSection, formSection, controlType, owningAttribute.getAttrDeclaration().getName(), owningAttribute, pathToRoot, owningAttribute.getRequired() ? 1 : 0, 1); } /** * adds a "chiba:selector" in the specified element **/ /*private void addSelector(Document xForm, Element el) { Element extension = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "extension"); el.appendChild(extension); Element selector = xForm.createElementNS(XFORMS_NS, getChibaNSPrefix() + "selector"); extension.appendChild(selector); }*/ /** * add triggers to use the repeat elements (allow to add an element, ...) */ private void addTriggersForRepeat(Document xForm, Element formSection, Element repeatSection, int minOccurs, int maxOccurs, String bindId) { ///////////// insert ////////////////// //trigger insert Element trigger_insert = xForm.createElementNS(SchemaFormBuilder.XFORMS_NS, SchemaFormBuilder.xformsNSPrefix + "trigger"); this.setXFormsId(trigger_insert); //label insert Element triggerLabel_insert = xForm.createElementNS(SchemaFormBuilder.XFORMS_NS, SchemaFormBuilder.xformsNSPrefix + "label"); this.setXFormsId(triggerLabel_insert); trigger_insert.appendChild(triggerLabel_insert); triggerLabel_insert.setAttributeNS(SchemaFormBuilder.XLINK_NS, SchemaFormBuilder.xlinkNSPrefix + "href", "images/add_new.gif"); Text label_insert = xForm.createTextNode("Insert after selected"); triggerLabel_insert.appendChild(label_insert); //hint insert Element hint_insert = xForm.createElementNS(SchemaFormBuilder.XFORMS_NS, SchemaFormBuilder.xformsNSPrefix + "hint"); this.setXFormsId(hint_insert); Text hint_insert_text = xForm.createTextNode("inserts a new entry in this collection"); hint_insert.appendChild(hint_insert_text); trigger_insert.appendChild(hint_insert); //insert action Element action_insert = xForm.createElementNS(SchemaFormBuilder.XFORMS_NS, SchemaFormBuilder.xformsNSPrefix + "action"); trigger_insert.appendChild(action_insert); this.setXFormsId(action_insert); Element insert = xForm.createElementNS(SchemaFormBuilder.XFORMS_NS, SchemaFormBuilder.xformsNSPrefix + "insert"); action_insert.appendChild(insert); this.setXFormsId(insert); //insert: bind & other attributes if (bindId != null) { insert.setAttributeNS(SchemaFormBuilder.XFORMS_NS, SchemaFormBuilder.xformsNSPrefix + "bind", bindId); } insert.setAttributeNS(SchemaFormBuilder.XFORMS_NS, SchemaFormBuilder.xformsNSPrefix + "position", "after"); //xforms:at = xforms:index from the "id" attribute on the repeat element String repeatId = repeatSection.getAttributeNS(SchemaFormBuilder.XFORMS_NS, "id"); if (repeatId != null) { insert.setAttributeNS(SchemaFormBuilder.XFORMS_NS, SchemaFormBuilder.xformsNSPrefix + "at", SchemaFormBuilder.xformsNSPrefix + "index('" + repeatId + "')"); } ///////////// delete ////////////////// //trigger delete Element trigger_delete = xForm.createElementNS(SchemaFormBuilder.XFORMS_NS, SchemaFormBuilder.xformsNSPrefix + "trigger"); this.setXFormsId(trigger_delete); //label delete Element triggerLabel_delete = xForm.createElementNS(SchemaFormBuilder.XFORMS_NS, SchemaFormBuilder.xformsNSPrefix + "label"); this.setXFormsId(triggerLabel_delete); trigger_delete.appendChild(triggerLabel_delete); triggerLabel_delete.setAttributeNS(SchemaFormBuilder.XLINK_NS, SchemaFormBuilder.xlinkNSPrefix + "href", "images/delete.gif"); Text label_delete = xForm.createTextNode("Delete selected"); triggerLabel_delete.appendChild(label_delete); //hint delete Element hint_delete = xForm.createElementNS(SchemaFormBuilder.XFORMS_NS, SchemaFormBuilder.xformsNSPrefix + "hint"); this.setXFormsId(hint_delete); Text hint_delete_text = xForm.createTextNode("deletes selected entry from this collection"); hint_delete.appendChild(hint_delete_text); trigger_delete.appendChild(hint_delete); //delete action Element action_delete = xForm.createElementNS(SchemaFormBuilder.XFORMS_NS, SchemaFormBuilder.xformsNSPrefix + "action"); trigger_delete.appendChild(action_delete); this.setXFormsId(action_delete); Element delete = xForm.createElementNS(SchemaFormBuilder.XFORMS_NS, SchemaFormBuilder.xformsNSPrefix + "delete"); action_delete.appendChild(delete); this.setXFormsId(delete); //delete: bind & other attributes if (bindId != null) { delete.setAttributeNS(SchemaFormBuilder.XFORMS_NS, SchemaFormBuilder.xformsNSPrefix + "bind", bindId); } //xforms:at = xforms:index from the "id" attribute on the repeat element if (repeatId != null) { delete.setAttributeNS(SchemaFormBuilder.XFORMS_NS, SchemaFormBuilder.xformsNSPrefix + "at", SchemaFormBuilder.xformsNSPrefix + "index('" + repeatId + "')"); } //add the triggers Element wrapper_triggers = _wrapper.createControlsWrapper(trigger_insert); if (wrapper_triggers == trigger_insert) { //no wrapper formSection.appendChild(trigger_insert); formSection.appendChild(trigger_delete); } else { formSection.appendChild(wrapper_triggers); Element insert_parent = (Element) trigger_insert.getParentNode(); if (insert_parent != null) { insert_parent.appendChild(trigger_delete); } } } /** * Build the type tree */ /*private void buildTypeTree(XSTypeDefinition type, TreeSet descendents) { if (type != null) { if (descendents.size() > 0) { TreeSet compatibleTypes = (TreeSet) typeTree.get(type.getName()); if (compatibleTypes == null) { compatibleTypes = new TreeSet(descendents); typeTree.put(type.getName(), compatibleTypes); } else { compatibleTypes.addAll(descendents); } } XSTypeDefinition parentType = type.getBaseType(); if (parentType != null && type.getTypeCategory() == parentType.getTypeCategory()) { String typeName = type.getName(); String parentTypeName = parentType.getName(); if ((typeName == null && parentTypeName != null) || (typeName != null && parentTypeName == null) || (typeName != null && parentTypeName != null && !type.getName().equals(parentType.getName()) && !parentType.getName().equals("anyType"))) { TreeSet newDescendents=new TreeSet(descendents); //extension (we only add it to "newDescendants" because we don't want //to have a type descendant to itself, but to consider it for the parent if (type.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { XSComplexTypeDefinition complexType = (XSComplexTypeDefinition) type; if (complexType.getDerivationMethod() == XSConstants.DERIVATION_EXTENSION && !complexType.getAbstract() && !descendents.contains(type.getName()) //to be tested ) { newDescendents.add(type.getName()); } } //note: extensions are impossible on simpleTypes ! buildTypeTree(parentType, newDescendents); } } } }*/ private void buildTypeTree(XSTypeDefinition type, TreeSet descendents) { if (type != null) { if (descendents.size() > 0) { //TreeSet compatibleTypes = (TreeSet) typeTree.get(type.getName()); TreeSet compatibleTypes = (TreeSet) typeTree.get(type.getName()); if (compatibleTypes == null) { //compatibleTypes = new TreeSet(descendents); compatibleTypes = new TreeSet(TypeExtensionSorter.getInstance()); compatibleTypes.addAll(descendents); //typeTree.put(type.getName(), compatibleTypes); typeTree.put(type.getName(), compatibleTypes); } else { compatibleTypes.addAll(descendents); } } XSTypeDefinition parentType = type.getBaseType(); if (parentType != null && type.getTypeCategory() == parentType.getTypeCategory()) { /*String typeName = type.getName(); String parentTypeName = parentType.getName(); if ((typeName == null && parentTypeName != null) || (typeName != null && parentTypeName == null) || (typeName != null && parentTypeName != null && !type.getName().equals(parentType.getName()) && !parentType.getName().equals("anyType"))) {*/ if (type != parentType && (parentType.getName() == null || !parentType.getName().equals("anyType"))) { //TreeSet newDescendents=new TreeSet(descendents); TreeSet newDescendents = new TreeSet(TypeExtensionSorter.getInstance()); newDescendents.addAll(descendents); //extension (we only add it to "newDescendants" because we don't want //to have a type descendant to itself, but to consider it for the parent if (type.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { XSComplexTypeDefinition complexType = (XSComplexTypeDefinition) type; if (complexType.getDerivationMethod() == XSConstants.DERIVATION_EXTENSION && !complexType.getAbstract() && !descendents.contains(type) //to be tested //&& !descendents.contains(type.getName()) //to be tested ) { //newDescendents.add(type.getName()); newDescendents.add(type); } } //note: extensions are impossible on simpleTypes ! buildTypeTree(parentType, newDescendents); } } } } private void buildTypeTree(XSModel schema) { // build the type tree for complex types // XSNamedMap types = schema.getComponents(XSConstants.TYPE_DEFINITION); int nb = types.getLength(); for (int i = 0; i < nb; i++) { XSTypeDefinition t = (XSTypeDefinition) types.item(i); if (t.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { XSComplexTypeDefinition type = (XSComplexTypeDefinition) t; buildTypeTree(type, new TreeSet(TypeExtensionSorter.getInstance())); } } // build the type tree for simple types for (int i = 0; i < nb; i++) { XSTypeDefinition t = (XSTypeDefinition) types.item(i); if (t.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) { XSSimpleTypeDefinition type = (XSSimpleTypeDefinition) t; buildTypeTree(type, new TreeSet(TypeExtensionSorter.getInstance())); } } // print out type hierarchy for debugging purposes if (LOGGER.isDebugEnabled()) { Iterator keys = typeTree.keySet().iterator(); while (keys.hasNext()) { String typeName = (String) keys.next(); TreeSet descendents = (TreeSet) typeTree.get(typeName); LOGGER.debug(">>>> for " + typeName + " Descendants=\n "); Iterator it = descendents.iterator(); while (it.hasNext()) { XSTypeDefinition desc = (XSTypeDefinition) it.next(); LOGGER.debug(" " + desc.getName()); } } } } private Element createFormControl(Document xForm, String caption, XSTypeDefinition controlType, String bindId, Element bindElement, int minOccurs, int maxOccurs) { // Select1 xform control to use: // Will use one of the following: input, textarea, selectOne, selectBoolean, selectMany, range // secret, output, button, do not apply // // select1: enumeration or keyref constrained value // select: list // range: union (? not sure about this) // textarea : ??? // input: default // Element formControl = null; if (controlType.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) { XSSimpleTypeDefinition simpleType = (XSSimpleTypeDefinition) controlType; if (simpleType.getItemType() != null) //list { formControl = createControlForListType(xForm, simpleType, caption, bindElement); } else { //other simple type // need to check constraints to determine which form control to use // // use the selectOne control // //XSObjectList enumerationFacets = simpleType.getFacets(XSSimpleTypeDefinition.FACET_ENUMERATION); //if(enumerationFacets.getLength()>0){ if (simpleType.isDefinedFacet(XSSimpleTypeDefinition.FACET_ENUMERATION)) { formControl = createControlForEnumerationType(xForm, simpleType, caption, bindElement); } /*if (enumerationFacets.hasMoreElements()) { formControl = createControlForEnumerationType(xForm, (SimpleType)controlType, caption, bindElement); } */ } } else if (controlType.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE && controlType.getName().equals("anyType")) { formControl = createControlForAnyType(xForm, caption, controlType); } if (formControl == null) { // default situation - use an input control // formControl = createControlForAtomicType(xForm, caption, (XSSimpleTypeDefinition) controlType); } startFormControl(formControl, controlType); formControl.setAttributeNS(XFORMS_NS, getXFormsNSPrefix() + "bind", bindId); //put the label before // no -> put in the "createControlFor..." methods /*Element captionElement=xForm.createElementNS(XFORMS_NS,getXFormsNSPrefix() + "label"); this.setXFormsId(captionElement); captionElement.appendChild(xForm.createTextNode(caption)); if(formControl.hasChildNodes()) { Node first=formControl.getFirstChild(); captionElement = (Element) formControl.insertBefore(captionElement, first); } else captionElement = (Element) formControl.appendChild(captionElement); */ // TODO: Enhance alert statement based on facet restrictions. // TODO: Enhance to support minOccurs > 1 and maxOccurs > 1. // TODO: Add i18n/l10n suppport to this - use java MessageFormatter... // // e.g. Please provide a valid value for 'Address'. 'Address' is a mandatory decimal field. // Element alertElement = (Element) formControl .appendChild(xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "alert")); this.setXFormsId(alertElement); StringBuffer alert = new StringBuffer("Please provide a valid value for '" + caption + "'."); Element enveloppe = xForm.getDocumentElement(); if (minOccurs != 0) { alert.append(" '" + caption + "' is a required '" + createCaption(this.getXFormsTypeName(enveloppe, controlType)) + "' value."); } else { alert.append(" '" + caption + "' is an optional '" + createCaption(this.getXFormsTypeName(enveloppe, controlType)) + "' value."); } alertElement.appendChild(xForm.createTextNode(alert.toString())); return formControl; } /** * used to get the type name that will be used in the XForms document * * @param context the element which will serve as context for namespaces * @param controlType the type from which we want the name * @return the complete type name (with namespace prefix) of the type in the XForms doc */ protected String getXFormsTypeName(Element context, XSTypeDefinition controlType) { String result = null; String typeName = controlType.getName(); String typeNS = controlType.getNamespace(); //if we use XMLSchema types: //first check if it is a simple type named in the XMLSchema if (_useSchemaTypes && controlType.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE && typeName != null && !typeName.equals("") && schema.getTypeDefinition(typeName, typeNS) != null) { //type is globally defined //use schema type //local type name String localTypeName = typeName; int index = typeName.indexOf(":"); if (index > -1 && typeName.length() > index) localTypeName = typeName.substring(index + 1); //namespace prefix in this document String prefix = NamespaceCtx.getPrefix(context, typeNS); //completeTypeName = new prefix + local name result = localTypeName; if (prefix != null && !prefix.equals("")) result = prefix + ":" + localTypeName; if (LOGGER.isDebugEnabled()) LOGGER.debug( "getXFormsTypeName: typeName=" + typeName + ", typeNS=" + typeNS + ", result=" + result); } else { //use built in type result = this.getDataTypeName(getBuiltInType(controlType)); } return result; } private Document createFormTemplate(String formId) throws IOException, ParserConfigurationException { return createFormTemplate(formId, "Form " + formId, getProperty(CSS_STYLE_PROP, DEFAULT_CSS_STYLE_PROP)); } private Document createFormTemplate(String formId, String formName, String stylesheet) throws ParserConfigurationException { Document xForm = documentBuilder.newDocument(); Element envelopeElement = _wrapper.createEnvelope(xForm); // set required namespace attributes envelopeElement.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:" + getChibaNSPrefix().substring(0, getChibaNSPrefix().length() - 1), CHIBA_NS); envelopeElement.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:" + getXFormsNSPrefix().substring(0, getXFormsNSPrefix().length() - 1), XFORMS_NS); envelopeElement.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:" + getXLinkNSPrefix().substring(0, getXLinkNSPrefix().length() - 1), XLINK_NS); //XMLEvent envelopeElement.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:" + xmleventsNSPrefix.substring(0, xmleventsNSPrefix.length() - 1), XMLEVENTS_NS); //XML Schema Instance envelopeElement.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:" + xmlSchemaInstancePrefix.substring(0, xmlSchemaInstancePrefix.length() - 1), XMLSCHEMA_INSTANCE_NAMESPACE_URI); //base if (_base != null && !_base.equals("")) { envelopeElement.setAttributeNS(XML_NAMESPACE_URI, "xml:base", _base); } //model element Element modelElement = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "model"); this.setXFormsId(modelElement); Element modelWrapper = _wrapper.createModelWrapper(modelElement); envelopeElement.appendChild(modelWrapper); //form control wrapper -> created by wrapper //Element formWrapper = xForm.createElement("body"); //envelopeElement.appendChild(formWrapper); Element formWrapper = _wrapper.createFormWrapper(envelopeElement); return xForm; } private Element createGroup(Document xForm, Element modelSection, Element formSection, XSElementDeclaration owner) { // add a group node and recurse // Element groupElement = xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "group"); groupElement = startFormGroup(groupElement, owner); if (groupElement != null) { this.setXFormsId(groupElement); Element controlsWrapper = _wrapper.createControlsWrapper(groupElement); //groupElement = (Element) formSection.appendChild(groupElement); formSection.appendChild(controlsWrapper); Element captionElement = (Element) groupElement .appendChild(xForm.createElementNS(XFORMS_NS, getXFormsNSPrefix() + "label")); this.setXFormsId(captionElement); captionElement.appendChild(xForm.createTextNode(createCaption(owner))); //no hint in groups /*Element hint = createHint(xForm,owner); if (hint != null) { groupElement.appendChild(hint); }*/ } else { groupElement = modelSection; } return groupElement; } /** * Get a fully qualified name for this element, and eventually declares a new prefix for the namespace if * it was not declared before * * @param element * @param xForm * @return */ private String getElementName(XSElementDeclaration element, Document xForm) { String elementName = element.getName(); String namespace = element.getNamespace(); if (namespace != null && !namespace.equals("")) { String prefix; if ((prefix = (String) namespacePrefixes.get(namespace)) == null) { String basePrefix = (namespace.substring(namespace.lastIndexOf('/', namespace.length() - 2) + 1)); int i = 1; prefix = basePrefix; while (namespacePrefixes.containsValue(prefix)) { prefix = basePrefix + (i++); } namespacePrefixes.put(namespace, prefix); Element envelope = xForm.getDocumentElement(); envelope.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:" + prefix, namespace); } elementName = prefix + ":" + elementName; } return elementName; } }