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.view.facelets.tag.jsf.core;
20  
21  import java.io.IOException;
22  import java.io.Serializable;
23  
24  import javax.el.ELException;
25  import javax.el.ValueExpression;
26  import javax.faces.FacesException;
27  import javax.faces.component.EditableValueHolder;
28  import javax.faces.component.UIComponent;
29  import javax.faces.context.FacesContext;
30  import javax.faces.event.AbortProcessingException;
31  import javax.faces.event.ValueChangeEvent;
32  import javax.faces.event.ValueChangeListener;
33  import javax.faces.view.EditableValueHolderAttachedObjectHandler;
34  import javax.faces.view.facelets.ComponentHandler;
35  import javax.faces.view.facelets.FaceletContext;
36  import javax.faces.view.facelets.FaceletException;
37  import javax.faces.view.facelets.TagAttribute;
38  import javax.faces.view.facelets.TagAttributeException;
39  import javax.faces.view.facelets.TagConfig;
40  import javax.faces.view.facelets.TagException;
41  import javax.faces.view.facelets.TagHandler;
42  
43  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFaceletAttribute;
44  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFaceletTag;
45  import org.apache.myfaces.view.facelets.FaceletCompositionContext;
46  import org.apache.myfaces.view.facelets.util.ReflectionUtil;
47  
48  /**
49   * Register an ValueChangeListener instance on the UIComponent associated with the closest parent UIComponent custom
50   * action.
51   * 
52   * @author Jacob Hookom
53   * @version $Id$
54   */
55  @JSFFaceletTag(
56          name = "f:valueChangeListener",
57          bodyContent = "empty", 
58          tagClass="org.apache.myfaces.taglib.core.ValueChangeListenerTag")
59  public final class ValueChangeListenerHandler extends TagHandler
60      implements EditableValueHolderAttachedObjectHandler
61  {
62  
63      private static class LazyValueChangeListener implements ValueChangeListener, Serializable
64      {
65  
66          private static final long serialVersionUID = 7613811124326963180L;
67  
68          private final String type;
69  
70          private final ValueExpression binding;
71  
72          public LazyValueChangeListener(String type, ValueExpression binding)
73          {
74              this.type = type;
75              this.binding = binding;
76          }
77  
78          public void processValueChange(ValueChangeEvent event) throws AbortProcessingException
79          {
80              ValueChangeListener instance = null;
81              FacesContext faces = FacesContext.getCurrentInstance();
82              if (faces == null)
83              {
84                  return;
85              }
86              if (this.binding != null)
87              {
88                  instance = (ValueChangeListener) binding.getValue(faces.getELContext());
89              }
90              if (instance == null && this.type != null)
91              {
92                  try
93                  {
94                      instance = (ValueChangeListener) ReflectionUtil.forName(this.type).newInstance();
95                  }
96                  catch (Exception e)
97                  {
98                      throw new AbortProcessingException("Couldn't Lazily instantiate ValueChangeListener", e);
99                  }
100                 if (this.binding != null)
101                 {
102                     binding.setValue(faces.getELContext(), instance);
103                 }
104             }
105             if (instance != null)
106             {
107                 instance.processValueChange(event);
108             }
109         }
110     }
111 
112     private final TagAttribute binding;
113 
114     private final String listenerType;
115 
116     public ValueChangeListenerHandler(TagConfig config)
117     {
118         super(config);
119         this.binding = this.getAttribute("binding");
120         TagAttribute type = this.getAttribute("type");
121         if (type != null)
122         {
123             if (!type.isLiteral())
124             {
125                 throw new TagAttributeException(type, "Must be a literal class name of type ValueChangeListener");
126             }
127             else
128             {
129                 // test it out
130                 try
131                 {
132                     ReflectionUtil.forName(type.getValue());
133                 }
134                 catch (ClassNotFoundException e)
135                 {
136                     throw new TagAttributeException(type, "Couldn't qualify ValueChangeListener", e);
137                 }
138             }
139             this.listenerType = type.getValue();
140         }
141         else
142         {
143             this.listenerType = null;
144         }
145     }
146 
147     /**
148      * See taglib documentation.
149      * 
150      * See javax.faces.view.facelets.FaceletHandler#apply(javax.faces.view.facelets.FaceletContext, 
151      * javax.faces.component.UIComponent)
152      */
153     public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, FaceletException,
154             ELException
155     {
156         if (!ComponentHandler.isNew(parent))
157         {
158             return;
159         }
160         if (parent instanceof EditableValueHolder)
161         {
162             applyAttachedObject(ctx.getFacesContext(), parent);
163         }
164         else if (UIComponent.isCompositeComponent(parent))
165         {
166             FaceletCompositionContext mctx = FaceletCompositionContext.getCurrentInstance(ctx);
167             mctx.addAttachedObjectHandler(parent, this);
168         }
169         else
170         {
171             throw new TagException(this.tag,
172                     "Parent not composite component or an instance of EditableValueHolder: " + parent);
173         }
174     }
175 
176     public void applyAttachedObject(FacesContext context, UIComponent parent)
177     {
178         // Retrieve the current FaceletContext from FacesContext object
179         FaceletContext faceletContext = (FaceletContext) context.getAttributes().get(
180                 FaceletContext.FACELET_CONTEXT_KEY);
181 
182         EditableValueHolder evh = (EditableValueHolder) parent;
183         ValueExpression b = null;
184         if (this.binding != null)
185         {
186             b = this.binding.getValueExpression(faceletContext, ValueChangeListener.class);
187         }
188         ValueChangeListener listener = new LazyValueChangeListener(this.listenerType, b);
189         evh.addValueChangeListener(listener);
190     }
191 
192     /**
193      * TODO: Document me!
194      */
195     @JSFFaceletAttribute
196     public String getFor()
197     {
198         TagAttribute forAttribute = getAttribute("for");
199         
200         if (forAttribute == null)
201         {
202             return null;
203         }
204         else
205         {
206             return forAttribute.getValue();
207         }
208     }
209 
210 }