Source code: com/sun/facelets/tag/jsf/ComponentHandler.java
1 /**
2 * Licensed under the Common Development and Distribution License,
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 * http://www.sun.com/cddl/
7 *
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
11 * implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15 package com.sun.facelets.tag.jsf;
16
17 import java.io.IOException;
18 import java.util.logging.Level;
19 import java.util.logging.Logger;
20
21 import javax.el.ELException;
22 import javax.el.MethodExpression;
23 import javax.el.ValueExpression;
24 import javax.faces.FacesException;
25 import javax.faces.application.Application;
26 import javax.faces.component.ActionSource;
27 import javax.faces.component.ActionSource2;
28 import javax.faces.component.EditableValueHolder;
29 import javax.faces.component.UIComponent;
30 import javax.faces.component.UIViewRoot;
31 import javax.faces.component.ValueHolder;
32 import javax.faces.context.FacesContext;
33 import javax.faces.convert.Converter;
34 import javax.faces.el.ValueBinding;
35 import javax.faces.event.ActionEvent;
36 import javax.faces.event.MethodExpressionActionListener;
37 import javax.faces.event.MethodExpressionValueChangeListener;
38 import javax.faces.event.ValueChangeEvent;
39 import javax.faces.validator.MethodExpressionValidator;
40
41 import com.sun.facelets.FaceletContext;
42 import com.sun.facelets.el.ELAdaptor;
43 import com.sun.facelets.el.LegacyMethodBinding;
44 import com.sun.facelets.el.LegacyValueBinding;
45 import com.sun.facelets.tag.MetaTagHandler;
46 import com.sun.facelets.tag.TagAttribute;
47 import com.sun.facelets.tag.Metadata;
48 import com.sun.facelets.tag.TagException;
49 import com.sun.facelets.tag.TagHandler;
50 import com.sun.facelets.tag.MetaRuleset;
51 import com.sun.facelets.util.FacesAPI;
52
53 /**
54 * Implementation of the tag logic used in the JSF specification. This is your
55 * golden hammer for wiring UIComponents to Facelets.
56 *
57 * @author Jacob Hookom
58 * @version $Id: ComponentHandler.java,v 1.14 2006/03/29 15:01:22 jhook Exp $
59 */
60 public class ComponentHandler extends MetaTagHandler {
61
62 private final static Logger log = Logger
63 .getLogger("facelets.tag.component");
64
65 private final TagAttribute binding;
66
67 private final String componentType;
68
69 private final TagAttribute id;
70
71 private final String rendererType;
72
73 public ComponentHandler(ComponentConfig config) {
74 super(config);
75 this.componentType = config.getComponentType();
76 this.rendererType = config.getRendererType();
77 this.id = this.getAttribute("id");
78 this.binding = this.getAttribute("binding");
79 }
80
81 /**
82 * Method handles UIComponent tree creation in accordance with the JSF 1.2
83 * spec.
84 * <ol>
85 * <li>First determines this UIComponent's id by calling
86 * {@link #getId(FaceletContext) getId(FaceletContext)}.</li>
87 * <li>Search the parent for an existing UIComponent of the id we just
88 * grabbed</li>
89 * <li>If found, {@link #markForDeletion(UIComponent) mark} its children
90 * for deletion.</li>
91 * <li>If <i>not</i> found, call
92 * {@link #createComponent(FaceletContext) createComponent}.
93 * <ol>
94 * <li>Only here do we apply
95 * {@link ObjectHandler#setAttributes(FaceletContext, Object) attributes}</li>
96 * <li>Set the UIComponent's id</li>
97 * <li>Set the RendererType of this instance</li>
98 * </ol>
99 * </li>
100 * <li>Now apply the nextHandler, passing the UIComponent we've
101 * created/found.</li>
102 * <li>Now add the UIComponent to the passed parent</li>
103 * <li>Lastly, if the UIComponent already existed (found), then
104 * {@link #finalizeForDeletion(UIComponent) finalize} for deletion.</li>
105 * </ol>
106 *
107 * @see com.sun.facelets.FaceletHandler#apply(com.sun.facelets.FaceletContext,
108 * javax.faces.component.UIComponent)
109 *
110 * @throws TagException
111 * if the UIComponent parent is null
112 */
113 public final void apply(FaceletContext ctx, UIComponent parent)
114 throws IOException, FacesException, ELException {
115 // make sure our parent is not null
116 if (parent == null) {
117 throw new TagException(this.tag, "Parent UIComponent was null");
118 }
119
120 // our id
121 String id = ctx.generateUniqueId(this.tagId);
122
123 // grab our component
124 UIComponent c = ComponentSupport.findChildByTagId(parent, id);
125 boolean componentFound = false;
126 if (c != null) {
127 componentFound = true;
128 // mark all children for cleaning
129 if (log.isLoggable(Level.FINE)) {
130 log.fine(this.tag
131 + " Component["+id+"] Found, marking children for cleanup");
132 }
133 ComponentSupport.markForDeletion(c);
134 } else {
135 c = this.createComponent(ctx);
136 if (log.isLoggable(Level.FINE)) {
137 log.fine(this.tag + " Component["+id+"] Created: "
138 + c.getClass().getName());
139 }
140 this.setAttributes(ctx, c);
141
142 // mark it owned by a facelet instance
143 c.getAttributes().put(ComponentSupport.MARK_CREATED, id);
144
145 // assign our unique id
146 if (this.id != null) {
147 c.setId(this.id.getValue(ctx));
148 } else {
149 UIViewRoot root = ComponentSupport.getViewRoot(ctx, parent);
150 if (root != null) {
151 String uid = root.createUniqueId();
152 c.setId(uid);
153 }
154 }
155
156 if (this.rendererType != null) {
157 c.setRendererType(this.rendererType);
158 }
159
160 // hook method
161 this.onComponentCreated(ctx, c, parent);
162 }
163
164 // first allow c to get populated
165 this.applyNextHandler(ctx, c);
166
167 // finish cleaning up orphaned children
168 if (componentFound) {
169 ComponentSupport.finalizeForDeletion(c);
170 parent.getChildren().remove(c);
171 }
172
173
174 this.onComponentPopulated(ctx, c, parent);
175
176 // add to the tree afterwards
177 // this allows children to determine if it's
178 // been part of the tree or not yet
179 parent.getChildren().add(c);
180 }
181
182 /**
183 * If the binding attribute was specified, use that in conjuction with our
184 * componentType String variable to call createComponent on the Application,
185 * otherwise just pass the componentType String.
186 * <p />
187 * If the binding was used, then set the ValueExpression "binding" on the
188 * created UIComponent.
189 *
190 * @see Application#createComponent(javax.faces.el.ValueBinding,
191 * javax.faces.context.FacesContext, java.lang.String)
192 * @see Application#createComponent(java.lang.String)
193 * @param ctx
194 * FaceletContext to use in creating a component
195 * @return
196 */
197 protected UIComponent createComponent(FaceletContext ctx) {
198 UIComponent c = null;
199 FacesContext faces = ctx.getFacesContext();
200 Application app = faces.getApplication();
201 if (this.binding != null) {
202 ValueExpression ve = this.binding.getValueExpression(ctx,
203 Object.class);
204 if (FacesAPI.getVersion() >= 12) {
205 c = app.createComponent(ve, faces, this.componentType);
206 if (c != null) {
207 // Make sure the component supports 1.2
208 if (FacesAPI.getComponentVersion(c) >= 12) {
209 c.setValueExpression("binding", ve);
210 } else {
211 ValueBinding vb = new LegacyValueBinding(ve);
212 c.setValueBinding("binding", vb);
213 }
214
215 }
216 } else {
217 ValueBinding vb = new LegacyValueBinding(ve);
218 c = app.createComponent(vb, faces, this.componentType);
219 if (c != null) {
220 c.setValueBinding("binding", vb);
221 }
222 }
223 } else {
224 c = app.createComponent(this.componentType);
225 }
226 return c;
227 }
228
229 /**
230 * If the id TagAttribute was specified, get it's value, otherwise generate
231 * a unique id from our tagId.
232 *
233 * @see TagAttribute#getValue(FaceletContext)
234 * @param ctx
235 * FaceletContext to use
236 * @return what should be a unique Id
237 */
238 protected String getId(FaceletContext ctx) {
239 if (this.id != null) {
240 return this.id.getValue(ctx);
241 }
242 return ctx.generateUniqueId(this.tagId);
243 }
244
245 protected MetaRuleset createMetaRuleset(Class type) {
246 MetaRuleset m = super.createMetaRuleset(type);
247
248 // ignore standard component attributes
249 m.ignore("binding").ignore("id");
250
251 // add auto wiring for attributes
252 m.addRule(ComponentRule.Instance);
253
254 // if it's an ActionSource
255 if (ActionSource.class.isAssignableFrom(type)) {
256 m.addRule(ActionSourceRule.Instance);
257 }
258
259 // if it's a ValueHolder
260 if (ValueHolder.class.isAssignableFrom(type)) {
261 m.addRule(ValueHolderRule.Instance);
262
263 // if it's an EditableValueHolder
264 if (EditableValueHolder.class.isAssignableFrom(type)) {
265 m.ignore("submittedValue");
266 m.ignore("valid");
267 m.addRule(EditableValueHolderRule.Instance);
268 }
269 }
270
271 return m;
272 }
273
274 /**
275 * A hook method for allowing developers to do additional processing once Facelets
276 * creates the component. The 'setAttributes' method is still perferred, but this
277 * method will provide the parent UIComponent before it's been added to the tree and
278 * before any children have been added to the newly created UIComponent.
279 *
280 * @param ctx
281 * @param c
282 * @param parent
283 */
284 protected void onComponentCreated(FaceletContext ctx, UIComponent c, UIComponent parent) {
285 // do nothing
286 }
287
288 protected void onComponentPopulated(FaceletContext ctx, UIComponent c, UIComponent parent) {
289 // do nothing
290 }
291
292 protected void applyNextHandler(FaceletContext ctx, UIComponent c)
293 throws IOException, FacesException, ELException {
294 // first allow c to get populated
295 this.nextHandler.apply(ctx, c);
296 }
297 }