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.component.chain;
22
23 import java.net.URL;
24 import java.util.ArrayList;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.TreeMap;
30 import java.util.TreeSet;
31
32 import org.apache.commons.chain.Catalog;
33 import org.apache.commons.chain.CatalogFactory;
34 import org.apache.commons.chain.Command;
35 import org.apache.commons.chain.Context;
36 import org.apache.commons.chain.config.ConfigParser;
37 import org.apache.shale.clay.config.Globals;
38 import org.apache.shale.clay.config.beans.SymbolBean;
39 import org.apache.shale.util.Messages;
40 import org.apache.shale.util.Tags;
41
42 /***
43 * <p>
44 * The base class for the commands that create the component tree.
45 * </p>
46 */
47 public abstract class AbstractCommand implements Command {
48
49 /***
50 * <p>
51 * Message resources for this class.
52 * </p>
53 */
54 private static Messages messages = new Messages(
55 "org.apache.shale.clay.Bundle", AbstractCommand.class
56 .getClassLoader());
57
58 /***
59 * @return message resources
60 */
61 protected static Messages getMessages() {
62 return messages;
63 }
64
65 /***
66 * <p>Shale tag helper class that contains utility methods for setting
67 * component binding and method properties.</p>
68 */
69 private Tags tagUtils = new Tags();
70
71 /***
72 * @return Shale tag helper class that contains utility methods for setting
73 * component binding and method properties.
74 */
75 protected Tags getTagUtils() {
76 return tagUtils;
77 }
78
79 /***
80 * <p>
81 * Returns the {@link Catalog} with a name of
82 * <code>Globals.CLAY_CATALOG_NAME</code> in the
83 * <code>Globals.CLAY_RESOURCE_NAME</code> configuration file.
84 * </p>
85 *
86 * @return commons chains catalog
87 * @exception Exception finding catalog
88 */
89 protected Catalog getCatalog() throws Exception {
90
91 Catalog catalog = CatalogFactory.getInstance().getCatalog(
92 Globals.CLAY_CATALOG_NAME);
93 if (catalog == null) {
94
95 ConfigParser parser = new ConfigParser();
96 URL url = this.getClass().getClassLoader().getResource(
97 Globals.CLAY_RESOURCE_NAME);
98 if (url == null) {
99 throw new IllegalArgumentException(Globals.CLAY_RESOURCE_NAME);
100 }
101 parser.parse(url);
102
103 catalog = CatalogFactory.getInstance().getCatalog(
104 Globals.CLAY_CATALOG_NAME);
105
106 }
107
108 return catalog;
109
110 }
111
112 /***
113 * <p>
114 * Returns the {@link Catalog} with a name identified by the
115 * constant <code>Globals.CLAY_CUSTOMIZATION_CATALOG_NAME</code>.
116 * </p>
117 *
118 * @return commons chains customizations catalog
119 * @exception Exception finding customizations catalog
120 */
121 protected Catalog getCustomizationCatalog() throws Exception {
122
123 Catalog catalog = CatalogFactory.getInstance().getCatalog(
124 Globals.CLAY_CUSTOMIZATION_CATALOG_NAME);
125
126 return catalog;
127 }
128
129 /***
130 * <p>
131 * This call is used to substitue an attribute binding expression containing
132 * the <code>symbols</code> with the target property value in the {@link ClayContext}.
133 * The current attribute within the context is assumed.
134 * </p>
135 *
136 * @param context holding the symbols and the target attribute
137 * @return string with the symbols replaces
138 */
139 public static String replaceMnemonic(ClayContext context) {
140 return replaceMnemonic(context, context.getAttribute().getValue());
141 }
142
143 /***
144 * <p>Evaluates nested symbols. These are symbols that have references to other
145 * symbols as their values. The nested symbols evaluation is sensitive to dependencies.
146 * The current scoped symbol table is found in the {@link ClayContext}.</p>
147 *
148 * @param context holding the symbols
149 */
150 public static void realizeSymbols(ClayContext context) {
151 Map symbols = context.getSymbols();
152 if (symbols == null || symbols.isEmpty()) {
153 return;
154 }
155
156 Iterator si = symbols.entrySet().iterator();
157
158 Map dependenciesMap = new TreeMap();
159 TreeSet allNestedSymbols = new TreeSet();
160
161 while (si.hasNext()) {
162 Map.Entry e = (Map.Entry) si.next();
163 SymbolBean symbol = (SymbolBean) e.getValue();
164 if (symbol.getValue() != null) {
165 List symbolDependencies = findSymbols(context, symbol.getValue());
166 if (!symbolDependencies.isEmpty()) {
167 dependenciesMap.put(symbol.getName(), symbolDependencies);
168 allNestedSymbols.addAll(symbolDependencies);
169 allNestedSymbols.add(symbol.getName());
170 }
171 }
172 }
173
174 List allNestedSymbolsOrdered = getOrderedByDependencies(allNestedSymbols, dependenciesMap);
175 for (int i = 0; i < allNestedSymbolsOrdered.size(); i++) {
176 String symbolName = (String) allNestedSymbolsOrdered.get(i);
177 SymbolBean sourceSymbol = (SymbolBean) symbols.get(symbolName);
178 if (sourceSymbol != null && sourceSymbol.getValue() != null) {
179 String value = replaceMnemonic(context, sourceSymbol.getValue());
180 SymbolBean targetSymbol = new SymbolBean();
181 targetSymbol.setDescription(sourceSymbol.getDescription());
182 targetSymbol.setName(sourceSymbol.getName());
183 targetSymbol.setValue(value);
184
185 symbols.put(targetSymbol.getName(), targetSymbol);
186 }
187 }
188
189 }
190
191 /***
192 * <p>Orders the symbols by their dependencies. Each symbol
193 * can have multiple dependencies.</p>
194 *
195 * @param allNestedSymbols Set of symbol names
196 * @param dependenciesMap List of dependencies for each symbol
197 * @return ordered list of symbol names ordered by dependencies
198 */
199 private static List getOrderedByDependencies(Set allNestedSymbols, Map dependenciesMap) {
200
201 List tmpList = new ArrayList(allNestedSymbols);
202
203 ordered: for (int i = 0; i < tmpList.size(); i++) {
204 boolean swap = false;
205 for (int j = 0; j < tmpList.size(); j++) {
206 String symbolName = (String) tmpList.get(j);
207 List symbolDependencies = (List) dependenciesMap.get(symbolName);
208 if (symbolDependencies != null && symbolDependencies.size() > 0) {
209 int max = -1;
210 for (int n = 0; n < symbolDependencies.size(); n++) {
211 max = Math.max(max, tmpList.indexOf(symbolDependencies.get(n)));
212 }
213 if (max > j) {
214 String tmp = (String) tmpList.get(j);
215 tmpList.remove(j);
216 tmpList.add(max, tmp);
217 swap = true;
218 j = max;
219 }
220 }
221
222 }
223 if (!swap) {
224 break ordered;
225 }
226 }
227
228 return tmpList;
229
230 }
231
232 /***
233 * <p>Returns a List of symbols found within the <code>symbolToken</code>.
234 * The comprehensive symbol table is found in the {@link ClayContext}.</p>
235 *
236 * @param context commons chains
237 * @param symbolToken target token having nested symbols
238 * @return list of nested symbols withing the target symbolToken
239 */
240 private static List findSymbols(ClayContext context, String symbolToken) {
241
242 List targetList = new ArrayList();
243
244 StringBuffer buff = new StringBuffer(symbolToken);
245 Map symbols = context.getSymbols();
246 Iterator si = symbols.entrySet().iterator();
247 boolean wasSymbolFound = false;
248 int i = buff.indexOf("@");
249 replace: while (i > -1 && si.hasNext()) {
250 Map.Entry e = (Map.Entry) si.next();
251 SymbolBean symbol = (SymbolBean) e.getValue();
252 String key = symbol.getName();
253 i = (wasSymbolFound ? buff.indexOf("@") : i);
254 if (i == -1) {
255 break replace;
256 }
257
258 next: while (i > -1 && i <= (buff.length() - key.length())) {
259
260 int n = -1;
261 indexOf: for (int s = i; s <= (buff.length() - key.length()); s++) {
262 for (int c = 0; c < key.length(); c++) {
263 char skey = Character.toLowerCase(key.charAt(c));
264 char tkey = Character.toLowerCase(buff.charAt(s + c));
265 if (skey != tkey) {
266 continue indexOf;
267 }
268 }
269
270 n = s;
271 break indexOf;
272 }
273
274 if (n > -1) {
275 if (!targetList.contains(key)) {
276 targetList.add(key);
277 }
278 i = n + key.length();
279 wasSymbolFound = true;
280 } else {
281 break next;
282 }
283 }
284
285 e = null;
286 key = null;
287 }
288 symbols = null;
289 si = null;
290
291 return targetList;
292 }
293
294 /***
295 * <p>
296 * This call is used to substitue an attribute binding expression containing
297 * the <code>symbols</code> within the <code>sybmolToken</code>.
298 * </p>
299 *
300 * @param context commons chains holding the substitution symbols
301 * @param symbolToken target token having nested symbols
302 * @return value with the symbols replaced
303 */
304 public static String replaceMnemonic(ClayContext context, String symbolToken) {
305
306 StringBuffer buff = new StringBuffer(symbolToken);
307 Map symbols = context.getSymbols();
308 Iterator si = symbols.entrySet().iterator();
309 boolean wasReplacementMade = false;
310 int i = buff.indexOf("@");
311 replace: while (i > -1 && si.hasNext()) {
312 Map.Entry e = (Map.Entry) si.next();
313 SymbolBean symbol = (SymbolBean) e.getValue();
314 String key = symbol.getName();
315 String value = (symbol.getValue() == null ? "" : symbol.getValue());
316 i = (wasReplacementMade ? buff.indexOf("@") : i);
317 if (i == -1) {
318 break replace;
319 }
320
321 next: while (i > -1 && i <= (buff.length() - key.length())) {
322
323 int n = -1;
324 indexOf: for (int s = i; s <= (buff.length() - key.length()); s++) {
325 for (int c = 0; c < key.length(); c++) {
326 char skey = Character.toLowerCase(key.charAt(c));
327 char tkey = Character.toLowerCase(buff.charAt(s + c));
328 if (skey != tkey) {
329 continue indexOf;
330 }
331 }
332
333 n = s;
334 break indexOf;
335 }
336
337 if (n > -1) {
338 buff.delete(n, n + key.length());
339 buff.insert(n, value);
340 i = n + value.length();
341 wasReplacementMade = true;
342 } else {
343 break next;
344 }
345 }
346
347 e = null;
348 key = null;
349 value = null;
350 }
351 symbols = null;
352 si = null;
353
354
355
356
357
358 if (buff.length() == 0 && wasReplacementMade) {
359 return null;
360 } else {
361 return buff.toString();
362 }
363
364 }
365
366 /***
367 * <p>
368 * This method comes from the <code>Command</code> interfaces. This method is
369 * invoked while executing the <code>Chain</code>.
370 * </p>
371 *
372 * @param context commons chains
373 * @return <code>true</code> if the chain is done
374 * @exception Exception checked
375 */
376 public abstract boolean execute(Context context) throws Exception;
377
378 /***
379 * <p>Return true if the specified string contains an EL expression.</p>
380 *
381 * <p>This is taken almost verbatim from {@link javax.faces.webapp.UIComponentTag}
382 * in order to remove JSP dependencies from the renderers.</p>
383 *
384 * @param value String to be checked for being an expression
385 * @return <code>true</code> if the value is a binding expression
386 */
387 protected boolean isValueReference(String value) {
388
389 if (value == null) {
390 return false;
391 }
392
393 int start = value.indexOf("#{");
394 if (start < 0) {
395 return false;
396 }
397
398 int end = value.lastIndexOf('}');
399 return (end >= 0) && (start < end);
400 }
401
402 }
403