View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.custom.aliasbean;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javax.el.ValueExpression;
29  import javax.faces.FacesException;
30  import javax.faces.component.ContextCallback;
31  import javax.faces.component.UIComponent;
32  import javax.faces.component.UIComponentBase;
33  import javax.faces.component.visit.VisitCallback;
34  import javax.faces.component.visit.VisitContext;
35  import javax.faces.context.FacesContext;
36  import javax.faces.event.AbortProcessingException;
37  import javax.faces.event.FacesEvent;
38  
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
42  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperties;
43  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty;
44  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
45  import org.apache.myfaces.shared_tomahawk.component.BindingAware;
46  import org.apache.myfaces.shared_tomahawk.util.RestoreStateUtils;
47  
48  /**
49   * The aliasBean tag allows you to create a temporary name for a real bean.
50   * The temporary name exists (is visible) only to the children of the aliasBean.
51   * <p>
52   * One use of this feature is to pass "parameters" from an including page to an
53   * included one. The included page can use any name it desires for beans it needs to
54   * reference, and the including page can then use aliasBean to make those names
55   * refer to the beans it wishes to "pass" as parameters.
56   * </p>
57   * <p>
58   * Suppose you have a block of components you use often but with different beans. You
59   * can create a separate JSP page (or equivalent) containing these beans, where the
60   * value-bindings refer to some fictive bean name. Document these names as the required
61   * "parameters" for this JSP page. Wherever you wish to use this block you then declare
62   * an alias component mapping each of these "parameters" to whatever beans (or literal
63   * values) you really want to apply the block to, then use jsp:include (or equivalent)
64   * to include the reusable block of components.
65   * </p>
66   * <p>
67   * Note, however, that AliasBean does not work for component bindings; JSF1.1
68   * just has no mechanism available to set up the alias during the "restore view"
69   * phase while the bindings of its children are being re-established, and then
70   * remove the alias after the child bindings are done.
71   * </p>
72   * <p>
73   * As a special case, if this component's direct parent is an AliasBeansScope
74   * then the alias (temporary name) is active until the end of the parent
75   * component, rather than the end of this component.
76   * </p>
77   *
78   * @author Sylvain Vieujot (latest modification by $Author: lu4242 $)
79   * @version $Revision: 690079 $ $Date: 2008-08-28 21:58:51 -0500 (jue, 28 ago 2008) $
80   */
81  @JSFComponent(
82          name="t:aliasBean",
83          tagClass = "org.apache.myfaces.custom.aliasbean.AliasBeanTag",
84          tagHandler = "org.apache.myfaces.custom.aliasbean.AliasBeanTagHandler")
85  @JSFJspProperties(properties={
86          @JSFJspProperty(
87                  name = "rendered",
88                  returnType = "boolean", 
89                  tagExcluded = true),
90          @JSFJspProperty(
91                  name = "binding",
92                  returnType = "java.lang.String",
93                  tagExcluded = true)
94                  })
95  public class AliasBean extends UIComponentBase implements BindingAware
96  {
97      private static final Log log = LogFactory.getLog(AliasBean.class);
98  
99      public static final String COMPONENT_TYPE = "org.apache.myfaces.AliasBean";
100     public static final String COMPONENT_FAMILY = "javax.faces.Data";
101 
102     private Alias alias;
103     
104     // Indicates whether withinScope has been initialised or not.
105     private boolean scopeSearched = false;
106     
107     // True if this is a direct child of an AliasBeansScope component.
108     private boolean withinScope;
109 
110     private transient FacesContext _context = null;
111 
112     public AliasBean()
113     {
114         alias = new Alias(this);
115     }
116 
117     public String getFamily()
118     {
119         return COMPONENT_FAMILY;
120     }
121 
122     public String getRendererType() {
123         return null;
124     }
125 
126     /**
127      * Define the "fictive" name which will be visible to the children
128      * of this component as an alias to the "real" object specified
129      * by the value attribute of this component.
130      *
131      * @param aliasBeanExpression
132      */
133     @JSFProperty
134     public void setAlias(String aliasBeanExpression)
135     {
136         alias.setAliasBeanExpression(aliasBeanExpression);
137     }
138 
139     /**
140      * The existing value that the alias can be set to. This can be 
141      * a literal string (like "toto") or a reference to an existing 
142      * bean (like "#{myBean.member1}").
143      * 
144      */
145     @JSFProperty(deferredValueType="java.lang.Object")
146     public String getValue()
147     {
148         String valueExpression = alias.getValueExpression();
149         if (valueExpression != null)
150             return valueExpression;
151 
152         // Normally, this component will have no value, because the setValue method always
153         // passes that data on to the alias instead. However it is possible for someone
154         // to use f:attribute (or other mechanism?) to set the value instead. So when the
155         // alias has no value, look for it there.
156         ValueExpression vb = getValueExpression("value");
157         return vb != null ?  (String) vb.getValue(getFacesContext().getELContext()) : null;
158     }
159 
160     public void setValue(String valueExpression)
161     {
162         alias.setValueExpression(valueExpression);
163     }
164 
165     public Object saveState(FacesContext context)
166     {
167         log.debug("saveState");
168 
169         _context = context;
170 
171         return new Object[]{super.saveState(context), alias.saveState()};
172     }
173 
174     public void restoreState(FacesContext context, Object state)
175     {
176         log.debug("restoreState");
177 
178         _context = context;
179 
180         Object values[] = (Object[]) state;
181         super.restoreState(context, values[0]);
182         alias.restoreState(values[1]);
183     }
184 
185     public Object processSaveState(FacesContext context)
186     {
187         if (context == null)
188             throw new NullPointerException("context");
189         if (isTransient())
190             return null;
191 
192         makeAlias(context);
193 
194         Map facetMap = null;
195         for (Iterator it = getFacets().entrySet().iterator(); it.hasNext();)
196         {
197             Map.Entry entry = (Map.Entry) it.next();
198             if (facetMap == null)
199                 facetMap = new HashMap();
200             UIComponent component = (UIComponent) entry.getValue();
201             if (!component.isTransient())
202             {
203                 facetMap.put(entry.getKey(), component.processSaveState(context));
204             }
205         }
206         List childrenList = null;
207         if (getChildCount() > 0)
208         {
209             for (Iterator it = getChildren().iterator(); it.hasNext();)
210             {
211                 UIComponent child = (UIComponent) it.next();
212                 if (!child.isTransient())
213                 {
214                     if (childrenList == null)
215                         childrenList = new ArrayList(getChildCount());
216                     childrenList.add(child.processSaveState(context));
217                 }
218             }
219         }
220 
221         removeAlias(context);
222 
223         return new Object[]{saveState(context), facetMap, childrenList};
224     }
225 
226     public void processRestoreState(FacesContext context, Object state)
227     {
228         if (context == null)
229             throw new NullPointerException("context");
230         Object myState = ((Object[]) state)[0];
231 
232         restoreState(context, myState);
233         makeAlias(context);
234 
235         Map facetMap = (Map) ((Object[]) state)[1];
236         List childrenList = (List) ((Object[]) state)[2];
237         for (Iterator it = getFacets().entrySet().iterator(); it.hasNext();)
238         {
239             Map.Entry entry = (Map.Entry) it.next();
240             Object facetState = facetMap.get(entry.getKey());
241             if (facetState != null)
242             {
243                 ((UIComponent) entry.getValue()).processRestoreState(context, facetState);
244             }
245             else
246             {
247                 context.getExternalContext().log("No state found to restore facet " + entry.getKey());
248             }
249         }
250         if (getChildCount() > 0)
251         {
252             int idx = 0;
253             for (Iterator it = getChildren().iterator(); it.hasNext();)
254             {
255                 UIComponent child = (UIComponent) it.next();
256                 Object childState = childrenList.get(idx++);
257                 if (childState != null)
258                 {
259                     child.processRestoreState(context, childState);
260                 }
261                 else
262                 {
263                     context.getExternalContext().log("No state found to restore child of component " + getId());
264                 }
265             }
266         }
267 
268         removeAlias(context);
269     }
270 
271     public void processValidators(FacesContext context)
272     {
273         if (withinScope)
274             return;
275 
276         log.debug("processValidators");
277         makeAlias(context);
278         super.processValidators(context);
279         removeAlias(context);
280     }
281 
282     public void processDecodes(FacesContext context)
283     {
284         log.debug("processDecodes");
285         if (withinScope)
286         {
287             if (! alias.isActive())
288                 makeAlias(context);
289 
290             super.processDecodes(context);
291             return;
292         }
293 
294         makeAlias(context);
295         super.processDecodes(context);
296         removeAlias(context);
297     }
298 
299     public void processUpdates(FacesContext context)
300     {
301         if (withinScope)
302             return;
303 
304         log.debug("processUpdates");
305         makeAlias(context);
306         super.processUpdates(context);
307         removeAlias(context);
308     }
309 
310 
311   public void encodeBegin(FacesContext context) throws IOException {
312     makeAlias(context);
313   }
314 
315 
316   public void encodeEnd(FacesContext context) throws IOException {
317     removeAlias();
318   }
319 
320   public void queueEvent(FacesEvent event)
321     {
322         super.queueEvent(new FacesEventWrapper(event, this));
323     }
324 
325     public void broadcast(FacesEvent event) throws AbortProcessingException
326     {
327         makeAlias();
328 
329         if (event instanceof FacesEventWrapper)
330         {
331             FacesEvent originalEvent = ((FacesEventWrapper) event).getWrappedFacesEvent();
332             originalEvent.getComponent().broadcast(originalEvent);
333         }
334         else
335         {
336             super.broadcast(event);
337         }
338 
339         removeAlias();
340     }
341 
342     void makeAlias(FacesContext context)
343     {
344         _context = context;
345         makeAlias();
346     }
347 
348     private void makeAlias()
349     {
350         if (! scopeSearched)
351         {
352             withinScope = getParent() instanceof AliasBeansScope;
353             if (withinScope)
354             {
355                 AliasBeansScope aliasScope = (AliasBeansScope) getParent();
356                 aliasScope.addAlias(alias);
357             }
358             scopeSearched = true;
359         }
360         alias.make(_context);
361     }
362 
363     void removeAlias(FacesContext context)
364     {
365         _context = context;
366         removeAlias();
367     }
368 
369     private void removeAlias()
370     {
371         if (! withinScope)
372             alias.remove(_context);
373     }
374 
375     @Deprecated
376     public void handleBindings()
377     {
378         makeAlias(getFacesContext());
379 
380         RestoreStateUtils.recursivelyHandleComponentReferencesAndSetValid(getFacesContext(),this,true);
381 
382         removeAlias(getFacesContext());
383     }
384 
385     @Override
386     public boolean invokeOnComponent(FacesContext context, String clientId,
387             ContextCallback callback) throws FacesException
388     {
389         makeAlias(getFacesContext());
390         try
391         {
392             return super.invokeOnComponent(context, clientId, callback);
393         }
394         finally
395         {
396             removeAlias(getFacesContext());
397         }
398     }
399 
400     @Override
401     public boolean visitTree(VisitContext context, VisitCallback callback)
402     {
403         makeAlias(getFacesContext());
404         try
405         {
406             return super.visitTree(context, callback);
407         }
408         finally
409         {
410             removeAlias(getFacesContext());
411         }
412     }
413 }