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 log.error(anchor); 126 resetComponentResursive(anchor); 127 } 128 } 129 } 130 } else { 131 log.error("No base component id given => cannot reset components state."); 132 } 133 } 134 135 /** 136 * Looks up the parent naming container for the component source of the action event, and reset components 137 * recursively within this container. 138 */ 139 public void resetComponents(ActionEvent event) { 140 resetComponents((FacesEvent) event); 141 } 142 143 /** 144 * Looks up the parent naming container for the component source of the action event, and reset components 145 * recursively within this container. 146 * 147 * @since 6.0 148 */ 149 public void resetComponents(AjaxBehaviorEvent event) { 150 resetComponents((FacesEvent) event); 151 } 152 153 protected void resetComponents(FacesEvent event) { 154 UIComponent component = event.getComponent(); 155 if (component == null) { 156 return; 157 } 158 // take first anchor and force flush on every resettable component 159 UIComponent anchor = component.getNamingContainer(); 160 if (anchor == null) { 161 resetComponentResursive(component); 162 } else { 163 resetComponentResursive(anchor); 164 } 165 } 166 167 /** 168 * Resets the given component. 169 * <p> 170 * Does not reset the component children. 171 */ 172 public void resetComponent(UIComponent component) { 173 resetComponent(component, false); 174 } 175 176 /** 177 * Resets the given component and its children recursively. 178 */ 179 public void resetComponentResursive(UIComponent parent) { 180 resetComponent(parent, true); 181 } 182 183 protected void resetComponent(UIComponent comp, boolean recursive) { 184 if (comp == null) { 185 return; 186 } 187 if (comp instanceof ResettableComponent) { 188 ((ResettableComponent) comp).resetCachedModel(); 189 } else { 190 if (comp instanceof EditableValueHolder) { 191 // reset submitted value 192 ((EditableValueHolder) comp).setSubmittedValue(null); 193 } 194 if (comp instanceof ValueHolder) { 195 // reset local value, only if there's a value expression 196 // binding 197 ValueExpression ve = comp.getValueExpression("value"); 198 if (ve != null) { 199 ValueHolder vo = (ValueHolder) comp; 200 vo.setValue(null); 201 if (comp instanceof EditableValueHolder) { 202 ((EditableValueHolder) comp).setLocalValueSet(false); 203 } 204 } 205 } 206 } 207 if (recursive) { 208 List<UIComponent> children = comp.getChildren(); 209 if (children != null && !children.isEmpty()) { 210 for (UIComponent child : children) { 211 resetComponent(child, recursive); 212 } 213 } 214 } 215 } 216 217}