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}