1 52 53 package freemarker.core; 54 55 import java.io.IOException ; 56 57 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 private char[] text; 70 private final boolean unparsed; 71 72 public TextBlock(String text) { 73 this(text, false); 74 } 75 76 public TextBlock(String 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 88 public void accept(Environment env) 89 throws IOException 90 { 91 env.getOut().write(text); 92 } 93 94 public String getCanonicalForm() { 95 String text = new String (this.text); 96 if (unparsed) { 97 return "<#noparse>" + text + "</#noparse>"; 98 } 99 return text; 100 } 101 102 public String getDescription() { 103 String s = new String (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 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 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 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 260 private int firstNewLineIndex() { 261 String content = new String (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 content = new String (text); 273 return Math.max(content.lastIndexOf('\r'), content.lastIndexOf('\n')); 274 } 275 276 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 (text).substring(0, newlineIndex).trim().length() >0) { 292 return 0; 293 } 294 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 312 private int trailingCharsToStrip() { 313 String content = new String (text); 314 int lastNewlineIndex = lastNewLineIndex(); 315 if (lastNewlineIndex == -1 && beginColumn != 1) { 316 return 0; 317 } 318 String substring = content.substring(lastNewlineIndex +1); 319 if (substring.trim().length() >0) { 320 return 0; 321 } 322 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 (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 |