1 21 22 package nu.xom; 23 24 import java.io.IOException ; 25 import java.io.Writer ; 26 27 import com.ibm.icu.text.Normalizer; 28 29 39 abstract class TextWriter { 40 41 protected Writer out; 42 protected String encoding; 43 44 private String lineSeparator = "\r\n"; 45 private boolean lineSeparatorSet = false; 48 private boolean inDocType = false; 49 private int maxLength = 0; 50 private int indent = 0; 51 private String indentString = ""; 52 private int column = 0; 53 private boolean preserveSpace = false; 55 private boolean normalize = false; 56 57 protected TextWriter(Writer out, String encoding) { 58 this.out = out; 59 this.encoding = encoding; 60 } 61 62 63 void reset() { 64 column = 0; 65 fakeIndents = 0; 66 lastCharacterWasSpace = false; 67 skipFollowingLinefeed = false; 68 } 69 70 71 private boolean lastCharacterWasSpace = false; 72 73 77 private boolean skipFollowingLinefeed = false; 78 79 private int highSurrogate; 80 81 82 private boolean isHighSurrogate(int c) { 83 return c >= 0xD800 && c <= 0xDBFF; 84 } 85 86 87 private boolean isLowSurrogate(int c) { 88 return c >= 0xDC00 && c <= 0xDFFF; 89 } 90 91 92 final void writePCDATA(char c) throws IOException { 94 95 if (needsEscaping(c)) { 96 writeEscapedChar(c); 97 } 98 else if (c == '&') { 99 out.write("&"); 100 column += 5; 101 lastCharacterWasSpace = false; 102 skipFollowingLinefeed = false; 103 justBroke = false; 104 } 105 else if (c == '<') { 106 out.write("<"); 107 column += 4; 108 lastCharacterWasSpace = false; 109 skipFollowingLinefeed = false; 110 justBroke = false; 111 } 112 else if (c == '>') { 113 out.write(">"); 114 column += 4; 115 lastCharacterWasSpace = false; 116 skipFollowingLinefeed = false; 117 justBroke = false; 118 } 119 else if (c == '\r') { 120 if (!adjustingWhiteSpace() && !lineSeparatorSet) { 121 out.write("
"); 122 column += 6; 123 justBroke=false; 124 } 125 else if (!adjustingWhiteSpace() && lineSeparatorSet) { 126 escapeBreakLine(); 127 } 128 else { 129 breakLine(); 130 lastCharacterWasSpace = true; 131 } 132 skipFollowingLinefeed = true; 133 } 134 else { 135 write(c); 136 } 137 138 } 139 140 141 private void writeEscapedChar(char c) throws IOException { 142 143 if (isHighSurrogate(c)) { 144 highSurrogate = c; 146 } 147 else if (isLowSurrogate(c)) { 148 int high = highSurrogate & 0x7FF; 154 int low = c - 0xDC00; 155 int highShifted = high << 10; 156 int combined = highShifted | low; 157 int uchar = combined + 0x10000; 158 String s = "&#x" + Integer.toHexString(uchar).toUpperCase() + ';'; 159 out.write(s); 160 column += s.length(); 161 lastCharacterWasSpace = false; 162 skipFollowingLinefeed = false; 163 justBroke = false; 164 } 165 else { 166 String s = "&#x" + Integer.toHexString(c).toUpperCase() + ';'; 167 out.write(s); 168 column += s.length(); 169 lastCharacterWasSpace = false; 170 skipFollowingLinefeed = false; 171 justBroke=false; 172 } 173 174 } 175 176 177 private boolean adjustingWhiteSpace() { 178 return maxLength > 0 || indent > 0; 179 } 180 181 182 final void writeAttributeValue(char c) 188 throws IOException { 189 190 if (needsEscaping(c)) { 191 writeEscapedChar(c); 192 } 193 else if (c == '\t' && !adjustingWhiteSpace()) { 198 out.write("	"); 199 column += 6; 200 lastCharacterWasSpace = true; 201 skipFollowingLinefeed = false; 202 justBroke=false; 203 } 204 else if (c == '\n') { 205 if (skipFollowingLinefeed) { 206 skipFollowingLinefeed = false; 207 return; 208 } 209 else if (adjustingWhiteSpace()) { 210 out.write(" "); 211 lastCharacterWasSpace = true; 212 justBroke=false; 213 } 214 else { 215 if (lineSeparatorSet) { 216 escapeBreakLine(); 217 } 218 else { 219 out.write("
"); 220 column += 6; 221 justBroke=false; 222 } 223 lastCharacterWasSpace = true; 224 } 225 } 226 else if (c == '"') { 227 out.write("""); 228 column += 6; 229 lastCharacterWasSpace = false; 230 skipFollowingLinefeed = false; 231 justBroke=false; 232 } 233 else if (c == '\r') { 234 if (adjustingWhiteSpace()) { 235 out.write(" "); 236 lastCharacterWasSpace = true; 237 skipFollowingLinefeed = true; 238 justBroke=false; 239 } 240 else { 241 if (lineSeparatorSet) { 242 escapeBreakLine(); 243 } 244 else { 245 out.write("
"); 246 column += 6; 247 justBroke=false; 248 } 249 skipFollowingLinefeed = true; 250 } 251 } 252 else if (c == '&') { 254 out.write("&"); 255 column += 5; 256 lastCharacterWasSpace = false; 257 skipFollowingLinefeed = false; 258 justBroke=false; 259 } 260 else if (c == '<') { 261 out.write("<"); 262 column += 4; 263 lastCharacterWasSpace = false; 264 skipFollowingLinefeed = false; 265 justBroke=false; 266 } 267 else if (c == '>') { 268 out.write(">"); 269 column += 4; 270 lastCharacterWasSpace = false; 271 skipFollowingLinefeed = false; 272 justBroke=false; 273 } 274 else { 275 write(c); 276 } 277 } 278 279 280 private void write(char c) throws IOException { 281 282 if ((c == ' ' || c == '\n' || c == '\t')) { 286 if (needsBreak()) { 287 breakLine(); 288 skipFollowingLinefeed = false; 289 } 290 else if (preserveSpace || (indent <= 0 && maxLength <= 0)) { 291 if (c == ' ' || c == '\t') { 294 out.write(c); 295 skipFollowingLinefeed = false; 296 column++; 297 justBroke=false; 298 } 299 else { if (!lineSeparatorSet || 301 !skipFollowingLinefeed) { 302 writeLineSeparator(c); 303 } 304 skipFollowingLinefeed = false; 305 column = 0; 306 } 307 } 308 else if (!lastCharacterWasSpace) { 309 out.write(' '); 310 column++; 311 skipFollowingLinefeed = false; 312 justBroke=false; 313 } 314 lastCharacterWasSpace = true; 315 } 316 else { 317 out.write(c); 318 if (c < 0xd800 || c > 0xDBFF) column++; 320 lastCharacterWasSpace = false; 321 skipFollowingLinefeed = false; 322 justBroke=false; 323 } 324 325 } 326 327 328 private void writeLineSeparator(char c) 329 throws IOException { 330 331 if (!inDocType && (!lineSeparatorSet || preserveSpace)) out.write(c); 332 else if (lineSeparator.equals("\r\n")) { 333 out.write("\r\n"); 334 } 335 else if (lineSeparator.equals("\n")) { 336 out.write('\n'); 337 } 338 else { out.write('\r'); 340 } 341 343 } 344 345 346 private boolean needsBreak() { 347 348 if (maxLength <= 0 || preserveSpace) return false; 349 355 return column >= maxLength - 10; 356 357 } 358 359 360 private boolean justBroke = false; 361 362 boolean justBroke() { 363 return justBroke; 364 } 365 366 367 final void breakLine() throws IOException { 368 369 out.write(lineSeparator); 370 out.write(indentString); 371 column = indentString.length(); 372 lastCharacterWasSpace = true; 373 justBroke = true; 374 375 } 376 377 378 final void escapeBreakLine() throws IOException { 379 380 if ("\n".equals(lineSeparator)) { 381 out.write("
"); 382 column += 6; 383 } 384 else if ("\r\n".equals(lineSeparator)) { 385 out.write("
"); 386 column += 12; 387 } 388 else { 389 out.write("
"); 390 column += 6; 391 } 392 lastCharacterWasSpace = true; 393 394 } 395 396 397 protected final void writeMarkup(char c) throws IOException { 402 403 if (needsEscaping(c)) { 404 throw new UnavailableCharacterException(c, encoding); 405 } 406 write(c); 407 408 } 409 410 414 415 final void writePCDATA(String s) throws IOException { 416 417 if (normalize) { 418 s = Normalizer.normalize(s, Normalizer.NFC); 419 } 420 int length = s.length(); 421 for (int i=0; i < length; i++) { 422 writePCDATA(s.charAt(i)); 423 } 424 425 } 426 427 428 final void writeAttributeValue(String s) 429 throws IOException { 430 431 if (normalize) { 432 s = Normalizer.normalize(s, Normalizer.NFC); 433 } 434 int length = s.length(); 435 for (int i=0; i < length; i++) { 436 writeAttributeValue(s.charAt(i)); 437 } 438 439 } 440 441 442 final void writeMarkup(String s) throws IOException { 443 444 if (normalize) { 445 s = Normalizer.normalize(s, Normalizer.NFC); 446 } 447 int length = s.length(); 448 for (int i=0; i < length; i++) { 449 writeMarkup(s.charAt(i)); 450 } 451 452 } 453 454 455 boolean isIndenting() { 456 return indentString.length() > 0; 457 } 458 459 460 private int fakeIndents = 0; 461 462 void incrementIndent() { 463 464 StringBuffer newIndent = new StringBuffer (indentString); 465 for (int i = 0; i < indent; i++) { 466 newIndent.append(' '); 467 } 468 469 if (maxLength > 0 && newIndent.length() > maxLength / 2) { 471 fakeIndents++; 472 } 473 else this.indentString = newIndent.toString(); 474 475 } 476 477 478 void decrementIndent() { 479 if (fakeIndents > 0) fakeIndents--; 480 else { 481 indentString = indentString.substring( 482 0, indentString.length()-indent 483 ); 484 } 485 } 486 487 488 String getEncoding() { 489 return this.encoding; 490 } 491 492 493 501 String getLineSeparator() { 502 return lineSeparator; 503 } 504 505 506 520 void setLineSeparator(String lineSeparator) { 521 522 if (lineSeparator.equals("\n") 523 || lineSeparator.equals("\r") 524 || lineSeparator.equals("\r\n")) { 525 this.lineSeparator = lineSeparator; 526 this.lineSeparatorSet = true; 527 } 528 else { 529 throw new IllegalArgumentException ( 530 "Illegal Line Separator"); 531 } 532 533 } 534 535 536 void setInDocType(boolean inDocType) { 537 this.inDocType = inDocType; 538 } 539 540 541 548 int getIndent() { 549 return indent; 550 } 551 552 553 560 int getMaxLength() { 561 return maxLength; 562 } 563 564 572 void setMaxLength(int maxLength) { 573 if (maxLength < 0) maxLength = 0; 574 this.maxLength = maxLength; 575 } 576 577 578 586 void setIndent(int indent) { 587 this.indent = indent; 588 } 589 590 591 void flush() throws IOException { 592 out.flush(); 593 } 594 595 596 abstract boolean needsEscaping(char c); 597 598 599 612 boolean isPreserveSpace() { 613 return preserveSpace; 614 } 615 616 617 620 void setPreserveSpace(boolean preserveSpace) { 621 this.preserveSpace = preserveSpace; 622 } 623 624 625 628 int getColumnNumber() { 629 return this.column; 630 } 631 632 633 650 void setNFC(boolean normalize) { 651 this.normalize = normalize; 652 } 653 654 655 665 boolean getNFC() { 666 return this.normalize; 667 } 668 669 670 } | Popular Tags |