KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > freemarker > core > TextBlock


1 /*
2  * Copyright (c) 2003 The Visigoth Software Society. All rights
3  * reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  *
17  * 3. The end-user documentation included with the redistribution, if
18  * any, must include the following acknowledgement:
19  * "This product includes software developed by the
20  * Visigoth Software Society (http://www.visigoths.org/)."
21  * Alternately, this acknowledgement may appear in the software itself,
22  * if and wherever such third-party acknowledgements normally appear.
23  *
24  * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
25  * project contributors may be used to endorse or promote products derived
26  * from this software without prior written permission. For written
27  * permission, please contact visigoths@visigoths.org.
28  *
29  * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
30  * nor may "FreeMarker" or "Visigoth" appear in their names
31  * without prior written permission of the Visigoth Software Society.
32  *
33  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
34  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36  * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
37  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
39  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
40  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
41  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
42  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
43  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44  * SUCH DAMAGE.
45  * ====================================================================
46  *
47  * This software consists of voluntary contributions made by many
48  * individuals on behalf of the Visigoth Software Society. For more
49  * information on the Visigoth Software Society, please see
50  * http://www.visigoths.org/
51  */

52
53 package freemarker.core;
54
55 import java.io.IOException JavaDoc;
56
57 /**
58  * A TemplateElement representing a block of plain text.
59  * @version $Id: TextBlock.java,v 1.17 2004/01/06 17:06:42 szegedia Exp $
60  */

