001/* 002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Anahide Tchertchian 016 */ 017package org.nuxeo.ecm.platform.ui.web.component; 018 019import java.util.List; 020 021import javax.el.ValueExpression; 022import javax.faces.component.EditableValueHolder; 023import javax.faces.component.UIComponent; 024import javax.faces.component.ValueHolder; 025import javax.faces.event.ActionEvent; 026import javax.faces.event.AjaxBehaviorEvent; 027import javax.faces.event.FacesEvent; 028 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031import org.jboss.seam.ScopeType; 032import org.jboss.seam.annotations.Name; 033import org.jboss.seam.annotations.Scope; 034import org.nuxeo.ecm.platform.ui.web.util.ComponentRenderUtils; 035 036/** 037 * Managed bean, request-scoped, that resets the components value for ajax interactions. 038 * <p> 039 * Resets are done using the following rule: 040 * <ul> 041 * <li>if the component implements {@link ResettableComponent} interface, its method 042 * {@link ResettableComponent#resetCachedModel()} is called</li> 043 * <li>if the component implements {@link EditableValueHolder}, its submitted value is reset, and its local value is 044 * reset only if it holds a value binding for the "value" attribute</li> 045 * <li>if the component implements {@link ValueHolder}, its its local value is reset only if it holds a value binding 046 * for the "value" attribute.</li> 047 * </ul> 048 * 049 * @since 5.7 050 */ 051@Name("jsfResetActions") 052@Scope(ScopeType.EVENT) 053public class JSFResetActionsBean { 054 055 private static final Log log = LogFactory.getLog(JSFResetActionsBean.class); 056 057 /** 058 * Base component id for reset actions. 059 * 060 * @since 5.9.1 061 */ 062 protected String baseComponentId; 063 064 /** 065 * Returns the base component id, if {@link #setBaseComponentId(String)} was previously called in the same request, 066 * or null. 067 * 068 * @since 5.9.1 069 */ 070 public String getBaseComponentId() { 071 return baseComponentId; 072 } 073 074 /** 075 * Sets the base component id so that {@link #resetComponentsFor(ActionEvent)} can look it up in the hierarchy of 076 * components, and reset component states recursively from it. 077 * 078 * @since 5.9.1 079 * @see #resetComponentsFor(ActionEvent) 080 */ 081 public void setBaseComponentId(String baseComponentId) { 082 this.baseComponentId = baseComponentId; 083 } 084 085 /** 086 * Looks up the parent naming container for the component source of the action event, and reset components 087 * recursively within this container. 088 * 089 * @since 5.9.1 090 */ 091 public void resetComponentsFor(ActionEvent event) { 092 UIComponent component = event.getComponent(); 093 if (component == null) { 094 return; 095 } 096 String baseCompId = getBaseComponentId(); 097 if (baseCompId != null) { 098 UIComponent anchor = ComponentRenderUtils.getComponent(component, baseCompId); 099 resetComponentResursive(anchor); 100 } else { 101 log.error("No base component id given => cannot reset " + "components state."); 102 } 103 } 104 105 /** 106 * Looks up the parent naming container for the component source of the action event, and reset components 107 * recursively within this container. 108 */ 109 public void resetComponents(ActionEvent event) { 110 resetComponents((FacesEvent) event); 111 } 112 113 /** 114 * Looks up the parent naming container for the component source of the action event, and reset components 115 * recursively within this container. 116 * 117 * @since 6.0 118 */ 119 public void resetComponents(AjaxBehaviorEvent event) { 120 resetComponents((FacesEvent) event); 121 } 122 123 protected void resetComponents(FacesEvent event) { 124 UIComponent component = event.getComponent(); 125 if (component == null) { 126 return; 127 } 128 // take first anchor and force flush on every resettable component 129 UIComponent anchor = component.getNamingContainer(); 130 if (anchor == null) { 131 resetComponentResursive(component); 132 } else { 133 resetComponentResursive(anchor); 134 } 135 } 136 137 /** 138 * Resets the given component. 139 * <p> 140 * Does not reset the component children. 141 */ 142 public void resetComponent(UIComponent component) { 143 resetComponent(component, false); 144 } 145 146 /** 147 * Resets the given component and its children recursively. 148 */ 149 public void resetComponentResursive(UIComponent parent) { 150 resetComponent(parent, true); 151 } 152 153 protected void resetComponent(UIComponent comp, boolean recursive) { 154 if (comp == null) { 155 return; 156 } 157 if (comp instanceof ResettableComponent) { 158 ((ResettableComponent) comp).resetCachedModel(); 159 } else { 160 if (comp instanceof EditableValueHolder) { 161 // reset submitted value 162 ((EditableValueHolder) comp).setSubmittedValue(null); 163 } 164 if (comp instanceof ValueHolder) { 165 // reset local value, only if there's a value expression 166 // binding 167 ValueExpression ve = comp.getValueExpression("value"); 168 if (ve != null) { 169 ValueHolder vo = (ValueHolder) comp; 170 vo.setValue(null); 171 if (comp instanceof EditableValueHolder) { 172 ((EditableValueHolder) comp).setLocalValueSet(false); 173 } 174 } 175 } 176 } 177 if (recursive) { 178 List<UIComponent> children = comp.getChildren(); 179 if (children != null && !children.isEmpty()) { 180 for (UIComponent child : children) { 181 resetComponent(child, recursive); 182 } 183 } 184 } 185 } 186 187}