001/* 002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Anahide Tchertchian 018 */ 019package org.nuxeo.ecm.platform.ui.web.component; 020 021import java.util.List; 022 023import javax.el.ValueExpression; 024import javax.faces.component.EditableValueHolder; 025import javax.faces.component.UIComponent; 026import javax.faces.component.ValueHolder; 027import javax.faces.event.ActionEvent; 028import javax.faces.event.AjaxBehaviorEvent; 029import javax.faces.event.FacesEvent; 030 031import org.apache.commons.lang.StringUtils; 032import org.apache.commons.logging.Log; 033import org.apache.commons.logging.LogFactory; 034import org.jboss.seam.ScopeType; 035import org.jboss.seam.annotations.Name; 036import org.jboss.seam.annotations.Scope; 037import org.nuxeo.ecm.platform.ui.web.util.ComponentRenderUtils; 038import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils; 039 040/** 041 * Managed bean, request-scoped, that resets the components value for ajax interactions. 042 * <p> 043 * Resets are done using the following rule: 044 * <ul> 045 * <li>if the component implements {@link ResettableComponent} interface, its method 046 * {@link ResettableComponent#resetCachedModel()} is called</li> 047 * <li>if the component implements {@link EditableValueHolder}, its submitted value is reset, and its local value is 048 * reset only if it holds a value binding for the "value" attribute</li> 049 * <li>if the component implements {@link ValueHolder}, its its local value is reset only if it holds a value binding 050 * for the "value" attribute.</li> 051 * </ul> 052 * 053 * @since 5.7 054 */ 055@Name("jsfResetActions") 056@Scope(ScopeType.EVENT) 057public class JSFResetActionsBean { 058 059 private static final Log log = LogFactory.getLog(JSFResetActionsBean.class); 060 061 /** 062 * Base component id for reset actions. 063 * 064 * @since 5.9.1 065 */ 066 protected String baseComponentId; 067 068 /** 069 * Returns the base component id, if {@link #setBaseComponentId(String)} was previously called in the same request, 070 * or null. 071 * 072 * @since 5.9.1 073 */ 074 public String getBaseComponentId() { 075 return baseComponentId; 076 } 077 078 /** 079 * Sets the base component id so that {@link #resetComponentsFor(ActionEvent)} can look it up in the hierarchy of 080 * components, and reset component states recursively from it. 081 * 082 * @since 5.9.1 083 * @see #resetComponentsFor(ActionEvent) 084 */ 085 public void setBaseComponentId(String baseComponentId) { 086 this.baseComponentId = baseComponentId; 087 } 088 089 /** 090 * Looks up the parent naming container for the component source of the action event, and reset components 091 * recursively within this container. 092 * 093 * @since 5.9.1 094 */ 095 public void resetComponentsFor(ActionEvent event) { 096 resetComponentsFor((FacesEvent) event); 097 } 098 099 /** 100 * Looks up the parent naming container for the corresponding ajax behavior event, and reset components recursively 101 * within this container. 102 * 103 * @since 8.1 104 */ 105 public void resetComponentsFor(AjaxBehaviorEvent event) { 106 resetComponentsFor((FacesEvent) event); 107 } 108 109 protected void resetComponentsFor(FacesEvent event) { 110 UIComponent component = event.getComponent(); 111 if (component == null) { 112 return; 113 } 114 String baseCompId = ComponentUtils.getAttributeValue(component, "target", String.class, null, false); 115 if (baseCompId == null) { 116 // compat 117 baseCompId = getBaseComponentId(); 118 } 119 if (baseCompId != null) { 120 String[] split = baseCompId.split("\\s"); 121 if (split != null) { 122 for (String item : split) { 123 if (!StringUtils.isBlank(item)) { 124 UIComponent anchor = ComponentRenderUtils.getComponent(component, item); 125 resetComponentResursive(anchor); 126 } 127 } 128 } 129 } else { 130 log.error("No base component id given => cannot reset components state."); 131 } 132 } 133 134 /** 135 * Looks up the parent naming container for the component source of the action event, and reset components 136 * recursively within this container. 137 */ 138 public void resetComponents(ActionEvent event) { 139 resetComponents((FacesEvent) event); 140 } 141 142 /** 143 * Looks up the parent naming container for the component source of the action event, and reset components 144 * recursively within this container. 145 * 146 * @since 6.0 147 */ 148 public void resetComponents(AjaxBehaviorEvent event) { 149 resetComponents((FacesEvent) event); 150 } 151 152 protected void resetComponents(FacesEvent event) { 153 UIComponent component = event.getComponent(); 154 if (component == null) { 155 return; 156 } 157 // take first anchor and force flush on every resettable component 158 UIComponent anchor = component.getNamingContainer(); 159 if (anchor == null) { 160 resetComponentResursive(component); 161 } else { 162 resetComponentResursive(anchor); 163 } 164 } 165 166 /** 167 * Resets the given component. 168 * <p> 169 * Does not reset the component children. 170 */ 171 public void resetComponent(UIComponent component) { 172 resetComponent(component, false); 173 } 174 175 /** 176 * Resets the given component and its children recursively. 177 */ 178 public void resetComponentResursive(UIComponent parent) { 179 resetComponent(parent, true); 180 } 181 182 protected void resetComponent(UIComponent comp, boolean recursive) { 183 if (comp == null) { 184 return; 185 } 186 if (comp instanceof ResettableComponent) { 187 ((ResettableComponent) comp).resetCachedModel(); 188 } else { 189 if (comp instanceof EditableValueHolder) { 190 // reset submitted value 191 ((EditableValueHolder) comp).setSubmittedValue(null); 192 } 193 if (comp instanceof ValueHolder) { 194 // reset local value, only if there's a value expression 195 // binding 196 ValueExpression ve = comp.getValueExpression("value"); 197 if (ve != null) { 198 ValueHolder vo = (ValueHolder) comp; 199 vo.setValue(null); 200 if (comp instanceof EditableValueHolder) { 201 ((EditableValueHolder) comp).setLocalValueSet(false); 202 } 203 } 204 } 205 } 206 if (recursive) { 207 List<UIComponent> children = comp.getChildren(); 208 if (children != null && !children.isEmpty()) { 209 for (UIComponent child : children) { 210 resetComponent(child, recursive); 211 } 212 } 213 } 214 } 215 216}