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 }