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  
20  package org.apache.myfaces.orchestra.flow.components;
21  
22  import java.io.IOException;
23  import java.util.Iterator;
24  
25  import javax.el.ELException;
26  import javax.faces.FacesException;
27  import javax.faces.component.UIComponent;
28  
29  import com.sun.facelets.FaceletContext;
30  import com.sun.facelets.tag.TextHandler;
31  import com.sun.facelets.tag.jsf.ComponentConfig;
32  import com.sun.facelets.tag.jsf.ComponentHandler;
33  
34  /**
35   * Facelets component-handler which creates a FlowCallComponent instance.
36   * <p>
37   * Note that a facelets ComponentHandler is a subclass of TagHandler. The methods in TagHandler
38   * are "tag oriented", ie focused on handling a tree of tags. The methods in a ComponentHandler
39   * are "higher level", ie the callbacks are focused on component creation and configuration.
40   * <p>
41   * Facelets first parses the input into a syntax tree (of Facelets Instruction nodes) and caches
42   * it (for later use). It then walks the tree, invoking the appropriate tag or component handler.
43   * Each ComponentHandler has access to the list of nodes nested within it via the "nextHandler"
44   * member.
45   */
46  public class FlowCallComponentHandler extends ComponentHandler
47  {
48      public FlowCallComponentHandler(ComponentConfig config)
49      {
50          super(config);
51      }
52  
53      /**
54       * When Facelets encounters the start tag for this component, it will create the component and assign any
55       * attributes. It then calls this method.
56       * <p>
57       * Here we ask the nodes nested within this element to return their text. Then the nested text is passed
58       * to the new component. This has the same effect as a JSP tag overriding "doAfterBody", but is pull-style
59       * rather than waiting for a callback to occur. Note, however that the body of this element *has* already
60       * been parsed into instruction nodes; there appears to be no way to stop Facelets doing this.
61       * <p>
62       * Note that even though we process the body text explicitly here, facelets will try to process the nested
63       * Instruction nodes after this method has returned. Therefore the applyNextHandler method is overridden
64       * to prevent that.
65       */
66      @Override
67      protected void onComponentCreated(FaceletContext ctx, UIComponent c, UIComponent parent)
68      {
69          StringBuffer content = new StringBuffer();
70  
71          // Method findNextByType will scan the list of child "nodes" for the "StartElementInstruction" node
72          // that triggered the call to this method. We actually expect just one node (of type Instruction)
73          // which implements the TextHandler interface. It will internally have broken down the XML content
74          // of the flowCall element into separate nodes for the xml elements, attributes and body-text.
75          // However we don't need to know that; just calling getText on that top-level child will cause
76          // it to append together the text representations of all the child elements and return the complete
77          // string.
78          //
79          // The loop is just in case there are multiple child Instruction objects for some reason; this is
80          // not expected. The iterator always stops (hasNext is false) when there are no more child nodes
81          // for this flowCall element.
82          content.append("<flowCall>\n");
83          Iterator<?> iter = findNextByType(TextHandler.class);
84          while (iter.hasNext())
85          {
86              TextHandler text = (TextHandler) iter.next();
87  
88              // Fetch the text without evaluating any EL expressions.
89              // Note that text.getText(ctx) *would* evaluate EL expressions
90              String textBlock = text.getText();
91  
92              content.append(textBlock);
93          }
94          content.append("</flowCall>");
95  
96          ((FlowCallComponent) c).setBody(ctx, content.toString());
97      }
98      
99      @Override
100     protected void onComponentPopulated(FaceletContext ctx, UIComponent c, UIComponent parent)
101     {
102         // do nothing
103     }
104 
105     @Override
106     protected void applyNextHandler(FaceletContext ctx, UIComponent c) 
107             throws IOException, FacesException, ELException
108     {
109         // Do nothing.
110         //
111         // This method is called by the Facelets framework immediately after onComponentCreated
112         // is called.
113         //
114         // The normal behaviour is to call this.nextHandler.apply(ctx, c) in order to iterate over
115         // the child instructions of this element and process them. But we don't want this as out
116         // onComponentCreated method has already extracted all of the data we wanted. Without
117         // this override, transient text children would be added to component "c".
118         //
119         // It would be better if we could tell Facelets to not create Instruction nodes for the
120         // nested content of this element at all; then it would not be necessary to override
121         // this method as there *would* be no nested Instruction nodes. But I haven't figured
122         // out how to do that. Facelets is of course driven by a normal XML parser that will
123         // always break down the input into SAX events anyway... 
124     }
125 }