1 package org.apache.velocity.tools.generic;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 import org.apache.velocity.tools.config.DefaultKey;
27 import org.apache.velocity.tools.generic.SafeConfig;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 @DefaultKey("mark")
65 public class MarkupTool extends SafeConfig
66 {
67 public static final String DEFAULT_TAB = " ";
68 public static final String DEFAULT_DELIMITER = " ";
69
70 private String tab = DEFAULT_TAB;
71 private String delim = DEFAULT_DELIMITER;
72
73 public void setTab(String tab)
74 {
75 if (isConfigLocked())
76 {
77
78 }
79 else
80 {
81 this.tab = tab;
82 }
83 }
84
85 public String getTab()
86 {
87 return this.tab;
88 }
89
90 public Tag get(String tag)
91 {
92 return tag(tag);
93 }
94
95 public Tag tag(String definition)
96 {
97 String[] tags = split(definition);
98 Tag last = null;
99 for (int i=0; i < tags.length; i++)
100 {
101 Tag tag = parse(tags[i]);
102 if (last != null)
103 {
104 last.append(tag);
105 }
106 last = tag;
107 }
108 return last;
109 }
110
111 protected String[] split(String me)
112 {
113
114 return me.split(delim);
115 }
116
117 private static enum Mode { NAME, ID, CLASS, ATTR }
118 protected Tag parse(String definition)
119 {
120 StringBuilder store = new StringBuilder();
121 Tag tag = new Tag(this);
122 Mode mode = Mode.NAME;
123 for (int i=0; i < definition.length(); i++)
124 {
125 char c = definition.charAt(i);
126 if (c == '#')
127 {
128 store = clear(mode, tag, store, true);
129 mode = Mode.ID;
130 }
131 else if (c == '.')
132 {
133 store = clear(mode, tag, store, true);
134 mode = Mode.CLASS;
135 }
136 else if (c == '[')
137 {
138 store = clear(mode, tag, store, true);
139 mode = Mode.ATTR;
140 }
141 else if (c == ']')
142 {
143 store = clear(mode, tag, store, true);
144 mode = Mode.NAME;
145 }
146 else
147 {
148 store.append(c);
149 }
150 }
151 clear(mode, tag, store, false);
152 return tag;
153 }
154 private StringBuilder clear(Mode mode, Tag tag, StringBuilder val, boolean emptyStore)
155 {
156 if (val.length() > 0)
157 {
158 String s = val.toString();
159 switch (mode)
160 {
161 case NAME:
162 tag.name(s);
163 break;
164 case ID:
165 tag.id(s);
166 break;
167 case CLASS:
168 tag.addClass(s);
169 break;
170 case ATTR:
171 if (s.indexOf('=') > 0)
172 {
173 String[] kv = s.split("=");
174 tag.attr(kv[0], kv[1]);
175 }
176 else
177 {
178 tag.attr(s, null);
179 }
180 break;
181 }
182 if (emptyStore)
183 {
184 return new StringBuilder();
185 }
186 return val;
187 }
188 else
189 {
190
191 return val;
192 }
193 }
194
195 public static class Tag
196 {
197 private MarkupTool tool;
198 private Tag parent;
199 private Object name;
200 private Object id;
201 private List<Object> classes;
202 private Map<Object,Object> attributes;
203 private List<Object> children;
204
205 public Tag(MarkupTool tool)
206 {
207 this.tool = tool;
208 }
209
210 public Tag name(Object name)
211 {
212 this.name = name;
213 return this;
214 }
215
216 public Tag id(Object id)
217 {
218 this.id = id;
219 return this;
220 }
221
222 public Tag addClass(Object c)
223 {
224 if (c == null)
225 {
226 return null;
227 }
228
229 if (classes == null)
230 {
231 classes = new ArrayList<Object>();
232 }
233 classes.add(c);
234 return this;
235 }
236
237 public Tag attr(Object k, Object v)
238 {
239 if (k == null)
240 {
241 return null;
242 }
243 if (attributes == null)
244 {
245 attributes = new HashMap<Object,Object>();
246 }
247 attributes.put(k, v);
248 return this;
249 }
250
251 public Tag body(Object o)
252 {
253 if (children == null)
254 {
255 children = new ArrayList<Object>();
256 }
257 else
258 {
259 children.clear();
260 }
261 children.add(o);
262 return this;
263 }
264
265 public Tag append(Object o)
266 {
267 if (children == null)
268 {
269 children = new ArrayList<Object>();
270 }
271 children.add(o);
272 if (o instanceof Tag)
273 {
274 ((Tag)o).parent(this);
275 }
276 return this;
277 }
278
279 public Tag prepend(Object o)
280 {
281 if (children == null)
282 {
283 children = new ArrayList<Object>();
284 children.add(o);
285 }
286 else
287 {
288 children.add(0, o);
289 }
290 if (o instanceof Tag)
291 {
292 ((Tag)o).parent(this);
293 }
294 return this;
295 }
296
297 public Tag wrap(String tag)
298 {
299
300 Tag prnt = tool.tag(tag);
301
302 prnt.root().parent(parent());
303
304 parent(prnt);
305 return this;
306 }
307
308 public Tag orphan()
309 {
310 return parent(null);
311 }
312
313 public Tag parent(Tag parent)
314 {
315 this.parent = parent;
316 return this;
317 }
318
319 public Tag parent()
320 {
321 return this.parent;
322 }
323
324 public Tag root()
325 {
326 if (isOrphan())
327 {
328 return this;
329 }
330 return this.parent.root();
331 }
332
333 public List<Object> children()
334 {
335 return children;
336 }
337
338 public boolean isOrphan()
339 {
340 return (parent == null);
341 }
342
343 public boolean isEmpty()
344 {
345 return (children == null || children().isEmpty());
346 }
347
348 public boolean matches(Tag tag)
349 {
350 if (missed(name, tag.name) ||
351 missed(id, tag.id) ||
352 missed(classes, tag.classes))
353 {
354 return false;
355 }
356
357 return true;
358 }
359
360 protected boolean missed(Object target, Object arrow)
361 {
362
363 if (arrow == null)
364 {
365 return false;
366 }
367
368 return !arrow.equals(target);
369 }
370
371 protected boolean missed(List<Object> targets, List<Object> arrows)
372 {
373
374 if (arrows == null)
375 {
376 return false;
377 }
378
379 if (targets == null)
380 {
381 return true;
382 }
383 for (Object o : arrows)
384 {
385 if (!targets.contains(o))
386 {
387 return true;
388 }
389 }
390 return false;
391 }
392
393
394
395
396 protected void render(String indent, StringBuilder s)
397 {
398 if (render_start(indent, s))
399 {
400 render_body(indent, s);
401 render_end(indent, s);
402 }
403 }
404
405 protected boolean render_start(String indent, StringBuilder s)
406 {
407 if (indent != null)
408 {
409 s.append(indent);
410 }
411 s.append('<');
412 render_name(s);
413 render_id(s);
414 render_classes(s);
415 render_attributes(s);
416 if (isEmpty())
417 {
418 s.append("/>");
419 return false;
420 }
421 else
422 {
423 s.append('>');
424 return true;
425 }
426 }
427
428 protected void render_name(StringBuilder s)
429 {
430 s.append(name == null ? "div" : name);
431 }
432
433 protected void render_id(StringBuilder s)
434 {
435 if (id != null)
436 {
437 s.append(" id=\"").append(id).append('"');
438 }
439 }
440
441 protected void render_classes(StringBuilder s)
442 {
443 if (classes != null)
444 {
445 s.append(" class=\"");
446 for (int i=0; i < classes.size(); i++)
447 {
448 s.append(classes.get(i));
449 if (i + 1 != classes.size())
450 {
451 s.append(' ');
452 }
453 }
454 s.append('"');
455 }
456 }
457
458 protected void render_attributes(StringBuilder s)
459 {
460 if (attributes != null)
461 {
462 for (Map.Entry<Object,Object> entry : attributes.entrySet())
463 {
464 s.append(' ').append(entry.getKey()).append("=\"");
465 if (entry.getValue() != null)
466 {
467 s.append(entry.getValue());
468 }
469 s.append('"');
470 }
471 }
472 }
473
474 protected void render_body(String indent, StringBuilder s)
475 {
476 String kidIndent = indent + tool.getTab();
477 for (Object o : children)
478 {
479 if (o instanceof Tag)
480 {
481 ((Tag)o).render(kidIndent, s);
482 }
483 else
484 {
485 s.append(kidIndent);
486 s.append(o);
487 }
488 }
489 }
490
491 protected void render_end(String indent, StringBuilder s)
492 {
493 if (indent != null)
494 {
495 s.append(indent);
496 }
497 s.append("</").append(name).append('>');
498 }
499
500 public String toString()
501 {
502 StringBuilder s = new StringBuilder();
503 root().render("\n", s);
504 return s.toString();
505 }
506 }
507 }