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