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