/** * Copyright (C) 2012 BonitaSoft S.A. * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation * version 2.1 of the License. * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301, USA. **/ package org.bonitasoft.forms.server.builder.impl; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.StringReader; import java.net.URL; import java.nio.charset.Charset; import java.util.Arrays; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.forms.client.model.ActionType; import org.bonitasoft.forms.client.model.FileWidgetInputType; import org.bonitasoft.forms.client.model.FormType; import org.bonitasoft.forms.client.model.ReducedFormSubtitle.SubTitlePosition; import org.bonitasoft.forms.client.model.ReducedFormValidator.ValidatorPosition; import org.bonitasoft.forms.client.model.ReducedFormWidget.ItemPosition; import org.bonitasoft.forms.client.model.ReducedFormWidget.SelectMode; import org.bonitasoft.forms.client.model.WidgetType; import org.bonitasoft.forms.server.builder.IFormBuilder; import org.bonitasoft.forms.server.constants.XMLForms; import org.bonitasoft.forms.server.exception.InvalidFormDefinitionException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * Implementation of the {@link IFormBuilder} interface generating an XML form definition file * * @author Anthony Birembaut, Zhiheng Yang */ public class FormBuilderImpl implements IFormBuilder { /** * the product version constant */ public static final String PRODUCT_VERSION = "6.3"; /** * the product version */ protected String productVersion; /** * DOM representation of the XML file to create */ protected Document document; /** * the root element */ private Element rootElement; /** * The current element */ protected Element currentElement; /** * Logger */ protected static Logger LOGGER = Logger.getLogger(FormBuilderImpl.class.getName()); /** * XML tansformer factory */ protected TransformerFactory transformerFactory = TransformerFactory.newInstance(); /** * document buildeer factory */ protected DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); /** * Instance attribute */ private static FormBuilderImpl INSTANCE = null; private final Validator validator; /** * @return the FormExpressionsAPI instance */ public static synchronized FormBuilderImpl getInstance() { if (INSTANCE == null) { INSTANCE = new FormBuilderImpl(); } return INSTANCE; } /** * Private constructor to prevent instantiation * */ protected FormBuilderImpl() { productVersion = PRODUCT_VERSION; documentBuilderFactory.setValidating(true); // ignore white space can only be set if parser is validating documentBuilderFactory.setIgnoringElementContentWhitespace(true); // select xml schema as the schema language (a.o.t. DTD) documentBuilderFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema"); final URL xsdURL = getClass().getClassLoader().getResource("forms.xsd"); documentBuilderFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource", xsdURL.toExternalForm()); try { transformerFactory.setAttribute("indent-number", Integer.valueOf(2)); } catch (final Exception e) { // Nothing to do: indent-number is not supported } final SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); Schema schema; InputStream schemaStream = null; try { schemaStream = this.getClass().getResourceAsStream("/forms.xsd"); schema = factory.newSchema(new StreamSource(schemaStream)); } catch (final SAXException e) { throw new RuntimeException("unable to initialize the xsd validator form the forms", e); } finally { if (schemaStream != null) { try { schemaStream.close(); } catch (final IOException e) { LOGGER.log(Level.INFO, "Unable to close schema input stream", e); } } } validator = schema.newValidator(); } /** * Build a XML form definition file. * This is the last method to call once the form has been built. * It perform the XSD validation and generates the XML file * * @return a {@link File} * @throws InvalidFormDefinitionException * if the generated document is not valid * @throws IOException */ @Override public File done() throws IOException, InvalidFormDefinitionException { final File formsDefinitionFile = File.createTempFile("forms", ".xml", getTempFolder()); formsDefinitionFile.deleteOnExit(); document.appendChild(rootElement); final Source source = new DOMSource(document); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final Result resultat = new StreamResult(new OutputStreamWriter(outputStream, "UTF-8")); try { final Transformer transformer = transformerFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.transform(source, resultat); final byte[] xmlContent = outputStream.toByteArray(); //commented because studio might generate forms that are not complient with xsd but still working // validateAgainstFormsXSD(xmlContent); outputStream.close(); final FileOutputStream fileOutputStream = new FileOutputStream(formsDefinitionFile); try { fileOutputStream.write(xmlContent); fileOutputStream.flush(); } finally { fileOutputStream.close(); } } catch (final TransformerException e) { throw new InvalidFormDefinitionException("Error while generating the forms definition file.", e); } return formsDefinitionFile; } protected File getTempFolder() { return WebBonitaConstantsUtils.getInstance().getTempFolder(); } void validateAgainstFormsXSD(final byte[] xmlContent) throws InvalidFormDefinitionException { final String content = new String(xmlContent, Charset.forName("UTF-8")); try { final StreamSource source = new StreamSource(new StringReader(content)); validator.validate(source); } catch (final SAXException e) { throw new InvalidFormDefinitionException("Unable to parse the forms.xml using the forms.xsd", e); } catch (final IOException e) { throw new InvalidFormDefinitionException("Unable to read the forms.xml", e); } } /** * Initiate the form definition * * @return an implementation of {@link IFormBuilder} */ @Override public IFormBuilder createFormDefinition() { DocumentBuilder builder; try { builder = documentBuilderFactory.newDocumentBuilder(); document = builder.newDocument(); document.setXmlVersion("1.0"); rootElement = document.createElement(XMLForms.FORMS_DEFINITION); rootElement.setAttribute(XMLForms.PRODUCT_VERSION, productVersion); final Element migrationVersionElement = document.createElement(XMLForms.MIGRATION_PRODUCT_VERSION); migrationVersionElement.setTextContent(productVersion); rootElement.appendChild(migrationVersionElement); currentElement = rootElement; } catch (final ParserConfigurationException e) { LOGGER.log(Level.WARNING, "Invalid parser configuration", e); } return this; } @Override @Deprecated public IFormBuilder addMigrationProductVersion(final String migrationProductVersion) throws InvalidFormDefinitionException { final String[] migrationProductVersionParentsNames = { XMLForms.FORMS_DEFINITION }; try { peek(migrationProductVersionParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of migration product version property is only supported on elements of type " + Arrays.asList(migrationProductVersionParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("migration product version", migrationProductVersion); final Element migrationProductVersionElement = document.createElement(XMLForms.MIGRATION_PRODUCT_VERSION); migrationProductVersionElement.setTextContent(migrationProductVersion); push(migrationProductVersionElement); return this; } /** * Add an action on an application and create the list of actions if it doesn't exist yet * * @param actionType * the action type * @param variableName * the name of the variable (if it's a set variable action) * @param operator * the operator for the action * @param operatorInputType * the operator in put type (for java methods operations) * @param submitButtonId * the submit button associated with the action * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addAction(final ActionType actionType, final String variableName, final String variableType, final String operator, final String operatorInputType, final String submitButtonId) throws InvalidFormDefinitionException { final String[] actionsParentsNames = { XMLForms.PAGE }; try { peek(actionsParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of an action is only supported on elements of type " + Arrays.asList(actionsParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("action type", actionType); Element actionsElement = findChildElement(currentElement, XMLForms.ACTIONS); if (actionsElement == null) { actionsElement = document.createElement(XMLForms.ACTIONS); } push(actionsElement); final Element actionElement = document.createElement(XMLForms.ACTION); actionElement.setAttribute(XMLForms.TYPE, actionType.name()); addChild(actionElement, XMLForms.VARIABLE, variableName, false, true); addChild(actionElement, XMLForms.VARIABLE_TYPE, variableType, false, true); addChild(actionElement, XMLForms.OPERATOR, operator, false, true); addChild(actionElement, XMLForms.INPUT_TYPE, operatorInputType, false, true); addChild(actionElement, XMLForms.SUBMIT_BUTTON, submitButtonId, false, true); push(actionElement); return this; } /** * Add an expression on a form action * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addActionExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] labelParentsNames = { XMLForms.ACTION }; try { peek(labelParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of an action expression is only supported on elements of type " + Arrays.asList(labelParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add a condition expression on a form action * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addConditionExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] labelParentsNames = { XMLForms.ACTION }; try { peek(labelParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of an action expression is only supported on elements of type " + Arrays.asList(labelParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element conditionElement = document.createElement(XMLForms.CONDITION); push(conditionElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add available values array to a widget and create the array of available values if it doesn't exist yet * (for table widget). * * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addAvailableValuesArray() throws InvalidFormDefinitionException { final String[] availableValuesParentsNames = { XMLForms.WIDGET }; try { peek(availableValuesParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of an array of available values is only supported on elements of type " + Arrays.asList(availableValuesParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } Element availableValuesElement = findChildElement(currentElement, XMLForms.AVAILABLE_VALUES); if (availableValuesElement == null) { availableValuesElement = document.createElement(XMLForms.AVAILABLE_VALUES); } push(availableValuesElement); final Element availableValuesArrayElement = document.createElement(XMLForms.VALUES_ARRAY); push(availableValuesArrayElement); return this; } /** * Add initial values array to a widget and create the array if it doesn't exist yet * (for grid widget). * * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addInitialValuesArray() throws InvalidFormDefinitionException { final String[] availableValuesParentsNames = { XMLForms.WIDGET }; try { peek(availableValuesParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of an array of initial values is only supported on elements of type " + Arrays.asList(availableValuesParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } Element initialValueElement = findChildElement(currentElement, XMLForms.INITIAL_VALUE); if (initialValueElement == null) { initialValueElement = document.createElement(XMLForms.INITIAL_VALUE); } push(initialValueElement); final Element initilaValueArrayElement = document.createElement(XMLForms.EXPRESSION_ARRAY); push(initilaValueArrayElement); return this; } /** * Add a row to an available values array or to an initial value array * (for table widget and grid widget). * * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addRow() throws InvalidFormDefinitionException { final String[] availableValuesParentsNames = { XMLForms.VALUES_ARRAY, XMLForms.EXPRESSION_ARRAY }; try { peek(availableValuesParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a row available values is only supported on elements of type " + Arrays.asList(availableValuesParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element rowElement = document.createElement(XMLForms.ROW); push(rowElement); return this; } /** * Add an available value to a widget and create the list of available values if it doesn't exist yet * (for radiobutton group, simple and multiple selectbox, checkbox group, table row). * * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addAvailableValue() throws InvalidFormDefinitionException { final String[] availableValuesParentsNames = { XMLForms.WIDGET, XMLForms.ROW }; try { peek(availableValuesParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of an available value is only supported on elements of type " + Arrays.asList(availableValuesParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } if (!currentElement.getNodeName().equals(XMLForms.ROW)) { Element availableValuesElement = findChildElement(currentElement, XMLForms.AVAILABLE_VALUES); if (availableValuesElement == null) { availableValuesElement = document.createElement(XMLForms.AVAILABLE_VALUES); } push(availableValuesElement); Element availableValuesListElement = findChildElement(currentElement, XMLForms.VALUES_LIST); if (availableValuesListElement == null) { availableValuesListElement = document.createElement(XMLForms.VALUES_LIST); } push(availableValuesListElement); } final Element availableValueElement = document.createElement(XMLForms.AVAILABLE_VALUE); push(availableValueElement); return this; } /** * Add a display format pattern for the display value of date widgets * * @param displayFormat * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addDisplayFormat(final String displayFormat) throws InvalidFormDefinitionException { final String[] displayFormatParentsNames = { XMLForms.WIDGET }; try { peek(displayFormatParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a display format is only supported on elements of type " + Arrays.asList(displayFormatParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("display format", displayFormat); final Element displayFormatElement = document.createElement(XMLForms.DISPLAY_FORMAT); displayFormatElement.setTextContent(displayFormat); push(displayFormatElement); return this; } /** * Add an error template on an application * * @param templateUri * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addErrorTemplate(final String templateUri) throws InvalidFormDefinitionException { final String[] errorTemplateParentsNames = { XMLForms.APPLICATION }; try { peek(errorTemplateParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of an error template is only supported on elements of type " + Arrays.asList(errorTemplateParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("error template", templateUri); checkStringNotEmpty("error template", templateUri); final Element errorTemplateElement = document.createElement(XMLForms.ERROR_TEMPLATE); errorTemplateElement.setTextContent(templateUri); push(errorTemplateElement); return this; } /** * Add a confirmation layout on an application * * @param templateUri * the uri of the template * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addConfirmationLayout(final String templateUri) throws InvalidFormDefinitionException { final String[] confirmationTemplateParentsNames = { XMLForms.FORM }; try { peek(confirmationTemplateParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a confirmation layout is only supported on elements of type " + Arrays.asList(confirmationTemplateParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("confirmation layout", templateUri); checkStringNotEmpty("confirmation layout", templateUri); final Element confirmationTemplateElement = document.createElement(XMLForms.CONFIRMATION_LAYOUT); confirmationTemplateElement.setTextContent(templateUri); push(confirmationTemplateElement); return this; } /** * Add a confirmation message on an application * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addConfirmationMessageExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] confirmationMessageParentsNames = { XMLForms.FORM }; try { peek(confirmationMessageParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a confirmation message is only supported on elements of type " + Arrays.asList(confirmationMessageParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element confirmationMessageElement = document.createElement(XMLForms.CONFIRMATION_MESSAGE); push(confirmationMessageElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add CSS class names to the items of a radiobutton or checkbox group widget * * @param cssClasses * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addItemsStyle(final String cssClasses) throws InvalidFormDefinitionException { final String[] itemsStyleParentsNames = { XMLForms.WIDGET }; try { peek(itemsStyleParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of an items style element is only supported on elements of type " + Arrays.asList(itemsStyleParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("items style", cssClasses); final Element itemsStyleElement = document.createElement(XMLForms.ITEMS_STYLE); itemsStyleElement.setTextContent(cssClasses); push(itemsStyleElement); return this; } /** * Add a label on an application, page or widget * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addLabelExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] labelParentsNames = { XMLForms.WIDGET, XMLForms.WIDGETS_GROUP, XMLForms.PAGE, XMLForms.APPLICATION, XMLForms.VALIDATOR, XMLForms.SUB_TITLE, XMLForms.AVAILABLE_VALUE }; try { peek(labelParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a label is only supported on elements of type " + Arrays.asList(labelParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } Element labelElement = null; if (currentElement.getNodeName().equals(XMLForms.APPLICATION)) { labelElement = document.createElement(XMLForms.APPLICATION_LABEL); } else if (currentElement.getNodeName().equals(XMLForms.PAGE)) { labelElement = document.createElement(XMLForms.PAGE_LABEL); } else { labelElement = document.createElement(XMLForms.LABEL); } push(labelElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add a Value expression * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addValueExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] labelParentsNames = { XMLForms.AVAILABLE_VALUE }; try { peek(labelParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a value is only supported on elements of type " + Arrays.asList(labelParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element valueElement = document.createElement(XMLForms.VALUE); push(valueElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add CSS class names to a widget label * * @param cssClasses * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addLabelStyle(final String cssClasses) throws InvalidFormDefinitionException { final String[] labelStyleParentsNames = { XMLForms.WIDGET, XMLForms.WIDGETS_GROUP }; try { peek(labelStyleParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a label style is only supported on elements of type " + Arrays.asList(labelStyleParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("label style", cssClasses); final Element labelStyleElement = document.createElement(XMLForms.LABEL_STYLE); labelStyleElement.setTextContent(cssClasses); push(labelStyleElement); return this; } /** * Specify the position of a widget label * * @param labelPosition * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addLabelPosition(final ItemPosition labelPosition) throws InvalidFormDefinitionException { final String[] labelPositionParentsNames = { XMLForms.WIDGET, XMLForms.WIDGETS_GROUP }; try { peek(labelPositionParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a label position is only supported on elements of type " + Arrays.asList(labelPositionParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("label position", labelPosition); final Element labelPositionElement = document.createElement(XMLForms.LABEL_POSITION); labelPositionElement.setTextContent(labelPosition.name()); push(labelPositionElement); return this; } /** * Add a mandatory field label on an application * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addMandatoryLabelExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] mandatoryLabelParentsNames = { XMLForms.APPLICATION }; try { peek(mandatoryLabelParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a mandatory label is only supported on elements of type " + Arrays.asList(mandatoryLabelParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element mandatoryLabelElement = document.createElement(XMLForms.MANDATORY_LABEL); push(mandatoryLabelElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add a mandatory property to a widget * * @param isMandatory * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addMandatoryBehavior(final boolean isMandatory) throws InvalidFormDefinitionException { final String[] mandatoryBehaviorParentsNames = { XMLForms.WIDGET }; try { peek(mandatoryBehaviorParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a mandatory behaviour is only supported on elements of type " + Arrays.asList(mandatoryBehaviorParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("mandatory behavior", isMandatory); final Element mandatoryBehaviorElement = document.createElement(XMLForms.MANDATORY); mandatoryBehaviorElement.setTextContent(Boolean.toString(isMandatory)); push(mandatoryBehaviorElement); return this; } /** * Indicates that the button should be displayed as a label instead of an html button * * @param isLabelButton * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addLabelButtonBehavior(final boolean isLabelButton) throws InvalidFormDefinitionException { final String[] labelButtonBehaviorParentsNames = { XMLForms.WIDGET }; try { peek(labelButtonBehaviorParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a label button behaviour is only supported on elements of type " + Arrays.asList(labelButtonBehaviorParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("label button behavior", isLabelButton); final Element labelButtonBehaviorElement = document.createElement(XMLForms.LABEL_BUTTON); labelButtonBehaviorElement.setTextContent(Boolean.toString(isLabelButton)); push(labelButtonBehaviorElement); return this; } /** * Add a mandatory field label and symbol style (css class names) on an application * * @param mandatoryStyle * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addMandatoryStyle(final String mandatoryStyle) throws InvalidFormDefinitionException { final String[] mandatoryStyleParentsNames = { XMLForms.APPLICATION }; try { peek(mandatoryStyleParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a mandatory label style is only supported on elements of type " + Arrays.asList(mandatoryStyleParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("mandatory label style", mandatoryStyle); final Element mandatoryStyleElement = document.createElement(XMLForms.MANDATORY_STYLE); mandatoryStyleElement.setTextContent(mandatoryStyle); push(mandatoryStyleElement); return this; } /** * Add a mandatory field symbol expression on an application * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addMandatorySymbolExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] mandatorySymbolParentsNames = { XMLForms.APPLICATION }; try { peek(mandatorySymbolParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a mandatory symbol is only supported on elements of type " + Arrays.asList(mandatorySymbolParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element mandatorySymbolElement = document.createElement(XMLForms.MANDATORY_SYMBOL); push(mandatorySymbolElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add a max length number of characters property to a widget for textbox and textarea widgets * * @param maxHeight * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addMaxHeight(final int maxHeight) throws InvalidFormDefinitionException { final String[] maxHeigthParentsNames = { XMLForms.WIDGET }; try { peek(maxHeigthParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a max heigth is only supported on elements of type " + Arrays.asList(maxHeigthParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element maxHeigthElement = document.createElement(XMLForms.MAX_HEIGHT); maxHeigthElement.setTextContent(Integer.toString(maxHeight)); push(maxHeigthElement); return this; } /** * Add a max length number of characters property to a widget for textbox and textarea widgets * * @param maxLength * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addMaxLength(final int maxLength) throws InvalidFormDefinitionException { final String[] maxLengthParentsNames = { XMLForms.WIDGET }; try { peek(maxLengthParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a max length is only supported on elements of type " + Arrays.asList(maxLengthParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element maxLengthElement = document.createElement(XMLForms.MAX_LENGTH); maxLengthElement.setTextContent(Integer.toString(maxLength)); push(maxLengthElement); return this; } /** * Add a page in the edition page flows and create the form if it doesn't exist yet * * @param pageId * id of the page to add * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addPage(final String pageId) throws InvalidFormDefinitionException { final String[] pageParentsNames = { XMLForms.FORM }; try { peek(pageParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of an entry page is only supported on elements of type " + Arrays.asList(pageParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("page Id", pageId); checkStringNotEmpty("page Id", pageId); Element pagesElement = findChildElement(currentElement, XMLForms.PAGES); if (pagesElement == null) { pagesElement = document.createElement(XMLForms.PAGES); } push(pagesElement); final Element pageElement = document.createElement(XMLForms.PAGE); pageElement.setAttribute(XMLForms.ID, escapeSingleQuote(pageId)); push(pageElement); return this; } /** * Add an entry form on an application * If an application has no entry form, it means that it hasn't been defined, * and the form for the application will be automatically generated. * Whereas if it has an empty entry form, the application will be automatically instantiated. * * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addEntryForm(final String formId) throws InvalidFormDefinitionException { final String[] formParentsNames = { XMLForms.APPLICATION }; try { peek(formParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of an entry form is only supported on elements of type " + Arrays.asList(formParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("form Id", formId); checkStringNotEmpty("form Id", formId); Element entryFormsElement = findChildElement(currentElement, XMLForms.FORMS); if (entryFormsElement == null) { entryFormsElement = document.createElement(XMLForms.FORMS); } push(entryFormsElement); final Element entryFormElement = document.createElement(XMLForms.FORM); entryFormElement.setAttribute(XMLForms.ID, escapeSingleQuote(formId)); addChild(entryFormElement, XMLForms.FORM_TYPE, FormType.entry.name(), true, true); push(entryFormElement); return this; } /** * Add a view form on an application * If an application has no form, it means that it hasn't been defined, * and the form for the application will be automatically generated. * * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addViewForm(final String formId) throws InvalidFormDefinitionException { final String[] formParentsNames = { XMLForms.APPLICATION }; try { peek(formParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a view form is only supported on elements of type " + Arrays.asList(formParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("form Id", formId); checkStringNotEmpty("form Id", formId); Element viewFormsElement = findChildElement(currentElement, XMLForms.FORMS); if (viewFormsElement == null) { viewFormsElement = document.createElement(XMLForms.FORMS); } push(viewFormsElement); final Element viewFormElement = document.createElement(XMLForms.FORM); viewFormElement.setAttribute(XMLForms.ID, escapeSingleQuote(formId)); addChild(viewFormElement, XMLForms.FORM_TYPE, FormType.view.name(), true, true); push(viewFormElement); return this; } /** * Add an application * * @param applicationName * the name of the application * @param applicationVersion * the version of the application * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addApplication(final String applicationName, final String applicationVersion) throws InvalidFormDefinitionException { final String[] applicationParentsNames = { XMLForms.FORMS_DEFINITION }; try { peek(applicationParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a application is only supported on elements of type " + Arrays.asList(applicationParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("application name", applicationName); checkArgNotNull("application version", applicationVersion); final Element applicationElement = document.createElement(XMLForms.APPLICATION); applicationElement.setAttribute(XMLForms.NAME, applicationName); applicationElement.setAttribute(XMLForms.VERSION, applicationVersion); push(applicationElement); return this; } /** * Add CSS classes names to a widget * * @param cssClasses * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addStyle(final String cssClasses) throws InvalidFormDefinitionException { final String[] styleParentsNames = { XMLForms.WIDGET, XMLForms.WIDGETS_GROUP }; try { peek(styleParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a style is only supported on elements of type " + Arrays.asList(styleParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("style", cssClasses); final Element styleElement = document.createElement(XMLForms.STYLE); styleElement.setTextContent(cssClasses); push(styleElement); return this; } /** * Add a layout on an application or a page * * @param layoutUri * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addLayout(final String layoutUri) throws InvalidFormDefinitionException { final String[] layoutParentsNames = { XMLForms.APPLICATION, XMLForms.PAGE }; try { peek(layoutParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a style is only supported on elements of type " + Arrays.asList(layoutParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("layout", layoutUri); checkStringNotEmpty("layout", layoutUri); Element templateElement = null; if (currentElement.getNodeName().equals(XMLForms.APPLICATION)) { templateElement = document.createElement(XMLForms.APPLICATION_LAYOUT); } else { templateElement = document.createElement(XMLForms.PAGE_LAYOUT); } templateElement.setTextContent(layoutUri); push(templateElement); return this; } /** * Add a title (tooltip) to a widget field * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addTitleExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] titleParentsNames = { XMLForms.WIDGET, XMLForms.WIDGETS_GROUP }; try { peek(titleParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a title is only supported on elements of type " + Arrays.asList(titleParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element titleElement = document.createElement(XMLForms.TITLE); push(titleElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add a validator on a page or a widget and create the list of validators if it doesn't exist yet * * @param validatorId * the validator Id * @param className * the classname of the validator * @param cssClasses * the css classes for the error label * @param position * the position of the error label * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addValidator(final String validatorId, final String className, final String cssClasses, final ValidatorPosition position) throws InvalidFormDefinitionException { final String[] validatorsParentsNames = { XMLForms.PAGE, XMLForms.WIDGET }; try { peek(validatorsParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a validator is only supported on elements of type " + Arrays.asList(validatorsParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("validator id", validatorId); checkStringNotEmpty("validator id", validatorId); Element validatorsElement = null; if (currentElement.getNodeName().equals(XMLForms.PAGE)) { validatorsElement = findChildElement(currentElement, XMLForms.PAGE_VALIDATORS); if (validatorsElement == null) { validatorsElement = document.createElement(XMLForms.PAGE_VALIDATORS); } } else { validatorsElement = findChildElement(currentElement, XMLForms.VALIDATORS); if (validatorsElement == null) { validatorsElement = document.createElement(XMLForms.VALIDATORS); } } push(validatorsElement); final Element validatorElement = document.createElement(XMLForms.VALIDATOR); validatorElement.setAttribute(XMLForms.ID, validatorId); addChild(validatorElement, XMLForms.CLASSNAME, className, true, true); addChild(validatorElement, XMLForms.STYLE, cssClasses, false, false); if (position != null) { addChild(validatorElement, XMLForms.POSITION, position.name(), false, true); } push(validatorElement); return this; } /** * Add a parameter expression * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addParameterExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] titleParentsNames = { XMLForms.VALIDATOR }; try { peek(titleParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a parameter is only supported on elements of type " + Arrays.asList(titleParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element parameterElement = document.createElement(XMLForms.PARAMETER); push(parameterElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add a widget on pages * * @param widgetId * @param widgetType * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addWidget(final String widgetId, final WidgetType widgetType) throws InvalidFormDefinitionException { final String[] widgetParentsNames = { XMLForms.PAGE, XMLForms.WIDGETS_GROUP }; try { peek(widgetParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a widget is only supported on elements of type " + Arrays.asList(widgetParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("widget id", widgetId); checkStringNotEmpty("widget id", widgetId); checkArgNotNull("widget type", widgetType); Element widgetsElement = findChildElement(currentElement, XMLForms.WIDGETS); if (widgetsElement == null) { widgetsElement = document.createElement(XMLForms.WIDGETS); } push(widgetsElement); final Element widgetElement = document.createElement(XMLForms.WIDGET); widgetElement.setAttribute(XMLForms.ID, widgetId); widgetElement.setAttribute(XMLForms.TYPE, widgetType.name()); push(widgetElement); return this; } /** * Add an attachement image behavior for the display of image previews on file download widgets or the display of attachments in image widgets * * @param attachmentImage * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addAttachmentImageBehavior(final boolean attachmentImage) throws InvalidFormDefinitionException { final String[] imagePreviewParentsNames = { XMLForms.WIDGET, XMLForms.WIDGETS_GROUP }; try { peek(imagePreviewParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a image preview behavior is only supported on elements of type " + Arrays.asList(imagePreviewParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("image preview", attachmentImage); final Element attachmentImageElement = document.createElement(XMLForms.DISPLAY_ATTACHMENT_IMAGE); attachmentImageElement.setTextContent(Boolean.toString(attachmentImage)); push(attachmentImageElement); return this; } /** * Add allow HTML in field behavior * * @param allowHTMLInField * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addAllowHTMLInFieldBehavior(final boolean allowHTMLInField) throws InvalidFormDefinitionException { final String[] allowHTMLInFiedParentsNames = { XMLForms.WIDGET }; try { peek(allowHTMLInFiedParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a property to allow or not HTML in form fields is only supported on elements of type " + Arrays.asList(allowHTMLInFiedParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("allow HTML in field", allowHTMLInField); final Element allowHTMLInFieldElement = document.createElement(XMLForms.ALLOW_HTML_IN_FIELD); allowHTMLInFieldElement.setTextContent(Boolean.toString(allowHTMLInField)); push(allowHTMLInFieldElement); return this; } /** * Add allow HTML in label behavior * * @param allowHTMLInLabel * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addAllowHTMLInLabelBehavior(final boolean allowHTMLInLabel) throws InvalidFormDefinitionException { final String[] allowHTMLInLabelParentsNames = { XMLForms.WIDGET, XMLForms.WIDGETS_GROUP, XMLForms.PAGE }; try { peek(allowHTMLInLabelParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a property to allow or not HTML in form labels is only supported on elements of type " + Arrays.asList(allowHTMLInLabelParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("allow HTML in label", allowHTMLInLabel); final Element allowHTMLInLabelElement = document.createElement(XMLForms.ALLOW_HTML_IN_LABEL); allowHTMLInLabelElement.setTextContent(Boolean.toString(allowHTMLInLabel)); push(allowHTMLInLabelElement); return this; } /** * Add a Html attribute to a widget * * @param name * the name of the attribute * @param value * the valueof the attribute * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addHTMLAttribute(final String name, final String value) throws InvalidFormDefinitionException { final String[] htmlAttributesParentsNames = { XMLForms.WIDGET }; try { peek(htmlAttributesParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of an HTML attribute is only supported on elements of type " + Arrays.asList(htmlAttributesParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("HTML attribute name", name); checkArgNotNull("HTML attribute name value", value); Element htmlAttributesElement = findChildElement(currentElement, XMLForms.HTML_ATTRIBUTES); if (htmlAttributesElement == null) { htmlAttributesElement = document.createElement(XMLForms.HTML_ATTRIBUTES); } push(htmlAttributesElement); final Element htmlAttributeElement = document.createElement(XMLForms.HTML_ATTRIBUTE); htmlAttributeElement.setAttribute(XMLForms.NAME, name); htmlAttributeElement.setTextContent(value); push(htmlAttributeElement); return this; } /** * Add table style for table widgets * * @param cssClasses * the CSS classes for the table * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addTableStyle(final String cssClasses) throws InvalidFormDefinitionException { final String[] styleParentsNames = { XMLForms.WIDGET }; try { peek(styleParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a table style is only supported on elements of type " + Arrays.asList(styleParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("table style", cssClasses); final Element styleElement = document.createElement(XMLForms.TABLE_STYLE); styleElement.setTextContent(cssClasses); push(styleElement); return this; } /** * Add image style for image widgets * * @param cssClasses * the CSS classes for the image * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addImageStyle(final String cssClasses) throws InvalidFormDefinitionException { final String[] styleParentsNames = { XMLForms.WIDGET }; try { peek(styleParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of an image style is only supported on elements of type " + Arrays.asList(styleParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("image style", cssClasses); final Element styleElement = document.createElement(XMLForms.IMAGE_STYLE); styleElement.setTextContent(cssClasses); push(styleElement); return this; } /** * Add cells style for table widgets * * @param cssClasses * the CSS classes for the cells * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addCellsStyle(final String cssClasses) throws InvalidFormDefinitionException { final String[] styleParentsNames = { XMLForms.WIDGET }; try { peek(styleParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a cells style is only supported on elements of type " + Arrays.asList(styleParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("cells style", cssClasses); final Element styleElement = document.createElement(XMLForms.CELL_STYLE); styleElement.setTextContent(cssClasses); push(styleElement); return this; } /** * Add headings style for table widgets * * @param cssClasses * the CSS classes for the headings * @param leftHeadings * if true, indicates that the left column of the grid should be considered as a header * @param topHeadings * if true, indicates that the top row of the grid should be considered as a header * @param rightHeadings * if true, indicates that the right column of the grid should be considered as a header * @param bottomHeadings * if true, indicates that the bottom row of the grid should be considered as a header * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addHeadingsStyle(final String cssClasses, final boolean leftHeadings, final boolean topHeadings, final boolean rightHeadings, final boolean bottomHeadings) throws InvalidFormDefinitionException { final String[] styleParentsNames = { XMLForms.WIDGET }; try { peek(styleParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a headingss style is only supported on elements of type " + Arrays.asList(styleParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("headings style", cssClasses); final Element styleElement = document.createElement(XMLForms.HEADINGS_STYLE); styleElement.setTextContent(cssClasses); push(styleElement); peek(styleParentsNames); final Element headingsPositionsElement = document.createElement(XMLForms.HEADINGS_POSITIONS); addChild(headingsPositionsElement, XMLForms.LEFT_HEADINGS, Boolean.toString(leftHeadings), true, true); addChild(headingsPositionsElement, XMLForms.TOP_HEADINGS, Boolean.toString(topHeadings), true, true); addChild(headingsPositionsElement, XMLForms.RIGHT_HEADINGS, Boolean.toString(rightHeadings), true, true); addChild(headingsPositionsElement, XMLForms.BOTTOM_HEADINGS, Boolean.toString(bottomHeadings), true, true); push(headingsPositionsElement); return this; } /** * Add a readonly property to a widget * * @param isReadOnly * the readonly behavior * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addReadOnlyBehavior(final boolean isReadOnly) throws InvalidFormDefinitionException { final String[] readOnlyBehaviorParentsNames = { XMLForms.WIDGET }; try { peek(readOnlyBehaviorParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a read-only behaviour is only supported on elements of type " + Arrays.asList(readOnlyBehaviorParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("read-only behavior", isReadOnly); final Element mandatoryBehaviorElement = document.createElement(XMLForms.READ_ONLY); mandatoryBehaviorElement.setTextContent(Boolean.toString(isReadOnly)); push(mandatoryBehaviorElement); return this; } /** * add a maximum number of columns to a widget (for editable tables) * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addMaxColumnsExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] maxColumnsParentsNames = { XMLForms.WIDGET }; try { peek(maxColumnsParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a max columns property is only supported on elements of type " + Arrays.asList(maxColumnsParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element maxColumnsElement = document.createElement(XMLForms.MAX_COLUMNS); push(maxColumnsElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * add a maximum number of rows to a widget (for tables) * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addMaxRowsExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] maxRowsParentsNames = { XMLForms.WIDGET }; try { peek(maxRowsParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a max rows property is only supported on elements of type " + Arrays.asList(maxRowsParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element maxRowsElement = document.createElement(XMLForms.MAX_ROWS); push(maxRowsElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * add a minimum number of columns to a widget (for editable tables) * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addMinColumnsExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] minColumnsParentsNames = { XMLForms.WIDGET }; try { peek(minColumnsParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a min columns property is only supported on elements of type " + Arrays.asList(minColumnsParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element minColumnsElement = document.createElement(XMLForms.MIN_COLUMNS); push(minColumnsElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * add a minimum number of rows to a widget (for editable tables) * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addMinRowsExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] minRowsParentsNames = { XMLForms.WIDGET }; try { peek(minRowsParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a min rows property is only supported on elements of type " + Arrays.asList(minRowsParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element minRowsElement = document.createElement(XMLForms.MIN_ROWS); push(minRowsElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * add a variable columns number behavior (for editable tables) * * @param variableColumnsNumber * the variable columns number * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addVariableColumnsNumber(final boolean variableColumnsNumber) throws InvalidFormDefinitionException { final String[] variableColumnsNumberParentsNames = { XMLForms.WIDGET }; try { peek(variableColumnsNumberParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a variable columns number behaviour is only supported on elements of type " + Arrays.asList(variableColumnsNumberParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("variable columns number behaviour", variableColumnsNumber); final Element variableColumnsNumberElement = document.createElement(XMLForms.VARIABLE_COLUMNS); variableColumnsNumberElement.setTextContent(Boolean.toString(variableColumnsNumber)); push(variableColumnsNumberElement); return this; } /** * add a variable rows number behavior (for editable tables) * * @param variableRowsNumber * the variable rows number * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addVariableRowsNumber(final boolean variableRowsNumber) throws InvalidFormDefinitionException { final String[] variableRowsNumberParentsNames = { XMLForms.WIDGET }; try { peek(variableRowsNumberParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a variable rows number behaviour is only supported on elements of type " + Arrays.asList(variableRowsNumberParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("variable rows number behaviour", variableRowsNumber); final Element variableRowsNumberElement = document.createElement(XMLForms.VARIABLE_ROWS); variableRowsNumberElement.setTextContent(Boolean.toString(variableRowsNumber)); push(variableRowsNumberElement); return this; } /** * specify the index of column which is used as the value of the selected row(s) * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addValueColumnIndexExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] valueColumnIndexParentsNames = { XMLForms.WIDGET }; try { peek(valueColumnIndexParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a value column index property is only supported on elements of type " + Arrays.asList(valueColumnIndexParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element valueColumnIndexElement = document.createElement(XMLForms.VALUE_COLUMN_INDEX); push(valueColumnIndexElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * @param selectMode */ @Override public IFormBuilder addSelectMode(final SelectMode selectMode) throws InvalidFormDefinitionException { final String[] styleParentsNames = { XMLForms.WIDGET }; try { peek(styleParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a select mode is only supported on elements of type " + Arrays.asList(styleParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("select mode", selectMode); final Element selectModeElement = document.createElement(XMLForms.SELECT_MODE); selectModeElement.setTextContent(selectMode.name()); push(selectModeElement); return this; } /** * @param selectedItemsStyle */ @Override public IFormBuilder addSelectedItemsStyle(final String selectedItemsStyle) throws InvalidFormDefinitionException { final String[] styleParentsNames = { XMLForms.WIDGET }; try { peek(styleParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a selected items style is only supported on elements of type " + Arrays.asList(styleParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("selected items style", selectedItemsStyle); final Element styleElement = document.createElement(XMLForms.SELECTED_ITEMS_STYLE); styleElement.setTextContent(selectedItemsStyle); push(styleElement); return this; } /** * @param maxItems */ @Override public IFormBuilder addMaxItems(final int maxItems) throws InvalidFormDefinitionException { final String[] maxItemsParentsNames = { XMLForms.WIDGET }; try { peek(maxItemsParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a max items property is only supported on elements of type " + Arrays.asList(maxItemsParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element maxItemsElement = document.createElement(XMLForms.MAX_ITEMS); maxItemsElement.setTextContent(Integer.toString(maxItems)); push(maxItemsElement); return this; } /** * Add a transient data on a page flow * * @param name * name of the transient data * @param className * classnameof the transient data * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addTransientData(final String name, final String className) throws InvalidFormDefinitionException { final String[] pageflowParentsNames = { XMLForms.FORM }; try { peek(pageflowParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a transient data is only supported on elements of type " + Arrays.asList(pageflowParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("data name", name); checkStringNotEmpty("data name", name); Element transientDataElement = findChildElement(currentElement, XMLForms.TRANSIENT_DATA); if (transientDataElement == null) { transientDataElement = document.createElement(XMLForms.TRANSIENT_DATA); } push(transientDataElement); final Element dataElement = document.createElement(XMLForms.DATA); dataElement.setAttribute(XMLForms.NAME, name); addChild(dataElement, XMLForms.CLASSNAME, className, true, true); push(dataElement); return this; } /** * Add a transient data expression * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addTransientDataExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] firstPageParentsNames = { XMLForms.DATA }; try { peek(firstPageParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a transient data expression element is only supported on elements of type " + Arrays.asList(firstPageParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element firstPageElement = document.createElement(XMLForms.VALUE); push(firstPageElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add a first page Id expression * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addFirstPageIdExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] firstPageParentsNames = { XMLForms.FORM }; try { peek(firstPageParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a first page element is only supported on elements of type " + Arrays.asList(firstPageParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element firstPageElement = document.createElement(XMLForms.FIRST_PAGE); push(firstPageElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add a next page id on a page flow * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addNextPageIdExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] firstPageParentsNames = { XMLForms.PAGE }; try { peek(firstPageParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a next page attribute is only supported on elements of type " + Arrays.asList(firstPageParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element nextPageElement = document.createElement(XMLForms.NEXT_PAGE); push(nextPageElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add a delay millisecond property to a widget for asynchronous suggestbox widgets * * @param delayMillis * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addDelayMillis(final int delayMillis) throws InvalidFormDefinitionException { final String[] delayMillisParentsNames = { XMLForms.WIDGET }; try { peek(delayMillisParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a delay millis property is only supported on elements of type " + Arrays.asList(delayMillisParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element delayMillisElement = document.createElement(XMLForms.DELAY_MILLIS); delayMillisElement.setTextContent(Integer.toString(delayMillis)); push(delayMillisElement); return this; } /** * Add a sub title property to a widget to accept an "example" parameter * * @param position * sub title position * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addSubTitle(final SubTitlePosition position) throws InvalidFormDefinitionException { final String[] subTitleParentsNames = { XMLForms.WIDGET }; try { peek(subTitleParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a sub title property is only supported on elements of type " + Arrays.asList(subTitleParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element subTitleElement = document.createElement(XMLForms.SUB_TITLE); if (position != null) { addChild(subTitleElement, XMLForms.POSITION, position.name(), false, true); } push(subTitleElement); return this; } /** * Add a popup tooltip, that will be displayed to help the user when he clicks on the * bulb icon that is placed beside of a widget. * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder addPopupToolTipExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] popupToolTipParentsNames = { XMLForms.WIDGET }; try { peek(popupToolTipParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a pupup tooltip property is only supported on elements of type " + Arrays.asList(popupToolTipParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element popupToolTipElement = document.createElement(XMLForms.POPUP_TOOLTIP); push(popupToolTipElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * add permissions * * @param permissions */ @Override public IFormBuilder addPermissions(final String permissions) throws InvalidFormDefinitionException { final String[] permissionsParentsNames = { XMLForms.APPLICATION, XMLForms.FORM }; try { peek(permissionsParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of permissions property is only supported on elements of type " + Arrays.asList(permissionsParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element permissionsElement = document.createElement(XMLForms.PERMISSIONS); permissionsElement.setTextContent(permissions); push(permissionsElement); return this; } /** * add next form id * * @param name * the name of the expression * @param content * the real script code of the expression * @param expressionType * @param returnType * @param interpreter */ @Override public IFormBuilder addNextFormIdExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] nextFormParentsNames = { XMLForms.FORM }; try { peek(nextFormParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a next form attribute is only supported on elements of type " + Arrays.asList(nextFormParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element nextPageElement = document.createElement(XMLForms.NEXT_FORM); push(nextPageElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add an expression to the current element, as its the dependent expressions * * @param name * the name of the expression * @param content * the real script code of the expression * @param expressionType * @param returnType * @param interpreter */ @Override public IFormBuilder addDependentExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { return addDependentExpression(name, content, expressionType, returnType, interpreter, true); } /** * Add an expression to the current element, as its the dependent expressions * * @param name * the name of the expression * @param content * the real script code of the expression * @param expressionType * @param returnType * @param interpreter * @param isSameLevelDependency */ @Override public IFormBuilder addDependentExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter, final boolean isSameLevelDependency) throws InvalidFormDefinitionException { if (isSameLevelDependency) { final String[] expressionElementNames = { XMLForms.EXPRESSION }; try { peekParentFirst(expressionElementNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The method addDependentExpression can only be called once an expression has been created."; if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } } Element dependenciesElement = findChildElement(currentElement, XMLForms.DEPENDENCIES); if (dependenciesElement == null) { dependenciesElement = document.createElement(XMLForms.DEPENDENCIES); push(dependenciesElement); } else { currentElement = dependenciesElement; } addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * End an expression dependencies group * * @return an implementation of {@link IFormBuilder} * @throws InvalidFormDefinitionException */ @Override public IFormBuilder endExpressionDependencies() throws InvalidFormDefinitionException { final String[] expressionElementNames = { XMLForms.EXPRESSION }; try { currentElement = (Element) currentElement.getParentNode(); peekParentFirst(expressionElementNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The method endExpressionDependencies can only be called once an expression dependency has been created."; if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } return this; } /** * Add an expression to the current element * * @param name * @param content * @param expressionType * @param returnType * @param interpreter * @throws InvalidFormDefinitionException */ protected IFormBuilder addExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final Element expressionElement = document.createElement(XMLForms.EXPRESSION); addChild(expressionElement, XMLForms.NAME, name, false, true); addChild(expressionElement, XMLForms.EXPRESSION_CONTENT, content, true, false); addChild(expressionElement, XMLForms.EXPRESSION_TYPE, expressionType, true, true); addChild(expressionElement, XMLForms.EXPRESSION_RETURN_TYPE, returnType, true, true); addChild(expressionElement, XMLForms.EXPRESSION_INTERPRETER, interpreter, false, true); push(expressionElement); return this; } /** * Add initial value * * @param name * @param content * @param expressionType * @param returnType * @param interpreter */ @Override public IFormBuilder addInitialValueExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] initialValueParentsNames = { XMLForms.WIDGET, XMLForms.WIDGETS_GROUP, XMLForms.ROW, XMLForms.TRANSIENT_DATA }; try { peek(initialValueParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of an initial value is only supported on elements of type " + Arrays.asList(initialValueParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } if (!currentElement.getNodeName().equals(XMLForms.ROW)) { Element initialValueElement = findChildElement(currentElement, XMLForms.INITIAL_VALUE); if (initialValueElement == null) { initialValueElement = document.createElement(XMLForms.INITIAL_VALUE); } else { throw new InvalidFormDefinitionException("This element already has an initial value defined."); } push(initialValueElement); } addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add initial value Resource for file widgets on instantiation form * * @param resourcePath */ @Override public IFormBuilder addInitialValueResource(final String resourcePath) throws InvalidFormDefinitionException { final String[] initialValueParentsNames = { XMLForms.WIDGET }; try { peek(initialValueParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a resource initial value is only supported on elements of type " + Arrays.asList(initialValueParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull(XMLForms.PATH, resourcePath); checkStringNotEmpty(XMLForms.PATH, resourcePath); Element initialValueElement = findChildElement(currentElement, XMLForms.INITIAL_VALUE); if (initialValueElement == null) { initialValueElement = document.createElement(XMLForms.INITIAL_VALUE); } else { throw new InvalidFormDefinitionException("This element already has an initial value defined."); } push(initialValueElement); final Element resourceElement = document.createElement(XMLForms.RESOURCE); initialValueElement.appendChild(resourceElement); final Element element = document.createElement(XMLForms.PATH); element.setTextContent(resourcePath); resourceElement.appendChild(element); return this; } /** * Add initial values * * @param name * @param content * @param expressionType * @param returnType * @param interpreter */ @Override public IFormBuilder addAvailableValuesExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] availableValuesExpressionParentsNames = { XMLForms.WIDGET }; try { peek(availableValuesExpressionParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of an available values expression is only supported on elements of type " + Arrays.asList(availableValuesExpressionParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } Element availableValuesElement = findChildElement(currentElement, XMLForms.AVAILABLE_VALUES); if (availableValuesElement == null) { availableValuesElement = document.createElement(XMLForms.AVAILABLE_VALUES); } push(availableValuesElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add vertical header expression list */ @Override public IFormBuilder addVerticalHeaderExpressionList() throws InvalidFormDefinitionException { final String[] verticalHeaderParentsNames = { XMLForms.WIDGET }; try { peek(verticalHeaderParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a vertical header is only supported on elements of type " + Arrays.asList(verticalHeaderParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element verticalHeaderElement = document.createElement(XMLForms.VERTICAL_HEADER); push(verticalHeaderElement); final Element rowElement = document.createElement(XMLForms.EXPRESSION_LIST); push(rowElement); return this; } /** * Add vertical header * * @param name * @param content * @param expressionType * @param returnType * @param interpreter */ @Override public IFormBuilder addVerticalHeaderExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] verticalHeaderParentsNames = { XMLForms.WIDGET, XMLForms.EXPRESSION_LIST }; try { peek(verticalHeaderParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a vertical header is only supported on elements of type " + Arrays.asList(verticalHeaderParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } if (currentElement.getNodeName().equals(XMLForms.EXPRESSION_LIST)) { addExpression(name, content, expressionType, returnType, interpreter); } else { final Element verticalHeaderElement = document.createElement(XMLForms.VERTICAL_HEADER); push(verticalHeaderElement); addExpression(name, content, expressionType, returnType, interpreter); } return this; } /** * Add horizontal header */ @Override public IFormBuilder addHorizontalHeaderExpressionList() throws InvalidFormDefinitionException { final String[] horizontalHeaderParentsNames = { XMLForms.WIDGET }; try { peek(horizontalHeaderParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a horizontal header is only supported on elements of type " + Arrays.asList(horizontalHeaderParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element horizontalHeaderElement = document.createElement(XMLForms.HORIZONTAL_HEADER); push(horizontalHeaderElement); final Element rowElement = document.createElement(XMLForms.EXPRESSION_LIST); push(rowElement); return this; } /** * Add horizontal header * * @param name * @param content * @param expressionType * @param returnType * @param interpreter */ @Override public IFormBuilder addHorizontalHeaderExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] horizontalHeaderParentsNames = { XMLForms.WIDGET, XMLForms.EXPRESSION_LIST }; try { peek(horizontalHeaderParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a horizontal header is only supported on elements of type " + Arrays.asList(horizontalHeaderParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } if (currentElement.getNodeName().equals(XMLForms.EXPRESSION_LIST)) { addExpression(name, content, expressionType, returnType, interpreter); } else { final Element horizontalHeaderElement = document.createElement(XMLForms.HORIZONTAL_HEADER); push(horizontalHeaderElement); addExpression(name, content, expressionType, returnType, interpreter); } return this; } /** * Add display condition * * @param name * @param content * @param expressionType * @param returnType * @param interpreter */ @Override public IFormBuilder addDisplayConditionExpression(final String name, final String content, final String expressionType, final String returnType, final String interpreter) throws InvalidFormDefinitionException { final String[] displayConditionParentsNames = { XMLForms.WIDGET, XMLForms.WIDGETS_GROUP }; try { peek(displayConditionParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a display condition is only supported on elements of type " + Arrays.asList(displayConditionParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element displayConditionElement = document.createElement(XMLForms.DISPLAY_CONDITION); push(displayConditionElement); addExpression(name, content, expressionType, returnType, interpreter); return this; } /** * Add an input type to a file widget * * @param fileWidgetInputType */ @Override public IFormBuilder addFileWidgetInputType(final FileWidgetInputType fileWidgetInputType) throws InvalidFormDefinitionException { final String[] displayConditionParentsNames = { XMLForms.WIDGET }; try { peek(displayConditionParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of an input type on a file widget is only supported on elements of type " + Arrays.asList(displayConditionParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element fileInputTypeElement = document.createElement(XMLForms.FILE_INPUT_TYPE); fileInputTypeElement.setTextContent(fileWidgetInputType.name()); currentElement.appendChild(fileInputTypeElement); return this; } /** * {@inheritDoc} */ @Override public IFormBuilder addFieldOutputType(final String fieldOutputType) throws InvalidFormDefinitionException { final String[] displayConditionParentsNames = { XMLForms.WIDGET }; try { peek(displayConditionParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a field output type on a file widget is only supported on elements of type " + Arrays.asList(displayConditionParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } final Element fieldOutputTypeElement = document.createElement(XMLForms.FIELD_OUTPUT_TYPE); fieldOutputTypeElement.setTextContent(fieldOutputType); currentElement.appendChild(fieldOutputTypeElement); return this; } /** * {@inheritDoc} */ @Override public IFormBuilder addInputStyle(final String cssClasses) throws InvalidFormDefinitionException { final String[] styleParentsNames = { XMLForms.WIDGET, XMLForms.WIDGETS_GROUP }; try { peek(styleParentsNames); } catch (final InvalidFormDefinitionException e) { final String errorMessage = "The addition of a style is only supported on elements of type " + Arrays.asList(styleParentsNames); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage, e); } throw new InvalidFormDefinitionException(errorMessage, e); } checkArgNotNull("input style", cssClasses); final Element styleElement = document.createElement(XMLForms.INPUT_STYLE); styleElement.setTextContent(cssClasses); push(styleElement); return this; } protected String escapeSingleQuote(final String str) { return str.replaceAll("'", XMLForms.SINGLE_QUOTE_ESCAPE); } /** * Add a child element * * @param parentElement * @param childName * @param childValue * @param isMandatory * @throws InvalidFormDefinitionException */ protected void addChild(final Element parentElement, final String childName, final String childValue, final boolean isMandatory, final boolean isEmptyForbidden) throws InvalidFormDefinitionException { if (isMandatory) { checkArgNotNull(childName, childValue); } if (isEmptyForbidden) { checkStringNotEmpty(childName, childValue); } if (childValue != null) { final Element element = document.createElement(childName); element.setTextContent(childValue); parentElement.appendChild(element); } } /** * Find the first element with the given tag name among an element children * * @param parent * the parent element * @param childName * the tag name * @return an {@link Element} or null if there are no elements with thegiven tag name among the element's children */ protected Element findChildElement(final Element parent, final String childName) { final NodeList nodeList = parent.getElementsByTagName(childName); for (int i = 0; i < nodeList.getLength(); i++) { final Element element = (Element) nodeList.item(i); if (element.getParentNode().isSameNode(parent)) { return element; } } return null; } /** * Add an element to the stack * * @param element */ protected void push(final Element element) { if (element.getParentNode() == null) { currentElement.appendChild(element); } currentElement = element; } /** * Retrieve the first element in the DOM whose type is among the element types provided * * @param elementTypes * array of required element types * @return the first {@link Element} in the stack whose type is among the element types provided * @throws InvalidFormDefinitionException * if no element among the current element's parents has one of the required type */ protected Element peek(final String[] elementTypes) throws InvalidFormDefinitionException { Element element = currentElement; final List<String> elementTypesList = Arrays.asList(elementTypes); while (element.getParentNode() != null && element.getParentNode().getNodeType() == Node.ELEMENT_NODE) { if (elementTypesList.contains(element.getNodeName())) { currentElement = element; return currentElement; } element = (Element) element.getParentNode(); } if (elementTypesList.contains(element.getNodeName())) { currentElement = element; return currentElement; } else { throw new InvalidFormDefinitionException("No required element present among the parents of the current element."); } } /** * Retrieve the first element in the DOM whose type is among the element types provided * * @param elementTypes * array of required element types * @return the first {@link Element} in the stack whose type is among the element types provided * @throws InvalidFormDefinitionException * if no element among the current element's parents has one of the required type */ protected Element peekParentFirst(final String[] elementTypes) throws InvalidFormDefinitionException { Element element = currentElement; final List<String> elementTypesList = Arrays.asList(elementTypes); while (element.getParentNode() != null && element.getParentNode().getNodeType() == Node.ELEMENT_NODE) { element = (Element) element.getParentNode(); if (elementTypesList.contains(element.getNodeName())) { currentElement = element; return currentElement; } } if (elementTypesList.contains(currentElement.getNodeName())) { return currentElement; } else { throw new InvalidFormDefinitionException("No required element present among the parents of the current element."); } } /** * Verify that an element/attribute value is not null * * @param name * @param value * @throws InvalidFormDefinitionException */ protected void checkArgNotNull(final String name, final Object value) throws InvalidFormDefinitionException { if (value == null) { final String errorMessage = "The property " + name + " shouldn't be null."; if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage); } throw new InvalidFormDefinitionException(errorMessage); } } /** * Verify that an element/attribute value is not an empty string * * @param name * @param value * @throws InvalidFormDefinitionException */ protected void checkStringNotEmpty(final String name, final String value) throws InvalidFormDefinitionException { if (value != null && value.length() == 0) { final String errorMessage = "The property " + name + " shouldn't be empty."; if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, errorMessage); } throw new InvalidFormDefinitionException(errorMessage); } } }