1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.view.facelets.compiler;
20
21 import java.util.ArrayList;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Stack;
25
26 import javax.el.ELException;
27 import javax.faces.application.FacesMessage;
28 import javax.faces.view.facelets.CompositeFaceletHandler;
29 import javax.faces.view.facelets.FaceletHandler;
30 import javax.faces.view.facelets.Tag;
31 import javax.faces.view.facelets.TagAttribute;
32 import javax.faces.view.facelets.TagException;
33
34 import org.apache.myfaces.shared.renderkit.html.HTML;
35 import org.apache.myfaces.view.facelets.el.ELText;
36
37
38
39
40
41
42 final class TextUnit extends CompilationUnit
43 {
44
45 private final StringBuffer buffer;
46
47 private final StringBuffer textBuffer;
48
49 private final List<Instruction> instructionBuffer;
50
51 private final Stack<Tag> tags;
52
53 private final List<Object> children;
54
55 private boolean startTagOpen;
56
57 private final String alias;
58
59 private final String id;
60
61 private final List<Object> messages;
62
63 private final boolean escapeInlineText;
64
65 private final boolean compressSpaces;
66
67 public TextUnit(String alias, String id)
68 {
69 this(alias,id,true);
70 }
71
72 public TextUnit(String alias, String id, boolean escapeInlineText)
73 {
74 this(alias,id,escapeInlineText,false);
75 }
76
77 public TextUnit(String alias, String id, boolean escapeInlineText, boolean compressSpaces)
78 {
79 this.alias = alias;
80 this.id = id;
81 this.buffer = new StringBuffer();
82 this.textBuffer = new StringBuffer();
83 this.instructionBuffer = new ArrayList<Instruction>();
84 this.tags = new Stack<Tag>();
85 this.children = new ArrayList<Object>();
86 this.startTagOpen = false;
87 this.messages = new ArrayList<Object>(4);
88 this.escapeInlineText = escapeInlineText;
89 this.compressSpaces = compressSpaces;
90 }
91
92 public FaceletHandler createFaceletHandler()
93 {
94 this.flushBufferToConfig(true);
95
96 if (this.children.size() == 0)
97 {
98 return LEAF;
99 }
100
101 FaceletHandler[] h = new FaceletHandler[this.children.size()];
102 Object obj;
103 for (int i = 0; i < h.length; i++)
104 {
105 obj = this.children.get(i);
106 if (obj instanceof FaceletHandler)
107 {
108 h[i] = (FaceletHandler) obj;
109 }
110 else
111 {
112 h[i] = ((CompilationUnit) obj).createFaceletHandler();
113 }
114 }
115 if (h.length == 1)
116 {
117 return h[0];
118 }
119 return new CompositeFaceletHandler(h);
120 }
121
122 private void addInstruction(Instruction instruction)
123 {
124 this.flushTextBuffer(false);
125 this.instructionBuffer.add(instruction);
126 }
127
128 private void flushTextBuffer(boolean child)
129 {
130 if (this.textBuffer.length() > 0)
131 {
132 String s = this.textBuffer.toString();
133
134 if (child)
135 {
136 s = trimRight(s);
137 }
138 if (s.length() > 0)
139 {
140 if (!compressSpaces)
141 {
142
143 ELText txt = ELText.parse(s);
144 if (txt != null)
145 {
146 if (txt.isLiteral())
147 {
148 if (escapeInlineText)
149 {
150 this.instructionBuffer.add(new LiteralTextInstruction(txt.toString()));
151 }
152 else
153 {
154 this.instructionBuffer.add(new LiteralNonExcapedTextInstruction(txt.toString()));
155 }
156 }
157 else
158 {
159 this.instructionBuffer.add(new TextInstruction(this.alias, txt ));
160 }
161 }
162 }
163 else
164 {
165
166
167 if (s != null && s.length() > 0)
168 {
169 if (ELText.isLiteral(s))
170 {
171 if (escapeInlineText)
172 {
173 this.instructionBuffer.add(new LiteralTextInstruction(s));
174 }
175 else
176 {
177 this.instructionBuffer.add(new LiteralNonExcapedTextInstruction(s));
178 }
179 }
180 else
181 {
182 s = compressELText(s);
183 this.instructionBuffer.add(new TextInstruction(this.alias, ELText.parse(s) ));
184 }
185 }
186 }
187 }
188
189 }
190 this.textBuffer.setLength(0);
191 }
192
193 public void write(String text)
194 {
195 this.finishStartTag();
196 this.textBuffer.append(text);
197 this.buffer.append(text);
198 }
199
200 public void writeInstruction(String text)
201 {
202 this.finishStartTag();
203 ELText el = ELText.parse(text);
204 if (el.isLiteral())
205 {
206 this.addInstruction(new LiteralXMLInstruction(text));
207 }
208 else
209 {
210 this.addInstruction(new XMLInstruction(el));
211 }
212 this.buffer.append(text);
213 }
214
215 public void writeComment(String text)
216 {
217 this.finishStartTag();
218
219 ELText el = ELText.parse(text);
220 if (el.isLiteral())
221 {
222 this.addInstruction(new LiteralCommentInstruction(text));
223 }
224 else
225 {
226 this.addInstruction(new CommentInstruction(el));
227 }
228
229 this.buffer.append("<!--" + text + "-->");
230 }
231
232 public void startTag(Tag tag)
233 {
234
235
236 this.finishStartTag();
237
238
239 this.tags.push(tag);
240
241
242 this.buffer.append('<');
243 this.buffer.append(tag.getQName());
244
245 this.addInstruction(new StartElementInstruction(tag.getQName()));
246
247 TagAttribute[] attrs = tag.getAttributes().getAll();
248 if (attrs.length > 0)
249 {
250 for (int i = 0; i < attrs.length; i++)
251 {
252 String qname = attrs[i].getQName();
253 String value = attrs[i].getValue();
254 this.buffer.append(' ').append(qname).append("=\"").append(value).append("\"");
255
256 ELText txt = ELText.parse(value);
257 if (txt != null)
258 {
259 if (txt.isLiteral())
260 {
261 this.addInstruction(new LiteralAttributeInstruction(qname, txt.toString()));
262 }
263 else
264 {
265 this.addInstruction(new AttributeInstruction(this.alias, qname, txt));
266 }
267 }
268 }
269 }
270
271 if (!messages.isEmpty())
272 {
273 for (Iterator<Object> it = messages.iterator(); it.hasNext();)
274 {
275 Object[] message = (Object[])it.next();
276 this.addInstruction(new AddFacesMessageInstruction((FacesMessage.Severity) message[0],
277 (String)message[1], (String)message[2]));
278 it.remove();
279 }
280 }
281
282
283 this.startTagOpen = true;
284 }
285
286 private void finishStartTag()
287 {
288 if (this.tags.size() > 0 && this.startTagOpen)
289 {
290 this.buffer.append(">");
291 this.startTagOpen = false;
292 }
293 }
294
295 public void endTag()
296 {
297 Tag tag = (Tag) this.tags.pop();
298
299 if (HTML.BODY_ELEM.equalsIgnoreCase(tag.getQName()))
300 {
301 this.addInstruction(new BodyEndElementInstruction(tag.getQName()));
302 }
303 else
304 {
305 this.addInstruction(new EndElementInstruction(tag.getQName()));
306 }
307
308 if (this.startTagOpen)
309 {
310 this.buffer.append("/>");
311 this.startTagOpen = false;
312 }
313 else
314 {
315 this.buffer.append("</").append(tag.getQName()).append('>');
316 }
317 }
318
319 public void addChild(CompilationUnit unit)
320 {
321
322
323 this.finishStartTag();
324 this.flushBufferToConfig(true);
325 this.children.add(unit);
326 }
327
328 protected void flushBufferToConfig(boolean child)
329 {
330
331
332 if (true)
333 {
334
335 this.flushTextBuffer(child);
336
337 int size = this.instructionBuffer.size();
338 if (size > 0)
339 {
340 try
341 {
342 String s = this.buffer.toString();
343 if (child)
344 {
345 s = trimRight(s);
346 }
347 ELText txt = ELText.parse(s);
348 if (txt != null)
349 {
350 if (compressSpaces)
351 {
352
353
354
355 size = compressSpaces(instructionBuffer, size);
356 }
357 Instruction[] instructions = (Instruction[]) this.instructionBuffer
358 .toArray(new Instruction[size]);
359 this.children.add(new UIInstructionHandler(this.alias, this.id, instructions, txt));
360 this.instructionBuffer.clear();
361 }
362
363 }
364 catch (ELException e)
365 {
366 if (this.tags.size() > 0)
367 {
368 throw new TagException((Tag) this.tags.peek(), e.getMessage());
369 }
370 else
371 {
372 throw new ELException(this.alias + ": " + e.getMessage(), e.getCause());
373 }
374 }
375 }
376
377
378 }
379 else if (this.buffer.length() > 0)
380 {
381 String s = this.buffer.toString();
382 if (s.trim().length() > 0)
383 {
384 if (child)
385 {
386 s = trimRight(s);
387 }
388 if (s.length() > 0)
389 {
390 try
391 {
392 ELText txt = ELText.parse(s);
393 if (txt != null)
394 {
395 if (txt.isLiteral())
396 {
397 this.children.add(new UILiteralTextHandler(txt.toString()));
398 }
399 else
400 {
401 this.children.add(new UITextHandler(this.alias, txt));
402 }
403 }
404 }
405 catch (ELException e)
406 {
407 if (this.tags.size() > 0)
408 {
409 throw new TagException((Tag) this.tags.peek(), e.getMessage());
410 }
411 else
412 {
413 throw new ELException(this.alias + ": " + e.getMessage(), e.getCause());
414 }
415 }
416 }
417 }
418 }
419
420
421 this.buffer.setLength(0);
422 }
423
424 public boolean isClosed()
425 {
426 return this.tags.empty();
427 }
428
429 private final static String trimRight(String s)
430 {
431 int i = s.length() - 1;
432 while (i >= 0 && Character.isWhitespace(s.charAt(i)))
433 {
434 i--;
435 }
436 if (i >= 0)
437 {
438 return s;
439 }
440 else
441 {
442 return "";
443 }
444
445
446
447
448
449
450
451
452
453 }
454
455 final static String compressELText(String text)
456 {
457 int firstCharLocation = getFirstTextCharLocationIgnoringSpacesTabsAndCarriageReturn(text);
458 int lastCharLocation = getLastTextCharLocationIgnoringSpacesTabsAndCarriageReturn(text);
459 if (firstCharLocation == 0 && lastCharLocation == text.length()-1)
460 {
461 return text;
462 }
463 else
464 {
465 if (lastCharLocation+1 < text.length())
466 {
467 lastCharLocation = lastCharLocation+1;
468 }
469 if (firstCharLocation == 0)
470 {
471 return text.substring(firstCharLocation, lastCharLocation+1);
472 }
473 else
474 {
475 return text.substring(0,1)+text.substring(firstCharLocation, lastCharLocation+1);
476 }
477 }
478 }
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507 final static int compressSpaces(List<Instruction> instructionBuffer, int size)
508 {
509 boolean addleftspace = true;
510 boolean addrightspace = false;
511 for (int i = 0; i < size; i++)
512 {
513 Instruction ins = instructionBuffer.get(i);
514 if (i+1 == size)
515 {
516 addrightspace = true;
517 }
518
519
520 if (ins instanceof LiteralTextInstruction)
521 {
522 String text = ((LiteralTextInstruction)ins).getText();
523 int firstCharLocation = getFirstTextCharLocationIgnoringSpacesTabsAndCarriageReturn(text);
524 if (firstCharLocation == text.length() && text.length() > 1)
525 {
526
527
528 if (addleftspace || addrightspace)
529 {
530 instructionBuffer.set(i, new LiteralTextInstruction(text.substring(0,1)));
531 }
532 else
533 {
534 instructionBuffer.remove(i);
535 i--;
536 size--;
537 }
538 }
539 else if (firstCharLocation > 0)
540 {
541 int lastCharLocation = getLastTextCharLocationIgnoringSpacesTabsAndCarriageReturn(text);
542
543 if (lastCharLocation+1 < text.length())
544 {
545 lastCharLocation = lastCharLocation+1;
546 }
547 instructionBuffer.set(i, new LiteralTextInstruction(
548 text.substring(0,1)+text.substring(firstCharLocation, lastCharLocation+1)));
549 }
550 else
551 {
552 int lastCharLocation = getLastTextCharLocationIgnoringSpacesTabsAndCarriageReturn(text);
553
554 if (lastCharLocation+1 < text.length())
555 {
556 lastCharLocation = lastCharLocation+1;
557 }
558 instructionBuffer.set(i, new LiteralTextInstruction(
559 text.substring(firstCharLocation, lastCharLocation+1)));
560 }
561 }
562 else if (ins instanceof LiteralNonExcapedTextInstruction)
563 {
564 String text = ((LiteralTextInstruction)ins).getText();
565 int firstCharLocation = getFirstTextCharLocationIgnoringSpacesTabsAndCarriageReturn(text);
566 if (firstCharLocation == text.length())
567 {
568
569
570 if (addleftspace || addrightspace)
571 {
572 instructionBuffer.set(i, new LiteralNonExcapedTextInstruction(text.substring(0,1)));
573 }
574 else
575 {
576 instructionBuffer.remove(i);
577 i--;
578 size--;
579 }
580 }
581 else if (firstCharLocation > 1)
582 {
583 int lastCharLocation = getLastTextCharLocationIgnoringSpacesTabsAndCarriageReturn(text);
584
585 if (lastCharLocation+1 < text.length())
586 {
587 lastCharLocation = lastCharLocation+1;
588 }
589 instructionBuffer.set(i, new LiteralNonExcapedTextInstruction(
590 text.substring(0,1)+text.substring(firstCharLocation, lastCharLocation+1)));
591 }
592 else
593 {
594 int lastCharLocation = getLastTextCharLocationIgnoringSpacesTabsAndCarriageReturn(text);
595
596 if (lastCharLocation+1 < text.length())
597 {
598 lastCharLocation = lastCharLocation+1;
599 }
600 instructionBuffer.set(i, new LiteralNonExcapedTextInstruction(
601 text.substring(firstCharLocation, lastCharLocation+1)));
602 }
603 }
604 addleftspace = false;
605 }
606 return size;
607 }
608
609 private static int getFirstTextCharLocationIgnoringSpacesTabsAndCarriageReturn(String text)
610 {
611 for (int i = 0; i < text.length(); i++)
612 {
613 if (Character.isWhitespace(text.charAt(i)))
614 {
615 continue;
616 }
617 else
618 {
619 return i;
620 }
621 }
622 return text.length();
623 }
624
625 private static int getLastTextCharLocationIgnoringSpacesTabsAndCarriageReturn(String text)
626 {
627 for (int i = text.length()-1; i >= 0; i--)
628 {
629 if (Character.isWhitespace(text.charAt(i)))
630 {
631 continue;
632 }
633 else
634 {
635 return i;
636 }
637 }
638 return 0;
639 }
640
641 public String toString()
642 {
643 return "TextUnit[" + this.children.size() + "]";
644 }
645
646 public void addMessage(FacesMessage.Severity severity, String summary, String detail)
647 {
648 this.messages.add(new Object[]{severity, summary, detail});
649 }
650 }