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