2009/05/20 - Apache Shale has been retired.
For more information, please explore the Attic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.shale.clay.config.beans;
22
23 import java.util.BitSet;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.TreeMap;
27
28 import javax.servlet.ServletContext;
29
30 import org.apache.shale.clay.config.ClayTemplateParser;
31 import org.apache.shale.clay.config.Globals;
32
33 /***
34 * <p>The second type of top-level object pool. This implementation
35 * is designed to provide Tapestry like template composition. The
36 * top-level {@link ComponentBean} is materialized from a HTML fragment
37 * where HTML elements are bound to meta components defined in the
38 * XML configuration files and cached by an instance of {@link ComponentConfigBean}
39 * </p>
40 */
41 public class TemplateConfigBean extends ComponentConfigBean {
42
43 /***
44 * <p>Returns a {@link ComponentBean} that is materialized
45 * using a HTML template fragment. The <code>templateName</code>
46 * is the name of the file relative to the context root of the
47 * web application</p>
48 * @param templateName name of the markup template
49 * @return root component bean for the <code>templateName</code>
50 */
51 public ComponentBean getElement(String templateName) {
52
53 StringBuffer jsfid = new StringBuffer(templateName);
54 if (jsfid.length() > 0 && jsfid.charAt(0) != '/') {
55 jsfid.insert(0, '/');
56 }
57
58
59 WatchDog watchDog = (WatchDog) watchDogs.get(jsfid.toString());
60
61
62 if (watchDog == null) {
63 watchDog = (WatchDog) watchDogs.get(Globals.DEFAULT_COMPONENT_CONFIG_WATCHDOG);
64 }
65
66 if (watchDog == null || super.getElement(jsfid.toString()) == null) {
67
68
69 watchDog = new WatchDog(getConfigDefinitions(jsfid.toString()), jsfid.toString());
70
71
72 watchDogs.put(watchDog.getName(), watchDog);
73
74
75
76 watchDog.refresh(false);
77 } else {
78
79
80 watchDog.refresh(false);
81 }
82
83
84 return super.getElement(jsfid.toString());
85 }
86
87
88 /***
89 * <p>Returns an integer value use to order the registered {@link ConfigBean} instances
90 * with the {@link ConfigBeanFactory}.
91 * </p>
92 *
93 * @return weight value of <code>1</code>
94 */
95 public int getWeight() {
96 return 1;
97 }
98
99 /***
100 * <p>Overrides the super call to change the condition of the filter. This
101 * {@link ConfigBean} can create components where the id end in the suffix
102 * defined in the web deployment descriptor as a initialization parameter with
103 * the name defined by <code>Globals.CLAY_HTML_TEMPLATE_SUFFIX</code> Or, using
104 * the default defined by <code>Globals.CLAY_DEFAULT_HTML_TEMPLATE_SUFFIX</code>
105 *
106 * @param id jsfid
107 * @return <code>true</code> if the <code>jsfid</code> can be handled here
108 */
109 public boolean validMoniker(String id) {
110 return id.endsWith(suffixes[0]);
111 }
112
113 /***
114 * <p>This is an overridden method called from the init method.
115 * It loads an instance of the {@link ClayTemplateParser} and
116 * establishes a Map collection to hold the resource
117 * {@link org.apache.shale.clay.config.beans.ComponentConfigBean$WatchDog}'s.</p>
118 */
119 protected void loadConfigFiles() {
120 parser = new ClayTemplateParser();
121 parser.setConfig(this);
122
123 watchDogs = new TreeMap();
124 }
125
126
127 /***
128 * <p>The HTML templates are loaded on-demand. Override this method forcing the auto
129 * load option on. The XML configuration files are only effected by the
130 * <code>auto-reload-clay-files</code> initialization parameter.</p>
131 *
132 * @param context web container servlet context
133 */
134 public void init(ServletContext context) {
135 super.init(context);
136
137 isWatchDogOn = true;
138 }
139
140
141 /***
142 * <p>If the <code>watchDogName</code> equals
143 * the {@link ComponentBean} that defines the selected
144 * template, remove it.</p>
145 *
146 * @param watchDogName grouping of template files
147 */
148 protected void clear(String watchDogName) {
149 if (!watchDogName.equals(Globals.DEFAULT_COMPONENT_CONFIG_WATCHDOG)) {
150
151 ComponentBean b = (ComponentBean) displayElements.get(watchDogName);
152 displayElements.remove(watchDogName);
153 if (b != null) {
154 try {
155 unassignParent(b);
156 } catch (RuntimeException e1) {
157
158 }
159 }
160 b = null;
161 } else {
162 super.clear(watchDogName);
163 }
164 }
165
166
167 /***
168 * <p>If the <code>forceReload</code> is <code>true</code>,
169 * the <code>displayElements</code> cache is invalidated.
170 * A <code>true</code> value is returned if cache has
171 * been cleared.</p>
172 *
173 * @param forceReload invalidate the cache flag
174 * @return <code>true</code> if the templates were reloaded
175 */
176 public boolean refresh(boolean forceReload) {
177 if (forceReload) {
178
179
180
181 Iterator wi = watchDogs.entrySet().iterator();
182 while (wi.hasNext()) {
183 Map.Entry e = (Map.Entry) wi.next();
184 WatchDog watchDog = (WatchDog) e.getValue();
185 clear(watchDog.getName());
186 if (watchDog != null) {
187 watchDog.destroy();
188 }
189
190 }
191 watchDogs.clear();
192
193 }
194
195 return forceReload;
196 }
197
198
199 /***
200 * <p>
201 * Returns the root metadata component that is used to add to the component
202 * tree. It locates the {@link ComponentBean} using the <code>jsfid</code>
203 * attribute as the key. A call to the {@link ConfigBeanFactory} locates the
204 * correct {@link ConfigBean} used to find the {@link ComponentBean}. </p>
205 *
206 * @param jsfid parent id of a config bean
207 * @return parent config bean
208 */
209 protected ComponentBean getTopLevelElement(String jsfid) {
210
211 if (validMoniker(jsfid)) {
212 return getElement(jsfid);
213 }
214
215
216 ConfigBean config = ConfigBeanFactory.findConfig(jsfid);
217
218 if (config == null) {
219 throw new NullPointerException(messages
220 .getMessage("config.notloaded", new Object[] { jsfid }));
221 }
222
223 if (config == this) {
224 throw new NullPointerException(messages.getMessage(
225 "jsfid.notfound", new Object[] { jsfid }));
226 }
227
228
229 ComponentBean b = config.getElement(jsfid);
230 if (b == null) {
231 throw new NullPointerException(messages.getMessage(
232 "jsfid.notfound", new Object[] { jsfid }));
233 }
234
235 return b;
236 }
237
238
239 /***
240 * <p>Determines if the <code>node</code> is a transient
241 * <code>outputText</code> (<strong>verbatim</strong>) component.</p>
242 *
243 * @param node a config bean that represents a template token
244 * @return <code>true</code> if the node is a verbatim node
245 */
246 private boolean isVerbatim(ComponentBean node) {
247
248 AttributeBean attr = null;
249 if ((node.getJsfid().equals("verbatim") || node.getJsfid().equals("f:verbatim"))
250 && node.getComponentType().equals("javax.faces.HtmlOutputText")) {
251 attr = node.getAttribute("isTransient");
252 if (attr != null) {
253 if (attr.getValue() != null && attr.getValue().length() > 0) {
254 return (Character.toLowerCase(attr.getValue().charAt(0)) == 't');
255 }
256 }
257 }
258
259 return false;
260 }
261
262
263 /***
264 * <p>Recursively walks down the graph of meta-data {@link ComponentBean}'s
265 * looking at the children of the <code>root</code>. Adjacent
266 * children that are both <code>verbatim</code> component
267 * definitions are merged. If there is only one child and
268 * the child and root nodes are both <code>verbatim</code>
269 * definitions, the child is merged up to the root.</p>
270 *
271 * @param root top config bean that represents a markup template
272 */
273 public void optimizeTree(ComponentBean root) {
274
275
276 int size = root.getChildren().size();
277 ComponentBean[] children = new ComponentBean[size];
278 BitSet verbatimSet = new BitSet(size);
279 verbatimSet.clear(0, size);
280
281 StringBuffer buff = new StringBuffer();
282
283 int i = 0;
284 Iterator ci = root.getChildrenIterator();
285 while (ci.hasNext()) {
286 children[i] = (ComponentBean) ci.next();
287 if (isVerbatim(children[i])) {
288 verbatimSet.set(i);
289 }
290
291 if (children[i].getChildren().size() > 0) {
292 optimizeTree(children[i]);
293
294 }
295
296 i++;
297 }
298
299 int s = -1;
300 while ((s = verbatimSet.nextSetBit(++s)) > -1) {
301
302 merge: for (int j = s + 1; j < children.length; j++) {
303 if (verbatimSet.get(j)) {
304 buff.setLength(0);
305
306
307
308 AttributeBean attrTop = children[s].getAttribute("value");
309 if (attrTop != null) {
310 if (attrTop.getValue() != null) {
311 buff.append(attrTop.getValue());
312 }
313 } else {
314 break merge;
315 }
316
317 AttributeBean attrNext = children[j].getAttribute("value");
318 if (attrNext != null) {
319 if (attrNext.getValue() != null) {
320 buff.append(attrNext.getValue());
321 }
322 } else {
323 continue merge;
324 }
325
326 attrTop.setValue(buff.toString());
327 root.getChildren().remove(children[j]);
328
329 } else {
330
331 s = j;
332 break merge;
333 }
334 }
335
336
337 }
338
339
340
341 if (isVerbatim(root) && root.getChildren().size() == 1
342 && isVerbatim(children[0])) {
343
344 buff.setLength(0);
345
346
347
348 AttributeBean attrTop = root.getAttribute("value");
349 if (attrTop != null) {
350 if (attrTop.getValue() != null) {
351 buff.append(attrTop.getValue());
352 }
353
354 AttributeBean attrNext = children[0].getAttribute("value");
355 if (attrNext != null) {
356 if (attrNext.getValue() != null) {
357 buff.append(attrNext.getValue());
358 }
359 }
360
361 attrTop.setValue(buff.toString());
362 root.getChildren().clear();
363 }
364 }
365
366
367 }
368
369 }