Java tutorial
/* Copyright (C) 2007-2011 BlueXML - www.bluexml.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * */ package com.bluexml.xforms.controller.mapping; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import com.bluexml.side.form.utils.DOMUtil; import com.bluexml.xforms.controller.alfresco.AlfrescoController; import com.bluexml.xforms.controller.binding.AspectType; import com.bluexml.xforms.controller.binding.AttributeType; import com.bluexml.xforms.controller.binding.ClassType; import com.bluexml.xforms.controller.binding.Mapping; import com.bluexml.xforms.controller.binding.SearchFieldType; import com.bluexml.xforms.controller.binding.SearchFormType; import com.bluexml.xforms.messages.MsgId; /** * Provides the XForms instance document for a search form and deals with the * instance once it is * filled by the XForms engine. * * @author Amenel */ public class MappingToolSearch extends MappingToolCommon { /** The logger. */ protected static Log logger = LogFactory.getLog(MappingToolSearch.class); /** * @param mapping * @param controller */ public MappingToolSearch(Mapping mapping, AlfrescoController controller) { super(mapping, controller); } /** * Builds the XForms instance document for a search form. * * @param formName * @return */ public Document getInstanceSearch(String formName) { Document formInstance = documentBuilder.newDocument(); Element rootElement = formInstance.createElement("root"); formInstance.appendChild(rootElement); SearchFormType sfType = getSearchFormType(formName); if (sfType != null) { Element formElt = formInstance.createElement(sfType.getName()); rootElement.appendChild(formElt); for (SearchFieldType fieldType : sfType.getField()) { Element topElt = getSearchFieldNode(formInstance, fieldType); formElt.appendChild(topElt); } } return formInstance; } /** * Returns the full class definition (with attributes) for the given search * form's real class. * * @param sfType * @return */ private ClassType resolveClassType(SearchFormType sfType) { String realClassName = sfType.getRealClass().getPackage() + "." + sfType.getRealClass().getName(); ClassType realClassType = getClassType(realClassName); return realClassType; } /** * Creates and initializes the DOM section for a field. * * @param doc * @param fieldType * @return */ private Element getSearchFieldNode(Document doc, SearchFieldType fieldType) { String fieldName = fieldType.getName(); Element fieldElt = doc.createElement(fieldName); Element opElt = doc.createElement(MsgId.INT_INSTANCE_SEARCH_OPCODE.getText()); fieldElt.appendChild(opElt); opElt.setTextContent(fieldType.getPick()); // set the default operator String dataType = fieldType.getType(); boolean isTyped = (StringUtils.trimToNull(dataType) != null); if (isTyped && dataType.equalsIgnoreCase("datetime")) { dataType = "Date"; } if (hasSingleInput(fieldType) == true) { Element valueElt = doc.createElement(MsgId.INT_INSTANCE_SEARCH_VALUE.getText()); if (isTyped) { valueElt.setTextContent(createXFormsInitialValue(dataType, null, null, null)); } fieldElt.appendChild(valueElt); } else { // when more than two inputs become supported, test for the number of inputs Element valueLoElt = doc.createElement(MsgId.INT_INSTANCE_SEARCH_VALUE_LO.getText()); if (isTyped) { valueLoElt.setTextContent(createXFormsInitialValue(dataType, null, null, null)); } fieldElt.appendChild(valueLoElt); Element valueHiElt = doc.createElement(MsgId.INT_INSTANCE_SEARCH_VALUE_HI.getText()); if (isTyped) { valueHiElt.setTextContent(createXFormsInitialValue(dataType, null, null, null)); } fieldElt.appendChild(valueHiElt); } return fieldElt; } /** * Builds a JSON string for the search options provided by the XForms engine * in the node. * * @param formName * @param instance * @return * @throws ServletException */ public String transformSearchForm(String formName, Node instance, boolean shortPropertyNames, Map<String, String> initParams) throws ServletException { SearchFormType sfType = getSearchFormType(formName); if (sfType == null) { if (logger.isErrorEnabled()) { logger.error("No search form found with id '" + formName + "'"); } throw new ServletException("The search form '" + formName + "' was not found in the mapping.xml file."); } Element rootElt = getRootElement(formName, instance); StringBuffer buf = new StringBuffer(1024); String propName = ""; String typeName = ""; ClassType classType = resolveClassType(sfType); String packageName = classType.getPackage(); String namespaceClassType = MappingToolCommon.getNameSpaceFromPrefix(getRootPackage(packageName)); buf.append("{"); // open JSON string // the title buf.append("type:\""); typeName = "{" + namespaceClassType + "}" + classType.getAlfrescoName(); buf.append(typeName); buf.append("\""); // the combination operator buf.append(",operator:\""); buf.append(sfType.getOperator()); buf.append("\""); // the properties buf.append(",fields:{"); // open fields boolean first = true; for (SearchFieldType fieldType : sfType.getField()) { // find the corresponding attribute AttributeType refAttribute = null; String fieldName = fieldType.getName(); for (AttributeType attribute : getAllAttibutes(classType)) { if (attribute.getName().equals(fieldName)) { refAttribute = attribute; break; } } if (refAttribute == null) { logger.error("The attribute for search field '" + fieldName + "' was not found! Since this should never happen, the mapping file is most obviously not correct."); throw new ServletException("The processing of this search form could not complete."); } Element fieldElt = DOMUtil.getChild(rootElt, fieldName); Element opElt = DOMUtil.getChild(fieldElt, MsgId.INT_INSTANCE_SEARCH_OPCODE.getText()); String operator = opElt.getTextContent(); if (StringUtils.trimToNull(operator) == null) { continue; } if (operator.equals(MsgId.INT_SEARCH_OPERATOR_IGNORE.getText()) == false) { if (first == false) { buf.append(","); } String att_package = ""; if (refAttribute.getAspectType() != null) { att_package = refAttribute.getAspectType().getPackage(); } else if (refAttribute.getClassType() != null) { att_package = refAttribute.getClassType().getPackage(); } String namespaceAttribute = MappingToolCommon.getNameSpaceFromPrefix(getRootPackage(att_package)); propName = "{"; propName += namespaceAttribute; propName += "}"; propName += refAttribute.getAlfrescoName(); if (shortPropertyNames) { propName = StringUtils.replace(propName, typeName, ""); } buf.append("\""); buf.append(propName); buf.append("\":"); buf.append("{"); // open field buf.append("type:\""); String type; if (isEnumerated(fieldType)) { type = MsgId.INT_SEARCH_JSON_TYPE_ENUMS.getText(); } else if (refAttribute.getType().equalsIgnoreCase("object")) { type = "String"; } else { type = refAttribute.getType(); } buf.append(type); buf.append("\""); buf.append(",operator:\""); buf.append(operator); buf.append("\""); buf.append(",values:["); // open values if (hasSingleInput(fieldType)) { Element valElt = DOMUtil.getChild(fieldElt, MsgId.INT_INSTANCE_SEARCH_VALUE.getText()); String value = valElt.getTextContent(); if (isEnumerated(fieldType)) { value = convertSelectInputValuesToSearchFormat(value, fieldType.getEnum(), initParams); } buf.append("\""); buf.append(value); buf.append("\""); } else { Element valEltLo = DOMUtil.getChild(fieldElt, MsgId.INT_INSTANCE_SEARCH_VALUE_LO.getText()); String valueLo = valEltLo.getTextContent(); buf.append("\""); buf.append(valueLo); buf.append("\""); Element valEltHi = DOMUtil.getChild(fieldElt, MsgId.INT_INSTANCE_SEARCH_VALUE_HI.getText()); String valueHi = valEltHi.getTextContent(); buf.append(",\""); buf.append(valueHi); buf.append("\""); } buf.append("]"); // close values buf.append("}"); // close field first = false; } } buf.append("}"); // close fields buf.append("}"); // close the JSON string return buf.toString(); } /** * @param packageName * @param rootPackage * @return */ private String getRootPackage(String packageName) { String rootPackage = packageName; int pos = packageName.indexOf('.'); if (pos != -1) { rootPackage = packageName.substring(0, pos); } return rootPackage; } /** * Converts the text content of a node bound to a multiple select into the * appropriate format.<br/> * The XForms separator when multiple values are selected is a space. Since * we use sequential * numbers as 'values' (XForms terminology) there is no risk that spaces * inside the labels of * items interfere with the correct handling of the user's selections. * <p/> * Sample list items:<br/> * <table> * <th> * <tr> * <td>id (enum file terminology) / value (XForms term.)|</td> * <td>|value (enum file) / label (XForms)</td> * </tr> * </th> * <tr> * <td>1</td> * <td>Canada</td> * </tr> * <tr> * <td>2</td> * <td>England</td> * </tr> * <tr> * <td>3</td> * <td>France</td> * </tr> * <tr> * <td>4</td> * <td>New Zealand</td> * </tr> * <tr> * <td>5</td> * <td>United States of America</td> * </tr> * </table> * * @param value * the text content of the node, as provided by the XForms * engine. A sample content * is "1 3 4" to indicate that the first, third and fourth items * were selected. * @param enumName * the enumeration as saved in the mapping file * @return the comma-separated list of selected values or empty string if * none selected. e.g. * "Canada,France,New Zealand" */ private String convertSelectInputValuesToSearchFormat(String value, String enumName, Map<String, String> initParams) { String convertedValue = ""; boolean first = true; final String XFORMS_MULTIPLE_SELECT_SEPARATOR = " "; // we split the input value into individual tokens and process each String[] tokens = value.split(XFORMS_MULTIPLE_SELECT_SEPARATOR); for (String token : tokens) { String aValue = convertXformsAttributeToAlfresco(token, "string", enumName, initParams, false); if (first == false) { convertedValue += "\",\""; } convertedValue += aValue; first = false; } return convertedValue; } /** * Tells whether the search field has a single UI input control. * * @param fieldType * @return */ private boolean hasSingleInput(SearchFieldType fieldType) { String inputs = fieldType.getInputs(); boolean singleInput = ((StringUtils.trimToNull(inputs) == null) || (StringUtils.equals(inputs, "1"))); return singleInput; } /** * Tells whether the search field is mapped to a multiple select input * control, which happens * for string attributes that have an enumeration. * * @param fieldType * @return */ private boolean isEnumerated(SearchFieldType fieldType) { String enumName = fieldType.getEnum(); boolean isEnum = (StringUtils.trimToNull(enumName) != null); return isEnum; } /** * Build a list of all attributes including attributes from aspects * * @param classType * @return */ private List<AttributeType> getAllAttibutes(ClassType classType) { List<AttributeType> atts = new ArrayList<AttributeType>(); atts.addAll(classType.getAttribute()); List<AspectType> aspects = classType.getAspect(); for (AspectType aspectType : aspects) { List<AttributeType> aspectAttributes = getAspectType(aspectType).getAttribute(); atts.addAll(aspectAttributes); } return atts; } }