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}