Java tutorial
/* * Copyright (C) 2005-2013 ManyDesigns srl. All rights reserved. * http://www.manydesigns.com/ * * This 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; either version 3 of * the License, or (at your option) any later version. * * This software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package com.manydesigns.elements.fields.search; import com.manydesigns.elements.annotations.Select; import com.manydesigns.elements.ognl.OgnlUtils; import com.manydesigns.elements.options.DefaultSelectionProvider; import com.manydesigns.elements.options.SearchDisplayMode; import com.manydesigns.elements.options.SelectionModel; import com.manydesigns.elements.options.SelectionProvider; import com.manydesigns.elements.reflection.PropertyAccessor; import com.manydesigns.elements.xml.XhtmlBuffer; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import javax.servlet.http.HttpServletRequest; import java.text.MessageFormat; import java.util.Map; /* * @author Paolo Predonzani - paolo.predonzani@manydesigns.com * @author Angelo Lupo - angelo.lupo@manydesigns.com * @author Giampiero Granatella - giampiero.granatella@manydesigns.com * @author Alessio Stalla - alessio.stalla@manydesigns.com */ public class SelectSearchField extends AbstractSearchField { public static final String copyright = "Copyright (c) 2005-2013, ManyDesigns srl"; protected SelectionModel selectionModel; protected int selectionModelIndex; protected boolean notSet; protected String comboLabel; protected String notSetLabel; protected SearchDisplayMode displayMode; protected String autocompleteId; protected String autocompleteInputName; protected SelectSearchField previousSelectField; protected SelectSearchField nextSelectField; public final static String AUTOCOMPLETE_SUFFIX = "_autocomplete"; public final static String VALUE_NOT_SET = "__notset__"; public SelectSearchField(PropertyAccessor accessor, String prefix) { this(accessor, null, prefix); } private void initializeModel(PropertyAccessor accessor, SelectionProvider selectionProvider) { Select annotation = accessor.getAnnotation(Select.class); if (selectionProvider == null) { if (annotation != null) { Object[] values = annotation.values(); String[] labels = annotation.labels(); assert (values.length == labels.length); DefaultSelectionProvider sp = new DefaultSelectionProvider(accessor.getName(), 1); for (int i = 0; i < values.length; i++) { sp.appendRow(values[i], labels[i], true); } selectionModel = sp.createSelectionModel(); displayMode = annotation.searchDisplayMode(); } } else { displayMode = selectionProvider.getSearchDisplayMode(); if (displayMode == null && annotation != null) { displayMode = annotation.searchDisplayMode(); } } if (displayMode == null) { displayMode = SearchDisplayMode.DROPDOWN; } selectionModelIndex = 0; comboLabel = getText("elements.field.select.select", label); notSetLabel = getText("elements.search.select.notset", label); autocompleteId = id + AUTOCOMPLETE_SUFFIX; autocompleteInputName = inputName + AUTOCOMPLETE_SUFFIX; } public SelectSearchField(PropertyAccessor accessor, SelectionProvider selectionProvider, String prefix) { super(accessor, prefix); initializeModel(accessor, selectionProvider); } public void toSearchString(StringBuilder sb) { if (!required && notSet) { appendToSearchString(sb, inputName, VALUE_NOT_SET); return; } Object[] values = getValues(); if (null == values) { return; } else { for (Object value : values) { String valueString = OgnlUtils.convertValueToString(value); appendToSearchString(sb, inputName, valueString); } } } public void configureCriteria(Criteria criteria) { if (!required && notSet) { criteria.isNull(accessor); return; } Object[] values = getValues(); if (values == null) { logger.debug("Null values array. Not adding 'in' criteria."); } else if (values.length == 0) { logger.debug("Enpty values array. Not adding 'in' criteria."); } else { if (logger.isDebugEnabled()) { logger.debug("Adding 'in' criteria for values: {}", ArrayUtils.toString(values)); } criteria.in(accessor, values); } } //************************************************************************** // Element implementation //************************************************************************** public void readFromRequest(HttpServletRequest req) { Object[] values = req.getParameterValues(inputName); if (values == null) { return; } else if (values.length == 1 && VALUE_NOT_SET.equals(values[0])) { notSet = true; } else if (SearchDisplayMode.AUTOCOMPLETE == displayMode) { String stringValue = values[0].toString(); boolean search; Object value; String userValue = req.getParameter(autocompleteInputName); if (StringUtils.isEmpty(stringValue)) { search = true; value = null; } else { value = OgnlUtils.convertValue(stringValue, accessor.getType()); String label = selectionModel.getOption(selectionModelIndex, value, true); search = userValue != null && !StringUtils.equals(userValue, label); } if (search) { value = null; //Attempt to find the value among the options Map<Object, SelectionModel.Option> options = selectionModel.getOptions(selectionModelIndex); boolean found = false; for (SelectionModel.Option option : options.values()) { if (ObjectUtils.equals(userValue, option.label)) { found = true; value = option.value; } } if (!found) { return; } } selectionModel.setValue(selectionModelIndex, value); } else { Object[] castedValues = new Object[values.length]; for (int i = 0; i < values.length; i++) { if (!StringUtils.isEmpty((String) values[i])) { castedValues[i] = OgnlUtils.convertValueQuietly(values[i], accessor.getType()); } } selectionModel.setValue(selectionModelIndex, castedValues); } } public boolean validate() { return true; } public void toXhtml(@NotNull XhtmlBuffer xb) { xb.openElement("div"); xb.addAttribute("class", "control-group"); switch (displayMode) { case DROPDOWN: valueToXhtmlDropDown(xb); break; case RADIO: valueToXhtmlRadio(xb); break; case AUTOCOMPLETE: valueToXhtmlAutocomplete(xb); break; case CHECKBOX: valueToXhtmlCheckbox(xb); break; case MULTIPLESELECT: valueToXhtmlMultipleSelection(xb); break; default: throw new IllegalStateException("Unknown display mode: " + displayMode.name()); } xb.closeElement("div"); } private void valueToXhtmlDropDown(XhtmlBuffer xb) { xb.writeLabel(StringUtils.capitalize(label), id, ATTR_NAME_HTML_CLASS); xb.openElement("div"); xb.addAttribute("class", "controls"); Object[] values = getValues(); Map<Object, SelectionModel.Option> options = selectionModel.getOptions(selectionModelIndex); xb.openElement("select"); xb.addAttribute("id", id); xb.addAttribute("name", inputName); boolean selected = (values == null && !notSet); if (!options.isEmpty()) { xb.writeOption("", selected, comboLabel); if (!required) { xb.writeOption(VALUE_NOT_SET, notSet, notSetLabel); } } for (Map.Entry<Object, SelectionModel.Option> option : options.entrySet()) { //#1318 include inactive options, because they must be searchable. Object optionValue = option.getKey(); String optionStringValue = OgnlUtils.convertValueToString(optionValue); String optionLabel = option.getValue().label; selected = ArrayUtils.contains(values, optionValue); xb.writeOption(optionStringValue, selected, optionLabel); } xb.closeElement("select"); if (nextSelectField != null) { String js = composeDropDownJs(); xb.writeJavaScript(js); } xb.closeElement("div"); } public String composeDropDownJs() { StringBuilder sb = new StringBuilder(); sb.append(MessageFormat.format( "$(''#{0}'').change(" + "function() '{'" + "updateSelectOptions(''{1}'', {2}, ''jsonSelectFieldSearchOptions''", StringEscapeUtils.escapeJavaScript(id), StringEscapeUtils.escapeJavaScript(selectionModel.getName()), selectionModelIndex + 1)); appendIds(sb); sb.append(");});"); return sb.toString(); } public void valueToXhtmlRadio(XhtmlBuffer xb) { Object[] values = getValues(); Map<Object, SelectionModel.Option> options = selectionModel.getOptions(selectionModelIndex); xb.writeLabel(StringUtils.capitalize(label), id, ATTR_NAME_HTML_CLASS); xb.openElement("div"); xb.addAttribute("class", "controls"); int counter = 0; if (!required) { String radioId = id + "_" + counter; boolean checked = (values == null && !notSet); writeRadioWithLabel(xb, radioId, getText("elements.search.select.none"), "", checked); counter++; radioId = id + "_" + counter; writeRadioWithLabel(xb, radioId, getText("elements.search.select.notset.radio"), VALUE_NOT_SET, notSet); counter++; } for (Map.Entry<Object, SelectionModel.Option> option : options.entrySet()) { if (!option.getValue().active) { continue; } Object optionValue = option.getKey(); String optionStringValue = OgnlUtils.convertValueToString(optionValue); String optionLabel = option.getValue().label; String radioId = id + "_" + counter; boolean checked = ArrayUtils.contains(values, optionValue); writeRadioWithLabel(xb, radioId, optionLabel, optionStringValue, checked); counter++; } xb.closeElement("div"); // TODO: gestire radio in cascata } protected void writeRadioWithLabel(XhtmlBuffer xb, String radioId, String label, String stringValue, boolean checked) { xb.openElement("label"); xb.addAttribute("class", "radio inline"); xb.addAttribute("for", radioId); xb.write(label); xb.writeInputRadio(radioId, inputName, stringValue, checked); xb.closeElement("label"); } public void valueToXhtmlAutocomplete(XhtmlBuffer xb) { xb.writeLabel(StringUtils.capitalize(label), id, ATTR_NAME_HTML_CLASS); xb.openElement("div"); xb.addAttribute("class", "controls"); Object value = selectionModel.getValue(selectionModelIndex); String stringValue = OgnlUtils.convertValueToString(value); xb.writeInputHidden(id, inputName, stringValue); String label = selectionModel.getOption(selectionModelIndex, value, true); xb.openElement("input"); xb.addAttribute("id", autocompleteId); xb.addAttribute("type", "text"); xb.addAttribute("name", autocompleteInputName); xb.addAttribute("value", label); xb.addAttribute("class", null); xb.addAttribute("size", null); xb.closeElement("input"); String js = composeAutocompleteJs(); xb.writeJavaScript(js); xb.closeElement("div"); } private void valueToXhtmlCheckbox(XhtmlBuffer xb) { xb.writeLabel(StringUtils.capitalize(label), id, ATTR_NAME_HTML_CLASS); xb.openElement("div"); xb.addAttribute("class", "controls"); Object[] values = getValues(); Map<Object, SelectionModel.Option> options = selectionModel.getOptions(selectionModelIndex); int counter = 0; for (Map.Entry<Object, SelectionModel.Option> option : options.entrySet()) { if (!option.getValue().active) { continue; } Object optionValue = option.getKey(); String optionStringValue = OgnlUtils.convertValueToString(optionValue); String optionLabel = option.getValue().label; boolean checked = ArrayUtils.contains(values, optionValue); String checkboxId = id + "_" + counter; xb.openElement("label"); xb.addAttribute("class", "checkbox"); xb.addAttribute("for", checkboxId); xb.writeNbsp(); xb.write(optionLabel); xb.writeNbsp(); xb.writeInputCheckbox(checkboxId, inputName, optionStringValue, checked); xb.writeNbsp(); xb.closeElement("label"); counter++; } xb.closeElement("div"); } private void valueToXhtmlMultipleSelection(XhtmlBuffer xb) { xb.writeLabel(StringUtils.capitalize(label), id, ATTR_NAME_HTML_CLASS); xb.openElement("div"); xb.addAttribute("class", "controls"); Object[] values = getValues(); Map<Object, SelectionModel.Option> options = selectionModel.getOptions(selectionModelIndex); xb.openElement("select"); xb.addAttribute("id", id); xb.addAttribute("name", inputName); xb.addAttribute("multiple", "multiple"); xb.addAttribute("size", "5"); boolean checked; for (Map.Entry<Object, SelectionModel.Option> option : options.entrySet()) { if (!option.getValue().active) { continue; } Object optionValue = option.getKey(); String optionStringValue = OgnlUtils.convertValueToString(optionValue); String optionLabel = option.getValue().label; checked = ArrayUtils.contains(values, optionValue); xb.writeOption(optionStringValue, checked, optionLabel); } xb.closeElement("select"); xb.closeElement("div"); } public String composeAutocompleteJs() { StringBuilder sb = new StringBuilder(); sb.append( MessageFormat.format("setupAutocomplete(''#{0}'', ''{1}'', {2}, ''jsonAutocompleteSearchOptions''", StringEscapeUtils.escapeJavaScript(autocompleteId), StringEscapeUtils.escapeJavaScript(selectionModel.getName()), selectionModelIndex)); appendIds(sb); sb.append(");"); return sb.toString(); } public void appendIds(StringBuilder sb) { SelectSearchField rootField = this; while (rootField.previousSelectField != null) { rootField = rootField.previousSelectField; } SelectSearchField currentField = rootField; while (currentField != null) { sb.append(MessageFormat.format(", ''#{0}''", StringEscapeUtils.escapeJavaScript(currentField.getId()))); currentField = currentField.nextSelectField; } } //************************************************************************** // Getter/setter //************************************************************************** public SelectionModel getSelectionModel() { return selectionModel; } public void setSelectionModel(SelectionModel selectionModel) { this.selectionModel = selectionModel; } public int getSelectionModelIndex() { return selectionModelIndex; } public void setSelectionModelIndex(int selectionModelIndex) { this.selectionModelIndex = selectionModelIndex; } public String getComboLabel() { return comboLabel; } public void setComboLabel(String comboLabel) { this.comboLabel = comboLabel; } public String getNotSetLabel() { return notSetLabel; } public void setNotSetLabel(String notSetLabel) { this.notSetLabel = notSetLabel; } public SearchDisplayMode getDisplayMode() { return displayMode; } public void setDisplayMode(SearchDisplayMode displayMode) { this.displayMode = displayMode; } public String getAutocompleteId() { return autocompleteId; } public void setAutocompleteId(String autocompleteId) { this.autocompleteId = autocompleteId; } public String getAutocompleteInputName() { return autocompleteInputName; } public void setAutocompleteInputName(String autocompleteInputName) { this.autocompleteInputName = autocompleteInputName; } public SelectSearchField getPreviousSelectField() { return previousSelectField; } public void setPreviousSelectField(SelectSearchField previousSelectField) { this.previousSelectField = previousSelectField; } public SelectSearchField getNextSelectField() { return nextSelectField; } public void setNextSelectField(SelectSearchField nextSelectField) { this.nextSelectField = nextSelectField; } public Object[] getValues() { Object object = selectionModel.getValue(selectionModelIndex); if (object instanceof Object[]) { return (Object[]) object; } else if (object != null) { return new Object[] { object }; } else { return null; } } public void setValue(Object[] values) { selectionModel.setValue(selectionModelIndex, values); } public Map<Object, SelectionModel.Option> getOptions() { return selectionModel.getOptions(selectionModelIndex); } public String getLabelSearch() { return selectionModel.getLabelSearch(selectionModelIndex); } }