001/*
002 * (C) Copyright 2010 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.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.binding.alias;
018
019import java.io.IOException;
020import java.util.List;
021
022import javax.faces.FacesException;
023import javax.faces.component.ContextCallback;
024import javax.faces.component.UIComponent;
025import javax.faces.component.UIOutput;
026import javax.faces.component.visit.VisitCallback;
027import javax.faces.component.visit.VisitContext;
028import javax.faces.context.FacesContext;
029import javax.faces.event.FacesEvent;
030import javax.faces.event.PhaseId;
031
032import org.apache.commons.logging.Log;
033import org.apache.commons.logging.LogFactory;
034import org.nuxeo.ecm.platform.ui.web.tag.handler.SetTagHandler;
035
036import com.sun.faces.facelets.tag.jsf.ComponentSupport;
037
038/**
039 * Holder component for value expressions.
040 * <p>
041 * Since 6.0 and JSF2 migration, exposed values are now stored in a view-scoped managed bean and do not need to be
042 * exposed again at render time.
043 * <p>
044 * This component is still interesting to anchor the component in the tree, and make sure its children are reset on ajax
045 * requests, when the value it holds can have an impact on the underlying components. In this case, it is instantiated
046 * by the {@link AliasTagHandler} or {@link SetTagHandler} facelet handlers, depending on their instantiation criteria.
047 *
048 * @author Anahide Tchertchian
049 * @since 5.4
050 * @see AliasTagHandler
051 * @see SetTagHandler
052 */
053public class UIAliasHolder extends UIOutput {
054
055    private static final Log log = LogFactory.getLog(UIAliasHolder.class);
056
057    public static final String COMPONENT_TYPE = UIAliasHolder.class.getName();
058
059    public static final String COMPONENT_FAMILY = UIAliasHolder.class.getName();
060
061    /**
062     * Keep the alias transient: it's supposed to be set at build time by facelet handlers and does not need to be
063     * restored/saved.
064     *
065     * @since 6.0
066     */
067    protected transient AliasVariableMapper alias;
068
069    public UIAliasHolder() {
070        super();
071    }
072
073    @Override
074    public String getFamily() {
075        return COMPONENT_FAMILY;
076    }
077
078    @Override
079    public String getRendererType() {
080        return null;
081    }
082
083    @Override
084    public void setRendererType(String rendererType) {
085        // do nothing
086    }
087
088    @Override
089    public boolean isRendered() {
090        return true;
091    }
092
093    @Override
094    public void setRendered(boolean rendered) {
095        // do nothing
096    }
097
098    @Override
099    public boolean getRendersChildren() {
100        return true;
101    }
102
103    @Override
104    public void broadcast(FacesEvent event) {
105        if (event instanceof AliasEvent) {
106            FacesContext context = getFacesContext();
107            AliasVariableMapper alias = getAliasVariableMapper();
108            try {
109                AliasVariableMapper.exposeAliasesToRequest(context, alias);
110                FacesEvent origEvent = ((AliasEvent) event).getOriginalEvent();
111                origEvent.getComponent().broadcast(origEvent);
112            } finally {
113                if (alias != null) {
114                    AliasVariableMapper.removeAliasesExposedToRequest(context, alias.getId());
115                }
116            }
117        } else {
118            super.broadcast(event);
119        }
120    }
121
122    @Override
123    public void queueEvent(FacesEvent event) {
124        event = new AliasEvent(this, event);
125        super.queueEvent(event);
126    }
127
128    @Override
129    public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback)
130            throws FacesException {
131        AliasVariableMapper alias = getAliasVariableMapper();
132        try {
133            AliasVariableMapper.exposeAliasesToRequest(context, alias);
134            return super.invokeOnComponent(context, clientId, callback);
135        } finally {
136            if (alias != null) {
137                AliasVariableMapper.removeAliasesExposedToRequest(context, alias.getId());
138            }
139        }
140    }
141
142    @Override
143    public void encodeBegin(FacesContext context) throws IOException {
144        AliasVariableMapper alias = getAliasVariableMapper();
145        AliasVariableMapper.exposeAliasesToRequest(context, alias);
146        super.encodeBegin(context);
147    }
148
149    @Override
150    public void encodeChildren(final FacesContext context) throws IOException {
151        // no need to expose variables: already done in #encodeBegin
152        processFacetsAndChildren(context, PhaseId.RENDER_RESPONSE);
153    }
154
155    @Override
156    public void encodeEnd(FacesContext context) throws IOException {
157        AliasVariableMapper alias = getAliasVariableMapper();
158        if (alias != null) {
159            AliasVariableMapper.removeAliasesExposedToRequest(context, alias.getId());
160        }
161    }
162
163    @Override
164    public void processDecodes(FacesContext context) {
165        processFacetsAndChildrenWithVariables(context, PhaseId.APPLY_REQUEST_VALUES);
166    }
167
168    @Override
169    public void processValidators(FacesContext context) {
170        processFacetsAndChildrenWithVariables(context, PhaseId.PROCESS_VALIDATIONS);
171    }
172
173    @Override
174    public void processUpdates(FacesContext context) {
175        processFacetsAndChildrenWithVariables(context, PhaseId.UPDATE_MODEL_VALUES);
176    }
177
178    protected final void processFacetsAndChildren(final FacesContext context, final PhaseId phaseId) {
179        List<UIComponent> stamps = getChildren();
180        for (UIComponent stamp : stamps) {
181            processComponent(context, stamp, phaseId);
182        }
183    }
184
185    protected final void processFacetsAndChildrenWithVariables(final FacesContext context, final PhaseId phaseId) {
186        AliasVariableMapper alias = getAliasVariableMapper();
187        try {
188            AliasVariableMapper.exposeAliasesToRequest(context, alias);
189            processFacetsAndChildren(context, phaseId);
190        } finally {
191            if (alias != null) {
192                AliasVariableMapper.removeAliasesExposedToRequest(context, alias.getId());
193            }
194        }
195    }
196
197    protected final void processComponent(FacesContext context, UIComponent component, PhaseId phaseId) {
198        if (component != null) {
199            if (phaseId == PhaseId.APPLY_REQUEST_VALUES) {
200                component.processDecodes(context);
201            } else if (phaseId == PhaseId.PROCESS_VALIDATIONS) {
202                component.processValidators(context);
203            } else if (phaseId == PhaseId.UPDATE_MODEL_VALUES) {
204                component.processUpdates(context);
205            } else if (phaseId == PhaseId.RENDER_RESPONSE) {
206                try {
207                    ComponentSupport.encodeRecursive(context, component);
208                } catch (IOException err) {
209                    log.error("Error while rendering component " + component);
210                }
211            } else {
212                throw new IllegalArgumentException("Bad PhaseId:" + phaseId);
213            }
214        }
215    }
216
217    /**
218     * @since 6.0
219     */
220    public AliasVariableMapper getAlias() {
221        return alias;
222    }
223
224    /**
225     * @since 6.0
226     */
227    public void setAlias(AliasVariableMapper alias) {
228        this.alias = alias;
229    }
230
231    protected AliasVariableMapper getAliasVariableMapper() {
232        return getAlias();
233    }
234
235    @Override
236    public boolean visitTree(VisitContext visitContext, VisitCallback callback) {
237        FacesContext facesContext = visitContext.getFacesContext();
238        AliasVariableMapper alias = getAliasVariableMapper();
239        try {
240            AliasVariableMapper.exposeAliasesToRequest(facesContext, alias);
241            return super.visitTree(visitContext, callback);
242        } finally {
243            if (alias != null) {
244                AliasVariableMapper.removeAliasesExposedToRequest(facesContext, alias.getId());
245            }
246        }
247    }
248
249}