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