/* * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public License * (LGPL) version 2.1 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl.html * * 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. * * Contributors: * Nuxeo - initial API and implementation * * $Id$ */ package org.nuxeo.ecm.platform.ui.web.directory; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import javax.faces.component.UIComponent; import javax.faces.component.UIInput; import javax.faces.component.behavior.ClientBehaviorHolder; import javax.faces.context.FacesContext; import javax.faces.el.ValueBinding; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.common.utils.i18n.I18NUtils; /** * @author <a href="mailto:glefter@nuxeo.com">George Lefter</a> */ public class ChainSelectListboxComponent extends UIInput implements ClientBehaviorHolder { public static final String COMPONENT_TYPE = "nxdirectory.chainSelectListbox"; public static final String COMPONENT_FAMILY = "nxdirectory.chainSelectListbox"; private static final Log log = LogFactory.getLog(ChainSelectListboxComponent.class); public boolean ajaxUpdated = false; private String directoryName; private VocabularyEntryList directoryValues; private Boolean displayIdAndLabel = false; private Boolean displayObsoleteEntries = false; private String onchange; private int index; private String displayIdAndLabelSeparator = " "; private String cssStyle; private String cssStyleClass; private String size; private Boolean localize = false; private String displayValueOnlySeparator; private String ordering; private String display; public ChainSelectListboxComponent() { setRendererType(COMPONENT_TYPE); } @Override public String getFamily() { return COMPONENT_FAMILY; } public boolean isMultiSelect() { ChainSelect chain = getChain(); if (chain != null) { boolean isLastSelect = getIndex() == chain.getSize() - 1; boolean isChainMultiSelect = chain.getBooleanProperty( "multiSelect", false); return isLastSelect && isChainMultiSelect; } return false; } public String getDisplayIdAndLabelSeparator() { return displayIdAndLabelSeparator; } public void setDisplayIdAndLabelSeparator(String displayIdAndLabelSeparator) { this.displayIdAndLabelSeparator = displayIdAndLabelSeparator; } @Override public void restoreState(FacesContext context, Object state) { Object[] values = (Object[]) state; super.restoreState(context, values[0]); index = (Integer) values[1]; displayIdAndLabel = (Boolean) values[2]; displayIdAndLabelSeparator = (String) values[3]; displayObsoleteEntries = (Boolean) values[4]; ajaxUpdated = (Boolean) values[5]; directoryName = (String) values[6]; localize = (Boolean) values[7]; displayValueOnlySeparator = (String) values[10]; onchange = (String) values[11]; cssStyle = (String) values[12]; cssStyleClass = (String) values[13]; size = (String) values[14]; directoryValues = (VocabularyEntryList) values[15]; ordering = (String) values[16]; display = (String) values[17]; } @Override public Object saveState(FacesContext context) { Object[] values = new Object[18]; values[0] = super.saveState(context); values[1] = getIndex(); values[2] = displayIdAndLabel; values[3] = displayIdAndLabelSeparator; values[4] = displayObsoleteEntries; values[5] = ajaxUpdated; values[6] = directoryName; values[7] = localize; values[10] = displayValueOnlySeparator; values[11] = onchange; values[12] = cssStyle; values[13] = cssStyleClass; values[14] = size; values[15] = directoryValues; values[16] = ordering; values[17] = display; return values; } public String getDirectoryName() { ValueBinding vb = getValueBinding("directoryName"); if (vb != null) { return (String) vb.getValue(FacesContext.getCurrentInstance()); } else { return directoryName; } } public void setDirectoryName(String newDirectory) { directoryName = newDirectory; } public VocabularyEntryList getDirectoryValues() { ValueBinding vb = getValueBinding("directoryValues"); if (vb != null) { return (VocabularyEntryList) vb.getValue(FacesContext.getCurrentInstance()); } else { return null; } } public void setDirectoryValues(VocabularyEntryList directoryValues) { this.directoryValues = directoryValues; } public Map<String, DirectorySelectItem> getOptions() { index = getIndex(); if (index == 0 || getChain().getSelection(0).getColumnValue(index - 1) != null) { return rebuildOptions(); } return new HashMap<String, DirectorySelectItem>(); } public Boolean getDisplayIdAndLabel() { return displayIdAndLabel; } public void setDisplayIdAndLabel(Boolean displayIdAndLabel) { this.displayIdAndLabel = displayIdAndLabel; } public Boolean getDisplayObsoleteEntries() { return displayObsoleteEntries; } public void setDisplayObsoleteEntries(Boolean showObsolete) { displayObsoleteEntries = showObsolete; } public void setOnchange(String onchange) { this.onchange = onchange; } public String getOnchange() { return onchange; } public ChainSelect getChain() { UIComponent component = getParent(); while (component != null && !(component instanceof ChainSelect)) { component = component.getParent(); } return (ChainSelect) component; } public Object getProperty(String name) { ValueBinding vb = getValueBinding(name); if (vb != null) { return vb.getValue(FacesContext.getCurrentInstance()); } else { return getAttributes().get(name); } } public String getStringProperty(String name, String defaultValue) { String value = (String) getProperty(name); return value != null ? value : defaultValue; } public Boolean getBooleanProperty(String name, Boolean defaultValue) { Boolean value = (Boolean) getProperty(name); return value != null ? value : defaultValue; } public String getDisplayValueOnlySeparator() { return displayValueOnlySeparator; } public void setDisplayValueOnlySeparator(String displayValueOnlySeparator) { this.displayValueOnlySeparator = displayValueOnlySeparator; } public boolean isAjaxUpdated() { return ajaxUpdated; } public void setAjaxUpdated(boolean ajaxUpdated) { this.ajaxUpdated = ajaxUpdated; } /** * @return position of this component in the parent children list */ public Integer getIndex() { // return index; ValueBinding vb = getValueBinding("index"); if (vb != null) { return (Integer) vb.getValue(FacesContext.getCurrentInstance()); } else { return index; } } public void setIndex(Integer index) { this.index = index; } public String getCssStyle() { return cssStyle; } public void setCssStyle(String cssStyle) { this.cssStyle = cssStyle; } public String getCssStyleClass() { return cssStyleClass; } public void setCssStyleClass(String cssStyleClass) { this.cssStyleClass = cssStyleClass; } public String getSize() { return size; } public void setSize(String size) { this.size = size; } public Boolean getLocalize() { return localize; } public void setLocalize(Boolean localize) { this.localize = localize; } /** * Reload listbox values based on previous selections in the chain. * (functionality moved from ChainSelect) */ public LinkedHashMap<String, DirectorySelectItem> rebuildOptions() { index = getIndex(); Map<String, Serializable> filter = new HashMap<String, Serializable>(); Boolean displayObsolete = getBooleanProperty("displayObsoleteEntries", Boolean.FALSE); if (!displayObsolete) { filter.put("obsolete", 0); } String directoryName = getDirectoryName(); String defaultRootKey = getChain().getDefaultRootKey(); if (index == 0) { if (directoryName != null) { if (DirectoryHelper.instance().hasParentColumn(directoryName)) { filter.put("parent", defaultRootKey); } } else { filter.put("parent", defaultRootKey); } } else { boolean qualifiedParentKeys = getChain().isQualifiedParentKeys(); String keySeparator = getChain().getKeySeparator(); Selection sel = getChain().getSelections()[0]; String parentValue = sel.getParentKey(index, qualifiedParentKeys, keySeparator); if (parentValue == null) { // use default parent key parentValue = defaultRootKey; } filter.put("parent", parentValue); } VocabularyEntryList directoryValues = getDirectoryValues(); List<DirectorySelectItem> list; if (directoryName != null) { list = DirectoryHelper.instance().getSelectItems(directoryName, filter); } else { list = DirectoryHelper.getSelectItems(directoryValues, filter); } for (DirectorySelectItem item : list) { String id = (String) item.getValue(); String label = item.getLabel(); String translatedLabel = label; Boolean localize = getBooleanProperty("localize", Boolean.FALSE); if (localize) { translatedLabel = translate(label); } item.setLocalizedLabel(translatedLabel); String displayedLabel = translatedLabel; Boolean displayIdAndLabel = getBooleanProperty("displayIdAndLabel", Boolean.FALSE); if (displayIdAndLabel) { displayedLabel = id + displayIdAndLabelSeparator + label; } item.setDisplayedLabel(displayedLabel); } String ordering = getStringProperty("ordering", ""); if (ordering != null && !"".equals(ordering)) { Collections.sort(list, new DirectorySelectItemComparator(ordering)); } LinkedHashMap<String, DirectorySelectItem> options = new LinkedHashMap<String, DirectorySelectItem>(); options.clear(); for (DirectorySelectItem item : list) { options.put((String) item.getValue(), item); } return options; } private static String translate(String label) { FacesContext context = FacesContext.getCurrentInstance(); String bundleName = context.getApplication().getMessageBundle(); Locale locale = context.getViewRoot().getLocale(); label = I18NUtils.getMessageString(bundleName, label, null, locale); return label; } /** * This method reads submitted data and rebuilds the current list of values * based on selections in the parent components. */ @Override public void decode(FacesContext context) { // FIXME: this code is nonsense, what it's doing and why is // perfectly unclear ChainSelect chain = getChain(); if (chain.getDisplayValueOnly()) { return; } index = getIndex(); chain.setCompAtIndex(index, this); List<String> keyList = chain.getSelectionKeyList(); int size = chain.getSize(); String formerValue = chain.getSelection(0).getColumnValue(index); if (index == 0) { chain.setLastSelectedComponentIndex(Integer.MAX_VALUE); keyList.clear(); } if (chain.getLastSelectedComponentIndex() < index) { for (int i = index; i < size; i++) { chain.setOptions(i, null); } Map<String, DirectorySelectItem> options = rebuildOptions(); chain.setOptions(index, options); return; } Map<String, String> requestMap = context.getExternalContext().getRequestParameterMap(); Map<String, String[]> requestValueMap = context.getExternalContext().getRequestParameterValuesMap(); String name = getClientId(context); String value = requestMap.get(name); if (value == null || value.length() == 0) { if (chain.getLastSelectedComponentIndex() > index) { chain.setLastSelectedComponentIndex(index); } for (int i = index; i < chain.getSize(); i++) { chain.setOptions(i, null); } Map<String, DirectorySelectItem> options = rebuildOptions(); chain.setOptions(index, options); } else { keyList.add(value); } if (!StringUtils.equals(value, formerValue)) { chain.setLastSelectedComponentIndex(index); for (int i = index; i < chain.getSize(); i++) { chain.setOptions(i, null); } } String[] lastValues = requestValueMap.get(name); boolean lastValueIsOk = lastValues != null && lastValues.length != 0 && !StringUtils.isEmpty(lastValues[0]); Selection[] selections; boolean stop = chain.getLastSelectedComponentIndex() < index; if (index == size - 1 && lastValueIsOk && !stop) { String[] keyListArray = new String[size]; selections = new Selection[lastValues.length]; keyListArray = keyList.toArray(keyListArray); for (int i = 0; i < lastValues.length; i++) { keyListArray[size - 1] = lastValues[i]; selections[i] = chain.createSelection(keyListArray); } } else { selections = new Selection[1]; String[] columns = keyList.toArray(new String[0]); selections[0] = chain.createSelection(columns); } if (chain.getLastSelectedComponentIndex() == index) { chain.setSelections(selections); } Map<String, DirectorySelectItem> options = rebuildOptions(); chain.setOptions(index, options); } public String getOrdering() { return ordering; } public void setOrdering(String ordering) { this.ordering = ordering; } public String getDisplay() { if (display != null) { return display; } else if (Boolean.TRUE.equals(displayIdAndLabel)) { return "idAndLabel"; } return "label"; } public void setDisplay(String display) { this.display = display; } /** * @since 6.0 */ private static final Collection<String> EVENT_NAMES = Collections.unmodifiableCollection(Arrays.asList( "blur", "change", "valueChange", "click", "dblclick", "focus", "keydown", "keypress", "keyup", "mousedown", "mousemove", "mouseout", "mouseover", "mouseup", "select")); public Collection<String> getEventNames() { return EVENT_NAMES; } public String getDefaultEventName() { return "valueChange"; } }