61 public final class TextBlock extends TemplateElement {
62     private static final char[] EMPTY_CHAR_ARRAY = new char[0];
63     static final TextBlock EMPTY_BLOCK = new TextBlock(EMPTY_CHAR_ARRAY, false);
64     // We're using char[] instead of String for storing the text block because
65
// Writer.write(String) involves copying the String contents to a char[]
66
// using String.getChars(), and then calling Writer.write(char[]). By
67
// using Writer.write(char[]) directly, we avoid array copying on each
68
// write.
69
private char[] text;
70     private final boolean unparsed;
71
72     public TextBlock(String JavaDoc text) {
73         this(text, false);
74     }
75
76     public TextBlock(String JavaDoc text, boolean unparsed) {
77         this(text.toCharArray(), unparsed);
78     }
79
80     private TextBlock(char[] text, boolean unparsed) {
81         this.text = text;
82         this.unparsed = unparsed;
83     }
84
85     /**
86      * Simply outputs the text.
87      */

88     public void accept(Environment env)
89     throws IOException JavaDoc
90     {
91         env.getOut().write(text);
92     }
93
94     public String JavaDoc getCanonicalForm() {
95         String JavaDoc text = new String JavaDoc(this.text);
96         if (unparsed) {
97             return "<#noparse>" + text + "</#noparse>";
98         }
99         return text;
100     }
101
102     public String JavaDoc getDescription() {
103         String JavaDoc s = new String JavaDoc(text).trim();
104         if (s.length() == 0) {
105             return "whitespace";
106         }
107         if (s.length() > 20) {
108             s = s.substring(0,20) + "...";
109             s = s.replace('\n', ' ');
110             s = s.replace('\r', ' ');
111         }
112         return "text block (" + s + ")";
113     }
114     
115     TemplateElement postParseCleanup(boolean stripWhitespace) {
116         if (text.length == 0) return this;
117         int openingCharsToStrip = 0, trailingCharsToStrip=0;
118         boolean deliberateLeftTrim = deliberateLeftTrim();
119         boolean deliberateRightTrim = deliberateRightTrim();
120         if (!stripWhitespace || text.length == 0 ) {
121             return this;
122         }
123         if (parent.parent == null && previousSibling() == null) return this;
124         if (!deliberateLeftTrim) {
125             trailingCharsToStrip = trailingCharsToStrip();
126         }
127         if (!deliberateRightTrim) {
128             openingCharsToStrip = openingCharsToStrip();
129         }
130         if (openingCharsToStrip == 0 && trailingCharsToStrip == 0) {
131             return this;
132         }
133         this.text = substring(text, openingCharsToStrip, text.length - trailingCharsToStrip);
134         if (openingCharsToStrip > 0) {
135             this.beginLine++;
136             this.beginColumn = 1;
137         }
138         if (trailingCharsToStrip >0) {
139             this.endColumn = 0;
140         }
141         return this;
142     }
143     
144     /**
145      * Scans forward the nodes on the same line to see whether there is a
146      * deliberate left trim in effect. Returns true if the left trim was present.
147      */

148     private boolean deliberateLeftTrim() {
149         boolean result = false;
150         for (TemplateElement elem = this.nextTerminalNode();
151              elem != null && elem.beginLine == this.endLine;
152              elem = elem.nextTerminalNode())
153         {
154             if (elem instanceof TrimInstruction) {
155                 TrimInstruction ti = (TrimInstruction) elem;
156                 if (!ti.left && !ti.right) {
157                     result = true;
158                 }
159                 if (ti.left) {
160                     result = true;
161                     int lastNewLineIndex = lastNewLineIndex();
162                     if (lastNewLineIndex >=0 || beginColumn == 1) {
163                         char[] firstPart = substring(text, 0, lastNewLineIndex + 1);
164                         char[] lastLine = substring(text, 1+lastNewLineIndex);
165                         if (trim(lastLine).length == 0) {
166                             this.text = firstPart;
167                             this.endColumn = 0;
168                         } else {
169                             int i =0;
170                             while (Character.isWhitespace(lastLine[i])) {
171                                 i++;
172                             }
173                             char[] printablePart = substring(lastLine, i);
174                             this.text = concat(firstPart, printablePart);
175                         }
176                     }
177                 }
178             }
179         }
180         if (result) {
181         }
182         return result;
183     }
184
185     /**
186      * Checks for the presence of a t or rt directive on the
187      * same line. Returns true if the right trim directive was present.
188      */

189     private boolean deliberateRightTrim() {
190         boolean result = false;
191         for (TemplateElement elem = this.prevTerminalNode();
192              elem != null && elem.endLine == this.beginLine;
193              elem = elem.prevTerminalNode())
194         {
195             if (elem instanceof TrimInstruction) {
196                 TrimInstruction ti = (TrimInstruction) elem;
197                 if (!ti.left && !ti.right) {
198                     result = true;
199                 }
200                 if (ti.right) {
201                     result = true;
202                     int firstLineIndex = firstNewLineIndex() +1;
203                     if (firstLineIndex == 0) {
204                         return false;
205                     }
206                     if (text.length > firstLineIndex
207                         && text[firstLineIndex-1] == '\r'
208                         && text[firstLineIndex] == '\n')
209                     {
210                         firstLineIndex++;
211                     }
212                     char[] trailingPart = substring(text, firstLineIndex);
213                     char[] openingPart = substring(text, 0, firstLineIndex);
214                     if (trim(openingPart).length ==0) {
215                         this.text = trailingPart;
216                         this.beginLine++;
217                         this.beginColumn=1;
218                     } else {
219                         int lastNonWS = openingPart.length -1;
220                         while (Character.isWhitespace(text[lastNonWS])) {
221                             lastNonWS--;
222                         }
223                         char[] printablePart = substring(text, 0, lastNonWS+1);
224                         if (trim(trailingPart).length == 0) {
225                         // THIS BLOCK IS HEINOUS! THERE MUST BE A BETTER WAY! REVISIT (JR)
226
boolean trimTrailingPart = true;
227                             for (TemplateElement te = this.nextTerminalNode();
228                                  te != null && te.beginLine == this.endLine;
229                                  te = te.nextTerminalNode())
230                             {
231                                 if (te.heedsOpeningWhitespace())
232                                 {
233                                     trimTrailingPart = false;
234                                 }
235                                 if (te instanceof TrimInstruction && ((TrimInstruction) te).left) {
236                                     trimTrailingPart = true;
237                                     break;
238                                 }
239                             }
240                             if (trimTrailingPart) trailingPart = EMPTY_CHAR_ARRAY;
241                         }
242                         this.text = concat(printablePart, trailingPart);
243                     }
244                 }
245             }
246         }
247         return result;
248     }
249 /*
250     private String leftTrim(String s) {
251         int i =0;
252         while (i<s.length()) {
253             if (!Character.isWhitespace(s.charAt(i)))
254                 break;
255             ++i;
256         }
257         return s.substring(i);
258     }
259 */

260     private int firstNewLineIndex() {
261         String JavaDoc content = new String JavaDoc(text);
262         int newlineIndex1 = content.indexOf('\n');
263         int newlineIndex2 = content.indexOf('\r');
264         int result = newlineIndex1 >=0 ? newlineIndex1 : newlineIndex2;
265         if (newlineIndex1 >=0 && newlineIndex2 >=0) {
266             result = Math.min(newlineIndex1, newlineIndex2);
267         }
268         return result;
269     }
270
271     private int lastNewLineIndex() {
272         String JavaDoc content = new String JavaDoc(text);
273         return Math.max(content.lastIndexOf('\r'), content.lastIndexOf('\n'));
274     }
275
276     /**
277      * figures out how many opening whitespace characters to strip
278      * in the post-parse cleanup phase.
279      */

280     private int openingCharsToStrip() {
281         int newlineIndex = firstNewLineIndex();
282         if (newlineIndex == -1 && beginColumn != 1) {
283             return 0;
284         }
285         ++newlineIndex;
286         if (text.length > newlineIndex) {
287             if (newlineIndex >0 && text[newlineIndex-1] == '\r' && text[newlineIndex] == '\n') {
288                 ++newlineIndex;
289             }
290         }
291         if (new String JavaDoc(text).substring(0, newlineIndex).trim().length() >0) {
292             return 0;
293         }
294 // We look at the preceding elements on the line to see if we should
295
// strip the opening newline and any whitespace preceding it.
296
for (TemplateElement elem = this.prevTerminalNode();
297              elem != null && elem.endLine == this.beginLine;
298              elem = elem.prevTerminalNode())
299         {
300             if (elem.heedsOpeningWhitespace())
301             {
302                 return 0;
303             }
304         }
305         return newlineIndex;
306     }
307
308     /**
309      * figures out how many trailing whitespace characters to strip
310      * in the post-parse cleanup phase.
311      */

312     private int trailingCharsToStrip() {
313         String JavaDoc content = new String JavaDoc(text);
314         int lastNewlineIndex = lastNewLineIndex();
315         if (lastNewlineIndex == -1 && beginColumn != 1) {
316             return 0;
317         }
318         String JavaDoc substring = content.substring(lastNewlineIndex +1);
319         if (substring.trim().length() >0) {
320             return 0;
321         }
322 // We look at the elements afterward on the same line to see if we should
323
// strip any whitespace after the last newline
324
for (TemplateElement elem = this.nextTerminalNode();
325              elem != null && elem.beginLine == this.endLine;
326              elem = elem.nextTerminalNode())
327         {
328             if (elem.heedsTrailingWhitespace())
329             {
330                 return 0;
331             }
332         }
333         return substring.length();
334     }
335
336     boolean heedsTrailingWhitespace() {
337         if (isIgnorable()) {
338             return false;
339         }
340         for (int i=0; i<text.length; i++) {
341             char c = text[i];
342             if (c=='\n' || c=='\r') {
343                 return false;
344             }
345             if (!Character.isWhitespace(c)) {
346                 return true;
347             }
348         }
349         return true;
350     }
351
352     boolean heedsOpeningWhitespace() {
353         if (isIgnorable()) {
354             return false;
355         }
356         for (int i = text.length -1; i>=0; i--) {
357             char c = text[i];
358             if (c == '\n' || c == '\r') {
359                 return false;
360             }
361             if (!Character.isWhitespace(c)) {
362                 return true;
363             }
364         }
365         return true;
366     }
367
368     boolean isIgnorable() {
369         if (text == null || text.length == 0) {
370             return true;
371         }
372         if (!isWhitespace()) {
373             return false;
374         }
375         boolean atTopLevel = (getParent().getParent() == null);
376         TemplateElement prevSibling = previousSibling();
377         TemplateElement nextSibling = nextSibling();
378         return ((prevSibling == null && atTopLevel) || nonOutputtingType(prevSibling))
379               && ((nextSibling == null && atTopLevel) || nonOutputtingType(nextSibling));
380     }
381     
382
383     private boolean nonOutputtingType(TemplateElement element) {
384         return (element instanceof Macro ||
385                 element instanceof Assignment ||
386                 element instanceof AssignmentInstruction ||
387                 element instanceof PropertySetting ||
388                 element instanceof LibraryLoad ||
389                 element instanceof Comment);
390     }
391
392     private static char[] substring(char[] c, int from, int to) {
393         char[] c2 = new char[to - from];
394         System.arraycopy(c, from, c2, 0, c2.length);
395         return c2;
396     }
397     
398     private static char[] substring(char[] c, int from) {
399         return substring(c, from, c.length);
400     }
401     
402     private static char[] trim(char[] c) {
403         if (c.length == 0) {
404             return c;
405         }
406         return new String JavaDoc(c).trim().toCharArray();
407     }
408     
409     private static char[] concat(char[] c1, char[] c2) {
410         char[] c = new char[c1.length + c2.length];
411         System.arraycopy(c1, 0, c, 0, c1.length);
412         System.arraycopy(c2, 0, c, c1.length, c2.length);
413         return c;
414     }
415     
416     boolean isWhitespace() {
417         return text == null || trim(text).length == 0;
418     }
419 }
420
Popular Tags