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;
20  
21  import java.io.IOException;
22  import java.util.logging.Level;
23  import java.util.logging.Logger;
24  
25  import jakarta.el.ELException;
26  import jakarta.el.ValueExpression;
27  import jakarta.faces.FacesException;
28  import jakarta.faces.application.Application;
29  import jakarta.faces.component.ActionSource;
30  import jakarta.faces.component.EditableValueHolder;
31  import jakarta.faces.component.UIComponent;
32  import jakarta.faces.component.UIViewRoot;
33  import jakarta.faces.component.ValueHolder;
34  import jakarta.faces.context.FacesContext;
35  import jakarta.faces.view.facelets.ComponentConfig;
36  import jakarta.faces.view.facelets.FaceletContext;
37  import jakarta.faces.view.facelets.MetaRuleset;
38  import jakarta.faces.view.facelets.MetaTagHandler;
39  import jakarta.faces.view.facelets.TagAttribute;
40  import jakarta.faces.view.facelets.TagException;
41  
42  import org.apache.myfaces.view.facelets.tag.MetaRulesetImpl;
43  
44  /**
45   * Implementation of the tag logic used in the JSF specification. This is your golden hammer for wiring UIComponents to
46   * Facelets.
47   * 
48   * @deprecated Use jakarta.faces.view.facelets.ComponentHandler instead
49   * @author Jacob Hookom
50   * @version $Id$
51   */
52  @Deprecated
53  public class ComponentHandler extends MetaTagHandler
54  {
55  
56      //private final static Logger log = Logger.getLogger("facelets.tag.component");
57      private final static Logger log = Logger.getLogger(ComponentHandler.class.getName());
58  
59      private final TagAttribute binding;
60  
61      private final String componentType;
62  
63      private final TagAttribute id;
64  
65      private final String rendererType;
66  
67      public ComponentHandler(ComponentConfig config)
68      {
69          super(config);
70          this.componentType = config.getComponentType();
71          this.rendererType = config.getRendererType();
72          this.id = this.getAttribute("id");
73          this.binding = this.getAttribute("binding");
74      }
75  
76      /**
77       * Method handles UIComponent tree creation in accordance with the JSF 1.2 spec.
78       * <ol>
79       * <li>First determines this UIComponent's id by calling {@link #getId(FaceletContext) getId(FaceletContext)}.</li>
80       * <li>Search the parent for an existing UIComponent of the id we just grabbed</li>
81       * <li>If found, markForDeletion(UIComponent) its children for deletion.</li>
82       * <li>If <i>not</i> found, call #createComponent(FaceletContext).
83       * <ol>
84       * <li>Only here do we apply ObjectHandler#setAttributes(FaceletContext, Object)</li>
85       * <li>Set the UIComponent's id</li>
86       * <li>Set the RendererType of this instance</li>
87       * </ol>
88       * </li>
89       * <li>Now apply the nextHandler, passing the UIComponent we've created/found.</li>
90       * <li>Now add the UIComponent to the passed parent</li>
91       * <li>Lastly, if the UIComponent already existed (found), then #finalizeForDeletion(UIComponent)
92       * for deletion.</li>
93       * </ol>
94       * 
95       * See jakarta.faces.view.facelets.FaceletHandler#apply(jakarta.faces.view.facelets.FaceletContext,
96       *  jakarta.faces.component.UIComponent)
97       * 
98       * @throws TagException
99       *             if the UIComponent parent is null
100      */
101     public final void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, ELException
102     {
103         // make sure our parent is not null
104         if (parent == null)
105         {
106             throw new TagException(this.tag, "Parent UIComponent was null");
107         }
108 
109         // possible facet scoped
110         String facetName = this.getFacetName(ctx, parent);
111 
112         // our id
113         String id = ctx.generateUniqueId(this.tagId);
114 
115         // grab our component
116         UIComponent c = ComponentSupport.findChildByTagId(parent, id);
117         boolean componentFound = false;
118         if (c != null)
119         {
120             componentFound = true;
121             // mark all children for cleaning
122             if (log.isLoggable(Level.FINE))
123             {
124                 log.fine(this.tag + " Component[" + id + "] Found, marking children for cleanup");
125             }
126             ComponentSupport.markForDeletion(c);
127         }
128         else
129         {
130             c = this.createComponent(ctx);
131             if (log.isLoggable(Level.FINE))
132             {
133                 log.fine(this.tag + " Component[" + id + "] Created: " + c.getClass().getName());
134             }
135             this.setAttributes(ctx, c);
136 
137             // mark it owned by a facelet instance
138             c.getAttributes().put(ComponentSupport.MARK_CREATED, id);
139 
140             // assign our unique id
141             if (this.id != null)
142             {
143                 c.setId(this.id.getValue(ctx));
144             }
145             else
146             {
147                 UIViewRoot root = ComponentSupport.getViewRoot(ctx, parent);
148                 if (root != null)
149                 {
150                     String uid = root.createUniqueId();
151                     c.setId(uid);
152                 }
153             }
154 
155             if (this.rendererType != null)
156             {
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         {
170             ComponentSupport.finalizeForDeletion(c);
171 
172             if (facetName == null)
173             {
174                 parent.getChildren().remove(c);
175             }
176         }
177 
178         this.onComponentPopulated(ctx, c, parent);
179 
180         // add to the tree afterwards
181         // this allows children to determine if it's
182         // been part of the tree or not yet
183         if (facetName == null)
184         {
185             parent.getChildren().add(c);
186         }
187         else
188         {
189             parent.getFacets().put(facetName, c);
190         }
191     }
192 
193     /**
194      * Return the Facet name we are scoped in, otherwise null
195      * 
196      * @param ctx
197      * @return
198      */
199     protected final String getFacetName(FaceletContext ctx, UIComponent parent)
200     {
201         // TODO: REFACTOR - "facelets.FACET_NAME" should be a constant somewhere, used to be in FacetHandler
202         //                  from real Facelets
203         return (String) parent.getAttributes().get("facelets.FACET_NAME");
204     }
205 
206     /**
207      * If the binding attribute was specified, use that in conjuction with our componentType String variable to call
208      * createComponent on the Application, otherwise just pass the componentType String. <p> If the binding was used,
209      * then set the ValueExpression "binding" on the created UIComponent.</p>
210      * 
211      * See Application#createComponent(jakarta.faces.el.ValueBinding, jakarta.faces.context.FacesContext,
212      *      java.lang.String)
213      * See Application#createComponent(java.lang.String)
214      * @param ctx
215      *            FaceletContext to use in creating a component
216      * @return
217      */
218     protected UIComponent createComponent(FaceletContext ctx)
219     {
220         UIComponent c = null;
221         FacesContext faces = ctx.getFacesContext();
222         Application app = faces.getApplication();
223         if (this.binding != null)
224         {
225             ValueExpression ve = this.binding.getValueExpression(ctx, Object.class);
226             
227             c = app.createComponent(ve, faces, this.componentType);
228             if (c != null)
229             {
230                 c.setValueExpression("binding", ve);
231             }
232         }
233         else
234         {
235             c = app.createComponent(this.componentType);
236         }
237         return c;
238     }
239 
240     /**
241      * If the id TagAttribute was specified, get it's value, otherwise generate a unique id from our tagId.
242      * 
243      * See TagAttribute#getValue(FaceletContext)
244      * @param ctx
245      *            FaceletContext to use
246      * @return what should be a unique Id
247      */
248     protected String getId(FaceletContext ctx)
249     {
250         if (this.id != null)
251         {
252             return this.id.getValue(ctx);
253         }
254         return ctx.generateUniqueId(this.tagId);
255     }
256 
257     @Override
258     protected MetaRuleset createMetaRuleset(Class type)
259     {
260         /*MetaRuleset m = super.createMetaRuleset(type);
261 
262         // ignore standard component attributes
263         m.ignore("binding").ignore("id");
264 
265         // add auto wiring for attributes
266         m.addRule(ComponentRule.Instance);
267 
268         // if it's an ActionSource
269         if (ActionSource.class.isAssignableFrom(type))
270         {
271             m.addRule(ActionSourceRule.Instance);
272         }
273 
274         // if it's a ValueHolder
275         if (ValueHolder.class.isAssignableFrom(type))
276         {
277             m.addRule(ValueHolderRule.Instance);
278 
279             // if it's an EditableValueHolder
280             if (EditableValueHolder.class.isAssignableFrom(type))
281             {
282                 m.ignore("submittedValue");
283                 m.ignore("valid");
284                 m.addRule(EditableValueHolderRule.Instance);
285             }
286         }
287 
288         return m;*/
289         
290         // FIXME: Implement correctly
291         // temporally restore code
292         MetaRuleset m = new MetaRulesetImpl(this.tag, type);
293         // ignore standard component attributes
294         m.ignore("binding").ignore("id");
295 
296         // add auto wiring for attributes
297         m.addRule(ComponentRule.INSTANCE);
298 
299         // if it's an ActionSource
300         if (ActionSource.class.isAssignableFrom(type))
301         {
302             m.addRule(ActionSourceRule.INSTANCE);
303         }
304 
305         // if it's a ValueHolder
306         if (ValueHolder.class.isAssignableFrom(type))
307         {
308             m.addRule(ValueHolderRule.INSTANCE);
309 
310             // if it's an EditableValueHolder
311             if (EditableValueHolder.class.isAssignableFrom(type))
312             {
313                 m.ignore("submittedValue");
314                 m.ignore("valid");
315                 m.addRule(EditableValueHolderRule.INSTANCE);
316             }
317         }
318         
319         return m;
320     }
321 
322     /**
323      * A hook method for allowing developers to do additional processing once Facelets creates the component. The
324      * 'setAttributes' method is still perferred, but this method will provide the parent UIComponent before it's been
325      * added to the tree and before any children have been added to the newly created UIComponent.
326      * 
327      * @param ctx
328      * @param c
329      * @param parent
330      */
331     protected void onComponentCreated(FaceletContext ctx, UIComponent c, UIComponent parent)
332     {
333         // do nothing
334     }
335 
336     protected void onComponentPopulated(FaceletContext ctx, UIComponent c, UIComponent parent)
337     {
338         // do nothing
339     }
340 
341     protected void applyNextHandler(FaceletContext ctx, UIComponent c) throws IOException, FacesException, ELException
342     {
343         // first allow c to get populated
344         this.nextHandler.apply(ctx, c);
345     }
346 }