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}