Clover coverage report - Maven Clover report
Coverage timestamp: Tue Aug 1 2006 15:09:51 CEST
file stats: LOC: 2,798   Methods: 41
NCLOC: 1,902   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
PPrint.java 72.2% 71.9% 85.4% 72.3%
coverage coverage
 1    /*
 2    * Java HTML Tidy - JTidy
 3    * HTML parser and pretty printer
 4    *
 5    * Copyright (c) 1998-2000 World Wide Web Consortium (Massachusetts
 6    * Institute of Technology, Institut National de Recherche en
 7    * Informatique et en Automatique, Keio University). All Rights
 8    * Reserved.
 9    *
 10    * Contributing Author(s):
 11    *
 12    * Dave Raggett <dsr@w3.org>
 13    * Andy Quick <ac.quick@sympatico.ca> (translation to Java)
 14    * Gary L Peskin <garyp@firstech.com> (Java development)
 15    * Sami Lempinen <sami@lempinen.net> (release management)
 16    * Fabrizio Giustina <fgiust at users.sourceforge.net>
 17    *
 18    * The contributing author(s) would like to thank all those who
 19    * helped with testing, bug fixes, and patience. This wouldn't
 20    * have been possible without all of you.
 21    *
 22    * COPYRIGHT NOTICE:
 23    *
 24    * This software and documentation is provided "as is," and
 25    * the copyright holders and contributing author(s) make no
 26    * representations or warranties, express or implied, including
 27    * but not limited to, warranties of merchantability or fitness
 28    * for any particular purpose or that the use of the software or
 29    * documentation will not infringe any third party patents,
 30    * copyrights, trademarks or other rights.
 31    *
 32    * The copyright holders and contributing author(s) will not be
 33    * liable for any direct, indirect, special or consequential damages
 34    * arising out of any use of the software or documentation, even if
 35    * advised of the possibility of such damage.
 36    *
 37    * Permission is hereby granted to use, copy, modify, and distribute
 38    * this source code, or portions hereof, documentation and executables,
 39    * for any purpose, without fee, subject to the following restrictions:
 40    *
 41    * 1. The origin of this source code must not be misrepresented.
 42    * 2. Altered versions must be plainly marked as such and must
 43    * not be misrepresented as being the original source.
 44    * 3. This Copyright notice may not be removed or altered from any
 45    * source or altered source distribution.
 46    *
 47    * The copyright holders and contributing author(s) specifically
 48    * permit, without fee, and encourage the use of this source code
 49    * as a component for supporting the Hypertext Markup Language in
 50    * commercial products. If you use this source code in a product,
 51    * acknowledgment is not required but would be appreciated.
 52    *
 53    */
 54    package org.w3c.tidy;
 55   
 56    import java.io.File;
 57    import java.io.FileOutputStream;
 58    import java.io.IOException;
 59    import java.text.NumberFormat;
 60   
 61   
 62    /**
 63    * Pretty print parse tree. Block-level and unknown elements are printed on new lines and their contents indented 2
 64    * spaces Inline elements are printed inline. Inline content is wrapped on spaces (except in attribute values or
 65    * preformatted text, after start tags and before end tags.
 66    * @author Dave Raggett <a href="mailto:dsr@w3.org">dsr@w3.org </a>
 67    * @author Andy Quick <a href="mailto:ac.quick@sympatico.ca">ac.quick@sympatico.ca </a> (translation to Java)
 68    * @author Fabrizio Giustina
 69    * @version $Revision: 807 $ ($Author: fgiust $)
 70    */
 71    public class PPrint
 72    {
 73   
 74    /**
 75    * position: normal.
 76    */
 77    private static final short NORMAL = 0;
 78   
 79    /**
 80    * position: preformatted text.
 81    */
 82    private static final short PREFORMATTED = 1;
 83   
 84    /**
 85    * position: comment.
 86    */
 87    private static final short COMMENT = 2;
 88   
 89    /**
 90    * position: attribute value.
 91    */
 92    private static final short ATTRIBVALUE = 4;
 93   
 94    /**
 95    * position: nowrap.
 96    */
 97    private static final short NOWRAP = 8;
 98   
 99    /**
 100    * position: cdata.
 101    */
 102    private static final short CDATA = 16;
 103   
 104    /**
 105    * Start cdata token.
 106    */
 107    private static final String CDATA_START = "<![CDATA[";
 108   
 109    /**
 110    * End cdata token.
 111    */
 112    private static final String CDATA_END = "]]>";
 113   
 114    /**
 115    * Javascript comment start.
 116    */
 117    private static final String JS_COMMENT_START = "//";
 118   
 119    /**
 120    * Javascript comment end.
 121    */
 122    private static final String JS_COMMENT_END = "";
 123   
 124    /**
 125    * VB comment start.
 126    */
 127    private static final String VB_COMMENT_START = "\'";
 128   
 129    /**
 130    * VB comment end.
 131    */
 132    private static final String VB_COMMENT_END = "";
 133   
 134    /**
 135    * CSS comment start.
 136    */
 137    private static final String CSS_COMMENT_START = "/*";
 138   
 139    /**
 140    * CSS comment end.
 141    */
 142    private static final String CSS_COMMENT_END = "*/";
 143   
 144    /**
 145    * Default comment start.
 146    */
 147    private static final String DEFAULT_COMMENT_START = "";
 148   
 149    /**
 150    * Default comment end.
 151    */
 152    private static final String DEFAULT_COMMENT_END = "";
 153   
 154    private int[] linebuf;
 155   
 156    private int lbufsize;
 157   
 158    private int linelen;
 159   
 160    private int wraphere;
 161   
 162    private boolean inAttVal;
 163   
 164    private boolean inString;
 165   
 166    /**
 167    * Current slide number.
 168    */
 169    private int slide;
 170   
 171    /**
 172    * Total slides count.
 173    */
 174    private int count;
 175   
 176    private Node slidecontent;
 177   
 178    /**
 179    * current configuration.
 180    */
 181    private Configuration configuration;
 182   
 183    /**
 184    * Instantiates a new PPrint.
 185    * @param configuration configuration
 186    */
 187  222 public PPrint(Configuration configuration)
 188    {
 189  222 this.configuration = configuration;
 190    }
 191   
 192    /**
 193    * @param ind
 194    * @return
 195    */
 196  0 int cWrapLen(int ind)
 197    {
 198    /* #431953 - start RJ Wraplen adjusted for smooth international ride */
 199  0 if ("zh".equals(this.configuration.language))
 200    {
 201    // Chinese characters take two positions on a fixed-width screen
 202    // It would be more accurate to keep a parallel linelen and wraphere incremented by 2 for Chinese characters
 203    // and 1 otherwise, but this is way simpler.
 204  0 return (ind + ((this.configuration.wraplen - ind) / 2));
 205    }
 206  0 if ("ja".equals(this.configuration.language))
 207    {
 208    /* average Japanese text is 30% kanji */
 209  0 return (ind + (((this.configuration.wraplen - ind) * 7) / 10));
 210    }
 211  0 return (this.configuration.wraplen);
 212    /* #431953 - end RJ */
 213    }
 214   
 215    /**
 216    * return one less than the number of bytes used by the UTF-8 byte sequence. The Unicode char is returned in ch.
 217    * @param str points to the UTF-8 byte sequence
 218    * @param start starting offset in str
 219    * @param ch initialized to 1st byte, passed as an array to allow modification
 220    * @return one less that the number of bytes used by UTF-8 char
 221    */
 222  1025 public static int getUTF8(byte[] str, int start, int[] ch)
 223    {
 224   
 225  1025 int[] n = new int[1];
 226   
 227  1025 int[] bytes = new int[]{0};
 228   
 229    // first byte "str[0]" is passed in separately from the
 230    // rest of the UTF-8 byte sequence starting at "str[1]"
 231  1025 byte[] successorBytes = str;
 232   
 233  1025 boolean err = EncodingUtils.decodeUTF8BytesToChar(
 234    n,
 235    TidyUtils.toUnsigned(str[start]),
 236    successorBytes,
 237    null,
 238    bytes,
 239    start + 1);
 240   
 241  1025 if (err)
 242    {
 243  0 n[0] = 0xFFFD; // replacement char
 244    }
 245  1025 ch[0] = n[0];
 246  1025 return bytes[0] - 1;
 247   
 248    }
 249   
 250    /**
 251    * store char c as UTF-8 encoded byte stream.
 252    * @param buf
 253    * @param start
 254    * @param c
 255    * @return
 256    */
 257  0 public static int putUTF8(byte[] buf, int start, int c)
 258    {
 259  0 int[] count = new int[]{0};
 260   
 261  0 boolean err = EncodingUtils.encodeCharToUTF8Bytes(c, buf, null, count);
 262  0 if (err)
 263    {
 264    // replacement char 0xFFFD encoded as UTF-8
 265  0 buf[0] = (byte) 0xEF;
 266  0 buf[1] = (byte) 0xBF;
 267  0 buf[2] = (byte) 0xBD;
 268  0 count[0] = 3;
 269    }
 270   
 271  0 start += count[0];
 272   
 273  0 return start;
 274    }
 275   
 276  167694 private void addC(int c, int index)
 277    {
 278  167694 if (index + 1 >= lbufsize)
 279    {
 280  233 while (index + 1 >= lbufsize)
 281    {
 282  233 if (lbufsize == 0)
 283    {
 284  221 lbufsize = 256;
 285    }
 286    else
 287    {
 288  12 lbufsize = lbufsize * 2;
 289    }
 290    }
 291   
 292  233 int[] temp = new int[lbufsize];
 293  233 if (linebuf != null)
 294    {
 295  12 System.arraycopy(linebuf, 0, temp, 0, index);
 296    }
 297  233 linebuf = temp;
 298    }
 299   
 300  167694 linebuf[index] = c;
 301    }
 302   
 303    /**
 304    * Adds an ascii String.
 305    * @param str String to be added
 306    * @param index actual line lenght
 307    * @return final line length
 308    */
 309  78 private int addAsciiString(String str, int index)
 310    {
 311   
 312  78 int len = str.length();
 313  78 if (index + len >= lbufsize)
 314    {
 315  0 while (index + len >= lbufsize)
 316    {
 317  0 if (lbufsize == 0)
 318    {
 319  0 lbufsize = 256;
 320    }
 321    else
 322    {
 323  0 lbufsize = lbufsize * 2;
 324    }
 325    }
 326   
 327  0 int[] temp = new int[lbufsize];
 328  0 if (linebuf != null)
 329    {
 330  0 System.arraycopy(linebuf, 0, temp, 0, index);
 331    }
 332  0 linebuf = temp;
 333    }
 334   
 335  78 for (int ix = 0; ix < len; ++ix)
 336    {
 337  208 linebuf[index + ix] = str.charAt(ix);
 338    }
 339  78 return index + len;
 340    }
 341   
 342    /**
 343    * @param fout
 344    * @param indent
 345    */
 346  498 private void wrapLine(Out fout, int indent)
 347    {
 348  498 int i, p, q;
 349   
 350  498 if (wraphere == 0)
 351    {
 352  330 return;
 353    }
 354   
 355  168 for (i = 0; i < indent; ++i)
 356    {
 357  176 fout.outc(' ');
 358    }
 359   
 360  168 for (i = 0; i < wraphere; ++i)
 361    {
 362  6521 fout.outc(linebuf[i]);
 363    }
 364   
 365  168 if (inString)
 366    {
 367  0 fout.outc(' ');
 368  0 fout.outc('\\');
 369    }
 370   
 371  168 fout.newline();
 372   
 373  168 if (linelen > wraphere)
 374    {
 375  168 p = 0;
 376   
 377  168 if (linebuf[wraphere] == ' ')
 378    {
 379  78 ++wraphere;
 380    }
 381   
 382  168 q = wraphere;
 383  168 addC('\0', linelen);
 384   
 385  168 while (true)
 386    {
 387  5642 linebuf[p] = linebuf[q];
 388  5642 if (linebuf[q] == 0)
 389    {
 390  168 break;
 391    }
 392  5474 p++;
 393  5474 q++;
 394    }
 395  168 linelen -= wraphere;
 396    }
 397    else
 398    {
 399  0 linelen = 0;
 400    }
 401   
 402  168 wraphere = 0;
 403    }
 404   
 405    /**
 406    * @param fout
 407    * @param indent
 408    * @param inString
 409    */
 410  0 private void wrapAttrVal(Out fout, int indent, boolean inString)
 411    {
 412  0 int i, p, q;
 413   
 414  0 for (i = 0; i < indent; ++i)
 415    {
 416  0 fout.outc(' ');
 417    }
 418   
 419  0 for (i = 0; i < wraphere; ++i)
 420    {
 421  0 fout.outc(linebuf[i]);
 422    }
 423   
 424  0 fout.outc(' ');
 425   
 426  0 if (inString)
 427    {
 428  0 fout.outc('\\');
 429    }
 430   
 431  0 fout.newline();
 432   
 433  0 if (linelen > wraphere)
 434    {
 435  0 p = 0;
 436   
 437  0 if (linebuf[wraphere] == ' ')
 438    {
 439  0 ++wraphere;
 440    }
 441   
 442  0 q = wraphere;
 443  0 addC('\0', linelen);
 444   
 445  0 while (true)
 446    {
 447  0 linebuf[p] = linebuf[q];
 448  0 if (linebuf[q] == 0)
 449    {
 450  0 break;
 451    }
 452  0 p++;
 453  0 q++;
 454    }
 455  0 linelen -= wraphere;
 456    }
 457    else
 458    {
 459  0 linelen = 0;
 460    }
 461   
 462  0 wraphere = 0;
 463    }
 464   
 465    /**
 466    * @param fout
 467    * @param indent
 468    */
 469  4068 public void flushLine(Out fout, int indent)
 470    {
 471  4068 int i;
 472   
 473  4068 if (linelen > 0)
 474    {
 475  3984 if (indent + linelen >= this.configuration.wraplen)
 476    {
 477  51 wrapLine(fout, indent);
 478    }
 479   
 480  3984 if (!inAttVal || this.configuration.indentAttributes)
 481    {
 482  3984 for (i = 0; i < indent; ++i)
 483    {
 484  3388 fout.outc(' ');
 485    }
 486    }
 487   
 488  3984 for (i = 0; i < linelen; ++i)
 489    {
 490  116099 fout.outc(linebuf[i]);
 491    }
 492    }
 493   
 494  4068 fout.newline();
 495  4068 linelen = 0;
 496  4068 wraphere = 0;
 497  4068 inAttVal = false;
 498    }
 499   
 500    /**
 501    * @param fout
 502    * @param indent
 503    */
 504  4458 public void condFlushLine(Out fout, int indent)
 505    {
 506  4458 int i;
 507   
 508  4458 if (linelen > 0)
 509    {
 510  1666 if (indent + linelen >= this.configuration.wraplen)
 511    {
 512  2 wrapLine(fout, indent);
 513    }
 514   
 515  1666 if (!inAttVal || this.configuration.indentAttributes)
 516    {
 517  1666 for (i = 0; i < indent; ++i)
 518    {
 519  208 fout.outc(' ');
 520    }
 521    }
 522   
 523  1666 for (i = 0; i < linelen; ++i)
 524    {
 525  45036 fout.outc(linebuf[i]);
 526    }
 527   
 528  1666 fout.newline();
 529  1666 linelen = 0;
 530  1666 wraphere = 0;
 531  1666 inAttVal = false;
 532    }
 533    }
 534   
 535    /**
 536    * @param c
 537    * @param mode
 538    */
 539  96576 private void printChar(int c, short mode)
 540    {
 541  96576 String entity;
 542  96576 boolean breakable = false; // #431953 - RJ
 543   
 544  96576 if (c == ' ' && !TidyUtils.toBoolean(mode & (PREFORMATTED | COMMENT | ATTRIBVALUE | CDATA)))
 545    {
 546    // coerce a space character to a non-breaking space
 547  7178 if (TidyUtils.toBoolean(mode & NOWRAP))
 548    {
 549    // by default XML doesn't define &nbsp;
 550  0 if (this.configuration.numEntities || this.configuration.xmlTags)
 551    {
 552  0 addC('&', linelen++);
 553  0 addC('#', linelen++);
 554  0 addC('1', linelen++);
 555  0 addC('6', linelen++);
 556  0 addC('0', linelen++);
 557  0 addC(';', linelen++);
 558    }
 559    else
 560    {
 561    // otherwise use named entity
 562  0 addC('&', linelen++);
 563  0 addC('n', linelen++);
 564  0 addC('b', linelen++);
 565  0 addC('s', linelen++);
 566  0 addC('p', linelen++);
 567  0 addC(';', linelen++);
 568    }
 569  0 return;
 570    }
 571  7178 wraphere = linelen;
 572    }
 573   
 574    // comment characters are passed raw
 575  96576 if (TidyUtils.toBoolean(mode & (COMMENT | CDATA)))
 576    {
 577  15119 addC(c, linelen++);
 578  15119 return;
 579    }
 580   
 581    // except in CDATA map < to &lt; etc.
 582  81457 if (!TidyUtils.toBoolean(mode & CDATA))
 583    {
 584  81457 if (c == '<')
 585    {
 586  67 addC('&', linelen++);
 587  67 addC('l', linelen++);
 588  67 addC('t', linelen++);
 589  67 addC(';', linelen++);
 590  67 return;
 591    }
 592   
 593  81390 if (c == '>')
 594    {
 595  66 addC('&', linelen++);
 596  66 addC('g', linelen++);
 597  66 addC('t', linelen++);
 598  66 addC(';', linelen++);
 599  66 return;
 600    }
 601   
 602    // naked '&' chars can be left alone or quoted as &amp;
 603    // The latter is required for XML where naked '&' are illegal.
 604  81324 if (c == '&' && this.configuration.quoteAmpersand)
 605    {
 606  720 addC('&', linelen++);
 607  720 addC('a', linelen++);
 608  720 addC('m', linelen++);
 609  720 addC('p', linelen++);
 610  720 addC(';', linelen++);
 611  720 return;
 612    }
 613   
 614  80604 if (c == '"' && this.configuration.quoteMarks)
 615    {
 616  0 addC('&', linelen++);
 617  0 addC('q', linelen++);
 618  0 addC('u', linelen++);
 619  0 addC('o', linelen++);
 620  0 addC('t', linelen++);
 621  0 addC(';', linelen++);
 622  0 return;
 623    }
 624   
 625  80604 if (c == '\'' && this.configuration.quoteMarks)
 626    {
 627  0 addC('&', linelen++);
 628  0 addC('#', linelen++);
 629  0 addC('3', linelen++);
 630  0 addC('9', linelen++);
 631  0 addC(';', linelen++);
 632  0 return;
 633    }
 634   
 635  80604 if (c == 160 && !this.configuration.rawOut)
 636    {
 637  37 if (this.configuration.makeBare)
 638    {
 639  0 addC(' ', linelen++);
 640    }
 641  37 else if (this.configuration.quoteNbsp)
 642    {
 643  37 addC('&', linelen++);
 644   
 645  37 if (this.configuration.numEntities || this.configuration.xmlTags)
 646    {
 647  13 addC('#', linelen++);
 648  13 addC('1', linelen++);
 649  13 addC('6', linelen++);
 650  13 addC('0', linelen++);
 651    }
 652    else
 653    {
 654  24 addC('n', linelen++);
 655  24 addC('b', linelen++);
 656  24 addC('s', linelen++);
 657  24 addC('p', linelen++);
 658    }
 659   
 660  37 addC(';', linelen++);
 661    }
 662    else
 663    {
 664  0 addC(c, linelen++);
 665    }
 666   
 667  37 return;
 668    }
 669    }
 670   
 671    // #431953 - start RJ
 672    // Handle encoding-specific issues
 673   
 674  80567 if ("UTF8".equals(this.configuration.getOutCharEncodingName()))
 675    {
 676    // Chinese doesn't have spaces, so it needs other kinds of breaks
 677    // This will also help documents using nice Unicode punctuation
 678    // But we leave the ASCII range punctuation untouched
 679   
 680    // Break after any punctuation or spaces characters
 681  437 if ((c >= 0x2000) && !TidyUtils.toBoolean(mode & PREFORMATTED))
 682    {
 683  2 if (((c >= 0x2000) && (c <= 0x2006))
 684    || ((c >= 0x2008) && (c <= 0x2010))
 685    || ((c >= 0x2011) && (c <= 0x2046))
 686    || ((c >= 0x207D) && (c <= 0x207E))
 687    || ((c >= 0x208D) && (c <= 0x208E))
 688    || ((c >= 0x2329) && (c <= 0x232A))
 689    || ((c >= 0x3001) && (c <= 0x3003))
 690    || ((c >= 0x3008) && (c <= 0x3011))
 691    || ((c >= 0x3014) && (c <= 0x301F))
 692    || ((c >= 0xFD3E) && (c <= 0xFD3F))
 693    || ((c >= 0xFE30) && (c <= 0xFE44))
 694    || ((c >= 0xFE49) && (c <= 0xFE52))
 695    || ((c >= 0xFE54) && (c <= 0xFE61))
 696    || ((c >= 0xFE6A) && (c <= 0xFE6B))
 697    || ((c >= 0xFF01) && (c <= 0xFF03))
 698    || ((c >= 0xFF05) && (c <= 0xFF0A))
 699    || ((c >= 0xFF0C) && (c <= 0xFF0F))
 700    || ((c >= 0xFF1A) && (c <= 0xFF1B))
 701    || ((c >= 0xFF1F) && (c <= 0xFF20))
 702    || ((c >= 0xFF3B) && (c <= 0xFF3D))
 703    || ((c >= 0xFF61) && (c <= 0xFF65)))
 704    {
 705  2 wraphere = linelen + 2; // 2, because AddChar is not till later
 706  2 breakable = true;
 707    }
 708    else
 709    {
 710  0 switch (c)
 711    {
 712  0 case 0xFE63 :
 713  0 case 0xFE68 :
 714  0 case 0x3030 :
 715  0 case 0x30FB :
 716  0 case 0xFF3F :
 717  0 case 0xFF5B :
 718  0 case 0xFF5D :
 719  0 wraphere = linelen + 2;
 720  0 breakable = true;
 721    }
 722    }
 723    // but break before a left punctuation
 724  2 if (breakable)
 725    {
 726  2 if (((c >= 0x201A) && (c <= 0x201C)) || ((c >= 0x201E) && (c <= 0x201F)))
 727    {
 728  0 wraphere--;
 729    }
 730    else
 731    {
 732  2 switch (c)
 733    {
 734  0 case 0x2018 :
 735  0 case 0x2039 :
 736  0 case 0x2045 :
 737  0 case 0x207D :
 738  0 case 0x208D :
 739  0 case 0x2329 :
 740  0 case 0x3008 :
 741  0 case 0x300A :
 742  0 case 0x300C :
 743  0 case 0x300E :
 744  0 case 0x3010 :
 745  0 case 0x3014 :
 746  0 case 0x3016 :
 747  0 case 0x3018 :
 748  0 case 0x301A :
 749  0 case 0x301D :
 750  0 case 0xFD3E :
 751  0 case 0xFE35 :
 752  0 case 0xFE37 :
 753  0 case 0xFE39 :
 754  0 case 0xFE3B :
 755  0 case 0xFE3D :
 756  0 case 0xFE3F :
 757  0 case 0xFE41 :
 758  0 case 0xFE43 :
 759  0 case 0xFE59 :
 760  0 case 0xFE5B :
 761  0 case 0xFE5D :
 762  0 case 0xFF08 :
 763  0 case 0xFF3B :
 764  0 case 0xFF5B :
 765  0 case 0xFF62 :
 766  0 wraphere--;
 767    }
 768    }
 769    }
 770    }
 771  435 else if ("BIG5".equals(this.configuration.getOutCharEncodingName()))
 772    {
 773    // Allow linebreak at Chinese punctuation characters
 774    // There are not many spaces in Chinese
 775  0 addC(c, linelen++);
 776  0 if (((c & 0xFF00) == 0xA100) && !TidyUtils.toBoolean(mode & PREFORMATTED))
 777    {
 778  0 wraphere = linelen;
 779    // opening brackets have odd codes: break before them
 780  0 if ((c > 0x5C) && (c < 0xAD) && ((c & 1) == 1))
 781    {
 782  0 wraphere--;
 783    }
 784    }
 785  0 return;
 786    }
 787  435 else if ("SHIFTJIS".equals(this.configuration.getOutCharEncodingName())
 788    || "ISO2022".equals(this.configuration.getOutCharEncodingName()))
 789    {
 790    // ISO 2022 characters are passed raw
 791  0 addC(c, linelen++);
 792  0 return;
 793    }
 794    else
 795    {
 796  435 if (this.configuration.rawOut)
 797    {
 798  0 addC(c, linelen++);
 799  0 return;
 800    }
 801    }
 802    // #431953 - end RJ
 803    }
 804   
 805    // if preformatted text, map &nbsp; to space
 806  80567 if (c == 160 && TidyUtils.toBoolean(mode & PREFORMATTED))
 807    {
 808  0 addC(' ', linelen++);
 809  0 return;
 810    }
 811   
 812    // Filters from Word and PowerPoint often use smart quotes resulting in character codes between 128 and 159.
 813    // Unfortunately, the corresponding HTML 4.0 entities for these are not widely supported.
 814    // The following converts dashes and quotation marks to the nearest ASCII equivalent.
 815    // My thanks to Andrzej Novosiolov for his help with this code.
 816   
 817  80567 if (this.configuration.makeClean && this.configuration.asciiChars || this.configuration.makeBare)
 818    {
 819  11395 if (c >= 0x2013 && c <= 0x201E)
 820    {
 821  2 switch (c)
 822    {
 823  0 case 0x2013 : // en dash
 824  0 case 0x2014 : // em dash
 825  0 c = '-';
 826  0 break;
 827  0 case 0x2018 : // left single quotation mark
 828  0 case 0x2019 : // right single quotation mark
 829  0 case 0x201A : // single low-9 quotation mark
 830  0 c = '\'';
 831  0 break;
 832  1 case 0x201C : // left double quotation mark
 833  1 case 0x201D : // right double quotation mark
 834  0 case 0x201E : // double low-9 quotation mark
 835  2 c = '"';
 836  2 break;
 837    }
 838    }
 839    }
 840   
 841    // don't map latin-1 chars to entities
 842  80567 if ("ISO8859_1".equals(this.configuration.getOutCharEncodingName()))
 843    {
 844  2364 if (c > 255) /* multi byte chars */
 845    {
 846  0 if (!this.configuration.numEntities)
 847    {
 848  0 entity = EntityTable.getDefaultEntityTable().entityName((short) c);
 849  0 if (entity != null)
 850    {
 851  0 entity = "&" + entity + ";";
 852    }
 853    else
 854    {
 855  0 entity = "&#" + c + ";";
 856    }
 857    }
 858    else
 859    {
 860  0 entity = "&#" + c + ";";
 861    }
 862   
 863  0 for (int i = 0; i < entity.length(); i++)
 864    {
 865  0 addC(entity.charAt(i), linelen++);
 866    }
 867   
 868  0 return;
 869    }
 870   
 871  2364 if (c > 126 && c < 160)
 872    {
 873  0 entity = "&#" + c + ";";
 874   
 875  0 for (int i = 0; i < entity.length(); i++)
 876    {
 877  0 addC(entity.charAt(i), linelen++);
 878    }
 879   
 880  0 return;
 881    }
 882   
 883  2364 addC(c, linelen++);
 884  2364 return;
 885    }
 886   
 887    // don't map utf8 or utf16 chars to entities
 888  78203 if (this.configuration.getOutCharEncodingName().startsWith("UTF"))
 889    {
 890  437 addC(c, linelen++);
 891  437 return;
 892    }
 893   
 894    // use numeric entities only for XML
 895  77766 if (this.configuration.xmlTags)
 896    {
 897    // if ASCII use numeric entities for chars > 127
 898  864 if (c > 127 && "ASCII".equals(this.configuration.getOutCharEncodingName()))
 899    {
 900  1 entity = "&#" + c + ";";
 901   
 902  1 for (int i = 0; i < entity.length(); i++)
 903    {
 904  6 addC(entity.charAt(i), linelen++);
 905    }
 906   
 907  1 return;
 908    }
 909   
 910    // otherwise output char raw
 911  863 addC(c, linelen++);
 912  863 return;
 913    }
 914   
 915    // default treatment for ASCII
 916  76902 if ("ASCII".equals(this.configuration.getOutCharEncodingName()) && (c > 126 || (c < ' ' && c != '\t')))
 917    {
 918  496 if (!this.configuration.numEntities)
 919    {
 920  496 entity = EntityTable.getDefaultEntityTable().entityName((short) c);
 921  496 if (entity != null)
 922    {
 923  485 entity = "&" + entity + ";";
 924    }
 925    else
 926    {
 927  11 entity = "&#" + c + ";";
 928    }
 929    }
 930    else
 931    {
 932  0 entity = "&#" + c + ";";
 933    }
 934   
 935  496 for (int i = 0; i < entity.length(); i++)
 936    {
 937  3465 addC(entity.charAt(i), linelen++);
 938    }
 939   
 940  496 return;
 941    }
 942   
 943  76406 addC(c, linelen++);
 944    }
 945   
 946    /**
 947    * The line buffer is uint not char so we can hold Unicode values unencoded. The translation to UTF-8 is deferred to
 948    * the outc routine called to flush the line buffer.
 949    * @param fout
 950    * @param mode
 951    * @param indent
 952    * @param textarray
 953    * @param start
 954    * @param end
 955    */
 956  3082 private void printText(Out fout, short mode, int indent, byte[] textarray, int start, int end)
 957    {
 958  3082 int i, c;
 959  3082 int[] ci = new int[1];
 960   
 961  3082 for (i = start; i < end; ++i)
 962    {
 963  63125 if (indent + linelen >= this.configuration.wraplen)
 964    {
 965  332 wrapLine(fout, indent);
 966    }
 967   
 968  63125 c = (textarray[i]) & 0xFF; // Convert to unsigned.
 969   
 970    // look for UTF-8 multibyte character
 971  63125 if (c > 0x7F)
 972    {
 973  1014 i += getUTF8(textarray, i, ci);
 974  1014 c = ci[0];
 975    }
 976   
 977  63125 if (c == '\n')
 978    {
 979  199 flushLine(fout, indent);
 980  199 continue;
 981    }
 982   
 983  62926 printChar(c, mode);
 984    }
 985    }
 986   
 987    /**
 988    * @param str
 989    */
 990  332 private void printString(String str)
 991    {
 992  332 for (int i = 0; i < str.length(); i++)
 993    {
 994  8854 addC(str.charAt(i), linelen++);
 995    }
 996    }
 997   
 998    /**
 999    * @param fout
 1000    * @param indent
 1001    * @param value
 1002    * @param delim
 1003    * @param wrappable
 1004    */
 1005  1529 private void printAttrValue(Out fout, int indent, String value, int delim, boolean wrappable)
 1006    {
 1007  1529 int c;
 1008  1529 int[] ci = new int[1];
 1009  1529 boolean wasinstring = false;
 1010  1529 byte[] valueChars = null;
 1011  1529 int i;
 1012  1529 short mode = (wrappable ? (short) (NORMAL | ATTRIBVALUE) : (short) (PREFORMATTED | ATTRIBVALUE));
 1013   
 1014  1529 if (value != null)
 1015    {
 1016  1529 valueChars = TidyUtils.getBytes(value);
 1017    }
 1018   
 1019    // look for ASP, Tango or PHP instructions for computed attribute value
 1020  1529 if (valueChars != null && valueChars.length >= 5 && valueChars[0] == '<')
 1021    {
 1022  1 if (valueChars[1] == '%' || valueChars[1] == '@' || (new String(valueChars, 0, 5)).equals("<?php"))
 1023    {
 1024  0 mode |= CDATA;
 1025    }
 1026    }
 1027   
 1028  1529 if (delim == 0)
 1029    {
 1030  0 delim = '"';
 1031    }
 1032   
 1033  1529 addC('=', linelen++);
 1034   
 1035    // don't wrap after "=" for xml documents
 1036  1529 if (!this.configuration.xmlOut)
 1037    {
 1038   
 1039  1203 if (indent + linelen < this.configuration.wraplen)
 1040    {
 1041  1203 wraphere = linelen;
 1042    }
 1043   
 1044  1203 if (indent + linelen >= this.configuration.wraplen)
 1045    {
 1046  0 wrapLine(fout, indent);
 1047    }
 1048   
 1049  1203 if (indent + linelen < this.configuration.wraplen)
 1050    {
 1051  1203 wraphere = linelen;
 1052    }
 1053    else
 1054    {
 1055  0 condFlushLine(fout, indent);
 1056    }
 1057    }
 1058   
 1059  1529 addC(delim, linelen++);
 1060   
 1061  1529 if (value != null)
 1062    {
 1063  1529 inString = false;
 1064   
 1065  1529 i = 0;
 1066  1529 while (i < valueChars.length)
 1067    {
 1068  19593 c = (valueChars[i]) & 0xFF; // Convert to unsigned.
 1069   
 1070  19593 if (wrappable && c == ' ' && indent + linelen < this.configuration.wraplen)
 1071    {
 1072  0 wraphere = linelen;
 1073  0 wasinstring = inString;
 1074    }
 1075   
 1076  19593 if (wrappable && wraphere > 0 && indent + linelen >= this.configuration.wraplen)
 1077    {
 1078  0 wrapAttrVal(fout, indent, wasinstring);
 1079    }
 1080   
 1081  19593 if (c == delim)
 1082    {
 1083  10 String entity;
 1084   
 1085  10 entity = (c == '"' ? "&quot;" : "&#39;");
 1086   
 1087  10 for (int j = 0; j < entity.length(); j++)
 1088    {
 1089  60 addC(entity.charAt(j), linelen++);
 1090    }
 1091   
 1092  10 ++i;
 1093  10 continue;
 1094    }
 1095  19583 else if (c == '"')
 1096    {
 1097  0 if (this.configuration.quoteMarks)
 1098    {
 1099  0 addC('&', linelen++);
 1100  0 addC('q', linelen++);
 1101  0 addC('u', linelen++);
 1102  0 addC('o', linelen++);
 1103  0 addC('t', linelen++);
 1104  0 addC(';', linelen++);
 1105    }
 1106    else
 1107    {
 1108  0 addC('"', linelen++);
 1109    }
 1110   
 1111  0 if (delim == '\'')
 1112    {
 1113  0 inString = !inString;
 1114    }
 1115   
 1116  0 ++i;
 1117  0 continue;
 1118    }
 1119  19583 else if (c == '\'')
 1120    {
 1121  49 if (this.configuration.quoteMarks)
 1122    {
 1123  0 addC('&', linelen++);
 1124  0 addC('#', linelen++);
 1125  0 addC('3', linelen++);
 1126  0 addC('9', linelen++);
 1127  0 addC(';', linelen++);
 1128    }
 1129    else
 1130    {
 1131  49 addC('\'', linelen++);
 1132    }
 1133   
 1134  49 if (delim == '"')
 1135    {
 1136  49 inString = !inString;
 1137    }
 1138   
 1139  49 ++i;
 1140  49 continue;
 1141    }
 1142   
 1143    // look for UTF-8 multibyte character
 1144  19534 if (c > 0x7F)
 1145    {
 1146  3 i += getUTF8(valueChars, i, ci);
 1147  3 c = ci[0];
 1148    }
 1149   
 1150  19534 ++i;
 1151   
 1152  19534 if (c == '\n')
 1153    {
 1154  3 flushLine(fout, indent);
 1155  3 continue;
 1156    }
 1157   
 1158  19531 printChar(c, mode);
 1159    }
 1160    }
 1161   
 1162  1529 inString = false;
 1163  1529 addC(delim, linelen++);
 1164    }
 1165   
 1166    /**
 1167    * @param fout
 1168    * @param indent
 1169    * @param node
 1170    * @param attr
 1171    */
 1172  1534 private void printAttribute(Out fout, int indent, Node node, AttVal attr)
 1173    {
 1174  1534 String name;
 1175  1534 boolean wrappable = false;
 1176   
 1177  1534 if (this.configuration.indentAttributes)
 1178    {
 1179  275 flushLine(fout, indent);
 1180  275 indent += this.configuration.spaces;
 1181    }
 1182   
 1183  1534 name = attr.attribute;
 1184   
 1185  1534 if (indent + linelen >= this.configuration.wraplen)
 1186    {
 1187  3 wrapLine(fout, indent);
 1188    }
 1189   
 1190  1534 if (!this.configuration.xmlTags && !this.configuration.xmlOut && attr.dict != null)
 1191    {
 1192  1203 if (AttributeTable.getDefaultAttributeTable().isScript(name))
 1193    {
 1194  5 wrappable = this.configuration.wrapScriptlets;
 1195    }
 1196  1198 else if (!attr.dict.isNowrap() && this.configuration.wrapAttVals)
 1197    {
 1198  0 wrappable = true;
 1199    }
 1200    }
 1201   
 1202  1534 if (indent + linelen < this.configuration.wraplen)
 1203    {
 1204  1534 wraphere = linelen;
 1205  1534 addC(' ', linelen++);
 1206    }
 1207    else
 1208    {
 1209  0 condFlushLine(fout, indent);
 1210  0 addC(' ', linelen++);
 1211    }
 1212   
 1213  1534 for (int i = 0; i < name.length(); i++)
 1214    {
 1215  7891 addC(
 1216    TidyUtils.foldCase(name.charAt(i), this.configuration.upperCaseAttrs, this.configuration.xmlTags),
 1217    linelen++);
 1218    }
 1219   
 1220  1534 if (indent + linelen >= this.configuration.wraplen)
 1221    {
 1222  4 wrapLine(fout, indent);
 1223    }
 1224   
 1225  1534 if (attr.value == null)
 1226    {
 1227  10 if (this.configuration.xmlTags || this.configuration.xmlOut)
 1228    {
 1229  5 printAttrValue(fout, indent, (attr.isBoolAttribute() ? attr.attribute : ""), attr.delim, true);
 1230    }
 1231  5 else if (!attr.isBoolAttribute() && node != null && !node.isNewNode())
 1232    {
 1233  0 printAttrValue(fout, indent, "", attr.delim, true);
 1234    }
 1235  5 else if (indent + linelen < this.configuration.wraplen)
 1236    {
 1237  5 wraphere = linelen;
 1238    }
 1239   
 1240    }
 1241    else
 1242    {
 1243  1524 printAttrValue(fout, indent, attr.value, attr.delim, wrappable);
 1244    }
 1245    }
 1246   
 1247    /**
 1248    * @param fout
 1249    * @param indent
 1250    * @param node
 1251    * @param attr
 1252    */
 1253  4230 private void printAttrs(Out fout, int indent, Node node, AttVal attr)
 1254    {
 1255    // add xml:space attribute to pre and other elements
 1256  4230 if (configuration.xmlOut
 1257    && configuration.xmlSpace
 1258    && ParserImpl.XMLPreserveWhiteSpace(node, configuration.tt)
 1259    && node.getAttrByName("xml:space") == null)
 1260    {
 1261  0 node.addAttribute("xml:space", "preserve");
 1262  0 if (attr != null)
 1263    {
 1264  0 attr = node.attributes;
 1265    }
 1266    }
 1267   
 1268  4230 if (attr != null)
 1269    {
 1270  1539 if (attr.next != null)
 1271    {
 1272  469 printAttrs(fout, indent, node, attr.next);
 1273    }
 1274   
 1275  1539 if (attr.attribute != null)
 1276    {
 1277  1539 Attribute attribute = attr.dict;
 1278   
 1279  1539 if (!this.configuration.dropProprietaryAttributes
 1280    || !(attribute == null || TidyUtils.toBoolean(attribute.getVersions() & Dict.VERS_PROPRIETARY)))
 1281    {
 1282  1534 printAttribute(fout, indent, node, attr);
 1283    }
 1284    }
 1285  0 else if (attr.asp != null)
 1286    {
 1287  0 addC(' ', linelen++);
 1288  0 printAsp(fout, indent, attr.asp);
 1289    }
 1290  0 else if (attr.php != null)
 1291    {
 1292  0 addC(' ', linelen++);
 1293  0 printPhp(fout, indent, attr.php);
 1294    }
 1295    }
 1296   
 1297    }
 1298   
 1299    /**
 1300    * Line can be wrapped immediately after inline start tag provided if follows a text node ending in a space, or it
 1301    * parent is an inline element that that rule applies to. This behaviour was reverse engineered from Netscape 3.0
 1302    * @param node current Node
 1303    * @return <code>true</code> if the current char follows a space
 1304    */
 1305  3105 private static boolean afterSpace(Node node)
 1306    {
 1307  3105 Node prev;
 1308  3105 int c;
 1309   
 1310  3105 if (node == null || node.tag == null || !TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE))
 1311    {
 1312  2757 return true;
 1313    }
 1314   
 1315  348 prev = node.prev;
 1316   
 1317  348 if (prev != null)
 1318    {
 1319  346 if (prev.type == Node.TEXT_NODE && prev.end > prev.start)
 1320    {
 1321  13 c = (prev.textarray[prev.end - 1]) & 0xFF; // Convert to unsigned.
 1322   
 1323  13 if (c == 160 || c == ' ' || c == '\n')
 1324    {
 1325  1 return true;
 1326    }
 1327    }
 1328   
 1329  345 return false;
 1330    }
 1331   
 1332  2 return afterSpace(node.parent);
 1333    }
 1334   
 1335    /**
 1336    * @param lexer
 1337    * @param fout
 1338    * @param mode
 1339    * @param indent
 1340    * @param node
 1341    */
 1342  3726 private void printTag(Lexer lexer, Out fout, short mode, int indent, Node node)
 1343    {
 1344  3726 String p;
 1345  3726 TagTable tt = this.configuration.tt;
 1346   
 1347  3726 addC('<', linelen++);
 1348   
 1349  3726 if (node.type == Node.END_TAG)
 1350    {
 1351  0 addC('/', linelen++);
 1352    }
 1353   
 1354  3726 p = node.element;
 1355  3726 for (int i = 0; i < p.length(); i++)
 1356    {
 1357  10548 addC(
 1358    TidyUtils.foldCase(p.charAt(i), this.configuration.upperCaseTags, this.configuration.xmlTags),
 1359    linelen++);
 1360    }
 1361   
 1362  3726 printAttrs(fout, indent, node, node.attributes);
 1363   
 1364  3726 if ((this.configuration.xmlOut || this.configuration.xHTML)
 1365    && (node.type == Node.START_END_TAG || TidyUtils.toBoolean(node.tag.model & Dict.CM_EMPTY)))
 1366    {
 1367  47 addC(' ', linelen++); // Space is NS compatibility hack <br />
 1368  47 addC('/', linelen++); // Required end tag marker
 1369    }
 1370   
 1371  3726 addC('>', linelen++);
 1372   
 1373  3726 if ((node.type != Node.START_END_TAG || configuration.xHTML) && !TidyUtils.toBoolean(mode & PREFORMATTED))
 1374    {
 1375  3685 if (indent + linelen >= this.configuration.wraplen)
 1376    {
 1377  83 wrapLine(fout, indent);
 1378    }
 1379   
 1380  3685 if (indent + linelen < this.configuration.wraplen)
 1381    {
 1382   
 1383    // wrap after start tag if is <br/> or if it's not inline
 1384    // fix for [514348]
 1385  3678 if (!TidyUtils.toBoolean(mode & NOWRAP)
 1386    && (!TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE) || (node.tag == tt.tagBr))
 1387    && afterSpace(node))
 1388    {
 1389  2758 wraphere = linelen;
 1390    }
 1391   
 1392    }
 1393    }
 1394    else
 1395    {
 1396  41 condFlushLine(fout, indent);
 1397    }
 1398   
 1399    }
 1400   
 1401    /**
 1402    * @param mode
 1403    * @param indent
 1404    * @param node
 1405    */
 1406  3127 private void printEndTag(short mode, int indent, Node node)
 1407    {
 1408  3127 String p;
 1409   
 1410    // Netscape ignores SGML standard by not ignoring a line break before </A> or </U> etc.
 1411    // To avoid rendering this as an underlined space, I disable line wrapping before inline end tags
 1412   
 1413    // if (indent + linelen < this.configuration.wraplen && !TidyUtils.toBoolean(mode & NOWRAP))
 1414    // {
 1415    // wraphere = linelen;
 1416    // }
 1417   
 1418  3127 addC('<', linelen++);
 1419  3127 addC('/', linelen++);
 1420   
 1421  3127 p = node.element;
 1422  3127 for (int i = 0; i < p.length(); i++)
 1423    {
 1424  8801 addC(
 1425    TidyUtils.foldCase(p.charAt(i), this.configuration.upperCaseTags, this.configuration.xmlTags),
 1426    linelen++);
 1427    }
 1428   
 1429  3127 addC('>', linelen++);
 1430    }
 1431   
 1432    /**
 1433    * @param fout
 1434    * @param indent
 1435    * @param node
 1436    */
 1437  350 private void printComment(Out fout, int indent, Node node)
 1438    {
 1439  350 if (this.configuration.hideComments)
 1440    {
 1441  0 return;
 1442    }
 1443   
 1444  350 if (indent + linelen < this.configuration.wraplen)
 1445    {
 1446  350 wraphere = linelen;
 1447    }
 1448   
 1449  350 addC('<', linelen++);
 1450  350 addC('!', linelen++);
 1451  350 addC('-', linelen++);
 1452  350 addC('-', linelen++);
 1453   
 1454  350 printText(fout, COMMENT, indent, node.textarray, node.start, node.end);
 1455   
 1456    // See Lexer.java: AQ 8Jul2000
 1457  350 addC('-', linelen++);
 1458  350 addC('-', linelen++);
 1459  350 addC('>', linelen++);
 1460   
 1461  350 if (node.linebreak)
 1462    {
 1463  342 flushLine(fout, indent);
 1464    }
 1465    }
 1466   
 1467    /**
 1468    * @param fout
 1469    * @param indent
 1470    * @param lexer
 1471    * @param node
 1472    */
 1473  213 private void printDocType(Out fout, int indent, Lexer lexer, Node node)
 1474    {
 1475  213 int i, c = 0;
 1476  213 short mode = 0;
 1477  213 boolean q = this.configuration.quoteMarks;
 1478   
 1479  213 this.configuration.quoteMarks = false;
 1480   
 1481  213 if (indent + linelen < this.configuration.wraplen)
 1482    {
 1483  213 wraphere = linelen;
 1484    }
 1485   
 1486  213 condFlushLine(fout, indent);
 1487   
 1488  213 addC('<', linelen++);
 1489  213 addC('!', linelen++);
 1490  213 addC('D', linelen++);
 1491  213 addC('O', linelen++);
 1492  213 addC('C', linelen++);
 1493  213 addC('T', linelen++);
 1494  213 addC('Y', linelen++);
 1495  213 addC('P', linelen++);
 1496  213 addC('E', linelen++);
 1497  213 addC(' ', linelen++);
 1498   
 1499  213 if (indent + linelen < this.configuration.wraplen)
 1500    {
 1501  213 wraphere = linelen;
 1502    }
 1503   
 1504  213 for (i = node.start; i < node.end; ++i)
 1505    {
 1506  14130 if (indent + linelen >= this.configuration.wraplen)
 1507    {
 1508  23 wrapLine(fout, indent);
 1509    }
 1510   
 1511  14130 c = node.textarray[i] & 0xFF; // Convert to unsigned.
 1512   
 1513    // inDTDSubset?
 1514  14130 if (TidyUtils.toBoolean(mode & CDATA))
 1515    {
 1516  59 if (c == ']')
 1517    {
 1518  1 mode &= ~CDATA;
 1519    }
 1520    }
 1521  14071 else if (c == '[')
 1522    {
 1523  1 mode |= CDATA;
 1524    }
 1525  14130 int[] ci = new int[1];
 1526   
 1527    // look for UTF-8 multibyte character
 1528  14130 if (c > 0x7F)
 1529    {
 1530  0 i += getUTF8(node.textarray, i, ci);
 1531  0 c = ci[0];
 1532    }
 1533   
 1534  14130 if (c == '\n')
 1535    {
 1536  11 flushLine(fout, indent);
 1537  11 continue;
 1538    }
 1539   
 1540  14119 printChar(c, mode);
 1541    }
 1542   
 1543  213 if (linelen < this.configuration.wraplen)
 1544    {
 1545  213 wraphere = linelen;
 1546    }
 1547   
 1548  213 addC('>', linelen++);
 1549  213 this.configuration.quoteMarks = q;
 1550  213 condFlushLine(fout, indent);
 1551    }
 1552   
 1553    /**
 1554    * @param fout
 1555    * @param indent
 1556    * @param node
 1557    */
 1558  2 private void printPI(Out fout, int indent, Node node)
 1559    {
 1560  2 if (indent + linelen < this.configuration.wraplen)
 1561    {
 1562  2 wraphere = linelen;
 1563    }
 1564   
 1565  2 addC('<', linelen++);
 1566  2 addC('?', linelen++);
 1567   
 1568    // set CDATA to pass < and > unescaped
 1569  2 printText(fout, CDATA, indent, node.textarray, node.start, node.end);
 1570   
 1571  2 if (node.end <= 0 || node.textarray[node.end - 1] != '?') // #542029 - fix by Terry Teague 10 Apr 02
 1572    {
 1573  1 addC('?', linelen++);
 1574    }
 1575   
 1576  2 addC('>', linelen++);
 1577  2 condFlushLine(fout, indent);
 1578    }
 1579   
 1580    /**
 1581    * Pretty print the xml declaration.
 1582    * @param fout
 1583    * @param indent
 1584    * @param node
 1585    */
 1586  35 private void printXmlDecl(Out fout, int indent, Node node)
 1587    {
 1588  35 if (indent + linelen < this.configuration.wraplen)
 1589    {
 1590  35 wraphere = linelen;
 1591    }
 1592   
 1593  35 addC('<', linelen++);
 1594  35 addC('?', linelen++);
 1595  35 addC('x', linelen++);
 1596  35 addC('m', linelen++);
 1597  35 addC('l', linelen++);
 1598   
 1599  35 printAttrs(fout, indent, node, node.attributes);
 1600   
 1601  35 if (node.end <= 0 || node.textarray[node.end - 1] != '?') // #542029 - fix by Terry Teague 10 Apr 02
 1602    {
 1603  35 addC('?', linelen++);
 1604    }
 1605   
 1606  35 addC('>', linelen++);
 1607   
 1608  35 condFlushLine(fout, indent);
 1609    }
 1610   
 1611    /**
 1612    * note ASP and JSTE share <% ... %> syntax.
 1613    * @param fout
 1614    * @param indent
 1615    * @param node
 1616    */
 1617  0 private void printAsp(Out fout, int indent, Node node)
 1618    {
 1619  0 int savewraplen = this.configuration.wraplen;
 1620   
 1621    // disable wrapping if so requested
 1622   
 1623  0 if (!this.configuration.wrapAsp || !this.configuration.wrapJste)
 1624    {
 1625  0 this.configuration.wraplen = 0xFFFFFF; // a very large number
 1626    }
 1627   
 1628  0 addC('<', linelen++);
 1629  0 addC('%', linelen++);
 1630   
 1631  0 printText(fout, (this.configuration.wrapAsp ? CDATA : COMMENT), indent, node.textarray, node.start, node.end);
 1632   
 1633  0 addC('%', linelen++);
 1634  0 addC('>', linelen++);
 1635    /* condFlushLine(fout, indent); */
 1636  0 this.configuration.wraplen = savewraplen;
 1637    }
 1638   
 1639    /**
 1640    * JSTE also supports <# ... #> syntax
 1641    * @param fout
 1642    * @param indent
 1643    * @param node
 1644    */
 1645  1 private void printJste(Out fout, int indent, Node node)
 1646    {
 1647  1 int savewraplen = this.configuration.wraplen;
 1648   
 1649    // disable wrapping if so requested
 1650   
 1651  1 if (!this.configuration.wrapJste)
 1652    {
 1653  0 this.configuration.wraplen = 0xFFFFFF; // a very large number
 1654    }
 1655   
 1656  1 addC('<', linelen++);
 1657  1 addC('#', linelen++);
 1658   
 1659  1 printText(fout, (this.configuration.wrapJste ? CDATA : COMMENT), indent, node.textarray, node.start, node.end);
 1660   
 1661  1 addC('#', linelen++);
 1662  1 addC('>', linelen++);
 1663    // condFlushLine(fout, indent);
 1664  1 this.configuration.wraplen = savewraplen;
 1665    }
 1666   
 1667    /**
 1668    * PHP is based on XML processing instructions.
 1669    * @param fout
 1670    * @param indent
 1671    * @param node
 1672    */
 1673  1 private void printPhp(Out fout, int indent, Node node)
 1674    {
 1675  1 int savewraplen = this.configuration.wraplen;
 1676   
 1677    // disable wrapping if so requested
 1678   
 1679  1 if (!this.configuration.wrapPhp)
 1680    {
 1681  0 this.configuration.wraplen = 0xFFFFFF; // a very large number
 1682    }
 1683   
 1684  1 addC('<', linelen++);
 1685  1 addC('?', linelen++);
 1686   
 1687  1 printText(fout, (this.configuration.wrapPhp ? CDATA : COMMENT), indent, node.textarray, node.start, node.end);
 1688   
 1689  1 addC('?', linelen++);
 1690  1 addC('>', linelen++);
 1691    // PCondFlushLine(fout, indent);
 1692  1 this.configuration.wraplen = savewraplen;
 1693    }
 1694   
 1695    /**
 1696    * @param fout
 1697    * @param indent
 1698    * @param node
 1699    */
 1700  0 private void printCDATA(Out fout, int indent, Node node)
 1701    {
 1702  0 int savewraplen = this.configuration.wraplen;
 1703   
 1704  0 if (!this.configuration.indentCdata)
 1705    {
 1706  0 indent = 0;
 1707    }
 1708   
 1709  0 condFlushLine(fout, indent);
 1710   
 1711    // disable wrapping
 1712  0 this.configuration.wraplen = 0xFFFFFF; // a very large number
 1713   
 1714  0 addC('<', linelen++);
 1715  0 addC('!', linelen++);
 1716  0 addC('[', linelen++);
 1717  0 addC('C', linelen++);
 1718  0 addC('D', linelen++);
 1719  0 addC('A', linelen++);
 1720  0 addC('T', linelen++);
 1721  0 addC('A', linelen++);
 1722  0 addC('[', linelen++);
 1723   
 1724  0 printText(fout, COMMENT, indent, node.textarray, node.start, node.end);
 1725   
 1726  0 addC(']', linelen++);
 1727  0 addC(']', linelen++);
 1728  0 addC('>', linelen++);
 1729  0 condFlushLine(fout, indent);
 1730  0 this.configuration.wraplen = savewraplen;
 1731    }
 1732   
 1733    /**
 1734    * @param fout
 1735    * @param indent
 1736    * @param node
 1737    */
 1738  0 private void printSection(Out fout, int indent, Node node)
 1739    {
 1740  0 int savewraplen = this.configuration.wraplen;
 1741   
 1742    // disable wrapping if so requested
 1743   
 1744  0 if (!this.configuration.wrapSection)
 1745    {
 1746  0 this.configuration.wraplen = 0xFFFFFF; // a very large number
 1747    }
 1748   
 1749  0 addC('<', linelen++);
 1750  0 addC('!', linelen++);
 1751  0 addC('[', linelen++);
 1752   
 1753  0 printText(
 1754    fout,
 1755  0 (this.configuration.wrapSection ? CDATA : COMMENT),
 1756    indent,
 1757    node.textarray,
 1758    node.start,
 1759    node.end);
 1760   
 1761  0 addC(']', linelen++);
 1762  0 addC('>', linelen++);
 1763    // PCondFlushLine(fout, indent);
 1764  0 this.configuration.wraplen = savewraplen;
 1765    }
 1766   
 1767    /**
 1768    * Is the current node inside HEAD?
 1769    * @param node Node
 1770    * @return <code>true</code> if node is inside an HEAD tag
 1771    */
 1772  91 private boolean insideHead(Node node)
 1773    {
 1774  91 if (node.tag == this.configuration.tt.tagHead)
 1775    {
 1776  17 return true;
 1777    }
 1778   
 1779  74 if (node.parent != null)
 1780    {
 1781  61 return insideHead(node.parent);
 1782    }
 1783  13 return false;
 1784    }
 1785   
 1786    /**
 1787    * Is text node and already ends w/ a newline? Used to pretty print CDATA/PRE text content. If it already ends on a
 1788    * newline, it is not necessary to print another before printing end tag.
 1789    * @param lexer Lexer
 1790    * @param node text node
 1791    * @return text indent
 1792    */
 1793  27 private int textEndsWithNewline(Lexer lexer, Node node)
 1794    {
 1795  27 if (node.type == Node.TEXT_NODE && node.end > node.start)
 1796    {
 1797  27 int ch, ix = node.end - 1;
 1798    // Skip non-newline whitespace
 1799  ? while (ix >= node.start
 1800    && TidyUtils.toBoolean(ch = (node.textarray[ix] & 0xff))
 1801    && (ch == ' ' || ch == '\t' || ch == '\r'))
 1802    {
 1803  35 --ix;
 1804    }
 1805   
 1806  27 if (node.textarray[ix] == '\n')
 1807    {
 1808  18 return node.end - ix - 1; // #543262 tidy eats all memory
 1809    }
 1810    }
 1811  9 return -1;
 1812    }
 1813   
 1814    /**
 1815    * Does the current node contain a CDATA section?
 1816    * @param lexer Lexer
 1817    * @param node Node
 1818    * @return <code>true</code> if node contains a CDATA section
 1819    */
 1820  14 static boolean hasCDATA(Lexer lexer, Node node)
 1821    {
 1822    // Scan forward through the textarray. Since the characters we're
 1823    // looking for are < 0x7f, we don't have to do any UTF-8 decoding.
 1824   
 1825  14 if (node.type != Node.TEXT_NODE)
 1826    {
 1827  0 return false;
 1828    }
 1829   
 1830  14 int len = node.end - node.start + 1;
 1831  14 String start = TidyUtils.getString(node.textarray, node.start, len);
 1832   
 1833  14 int indexOfCData = start.indexOf(CDATA_START);
 1834  14 return indexOfCData > -1 && indexOfCData <= len;
 1835    }
 1836   
 1837    /**
 1838    * Print script and style elements. For XHTML, wrap the content as follows:
 1839    *
 1840    * <pre>
 1841    * JavaScript:
 1842    * //&lt;![CDATA[
 1843    * content
 1844    * //]]>
 1845    * VBScript:
 1846    * '&lt;![CDATA[
 1847    * content
 1848    * ']]>
 1849    * CSS:
 1850    * /*&lt;![CDATA[* /
 1851    * content
 1852    * /*]]>* /
 1853    * other:
 1854    * &lt;![CDATA[
 1855    * content
 1856    * ]]>
 1857    * </pre>
 1858    *
 1859    * @param fout
 1860    * @param mode
 1861    * @param indent
 1862    * @param lexer
 1863    * @param node
 1864    */
 1865  30 private void printScriptStyle(Out fout, short mode, int indent, Lexer lexer, Node node)
 1866    {
 1867  30 Node content;
 1868  30 String commentStart = DEFAULT_COMMENT_START;
 1869  30 String commentEnd = DEFAULT_COMMENT_END;
 1870  30 boolean hasCData = false;
 1871  30 int contentIndent = -1;
 1872   
 1873  30 if (insideHead(node))
 1874    {
 1875    // flushLine(fout, indent);
 1876    }
 1877   
 1878  30 indent = 0;
 1879   
 1880    // start script
 1881  30 printTag(lexer, fout, mode, indent, node);
 1882    // flushLine(fout, indent); // extra newline
 1883   
 1884  30 if (lexer.configuration.xHTML && node.content != null)
 1885    {
 1886  14 AttVal type = node.getAttrByName("type");
 1887  14 if (type != null)
 1888    {
 1889  13 if ("text/javascript".equalsIgnoreCase(type.value))
 1890    {
 1891  10 commentStart = JS_COMMENT_START;
 1892  10 commentEnd = JS_COMMENT_END;
 1893    }
 1894  3 else if ("text/css".equalsIgnoreCase(type.value))
 1895    {
 1896  2 commentStart = CSS_COMMENT_START;
 1897  2 commentEnd = CSS_COMMENT_END;
 1898    }
 1899  1 else if ("text/vbscript".equalsIgnoreCase(type.value))
 1900    {
 1901  0 commentStart = VB_COMMENT_START;
 1902  0 commentEnd = VB_COMMENT_END;
 1903    }
 1904    }
 1905   
 1906  14 hasCData = hasCDATA(lexer, node.content);
 1907  14 if (!hasCData)
 1908    {
 1909    // disable wrapping
 1910  13 int savewraplen = lexer.configuration.wraplen;
 1911  13 lexer.configuration.wraplen = 0xFFFFFF; // a very large number
 1912   
 1913  13 linelen = addAsciiString(commentStart, linelen);
 1914  13 linelen = addAsciiString(CDATA_START, linelen);
 1915  13 linelen = addAsciiString(commentEnd, linelen);
 1916  13 condFlushLine(fout, indent);
 1917   
 1918    // restore wrapping
 1919  13 lexer.configuration.wraplen = savewraplen;
 1920    }
 1921    }
 1922   
 1923  30 for (content = node.content; content != null; content = content.next)
 1924    {
 1925  27 printTree(fout, (short) (mode | PREFORMATTED | NOWRAP | CDATA), 0, lexer, content);
 1926   
 1927  27 if (content.next == null)
 1928    {
 1929  27 contentIndent = textEndsWithNewline(lexer, content);
 1930    }
 1931   
 1932    }
 1933   
 1934  30 if (contentIndent < 0)
 1935    {
 1936  12 condFlushLine(fout, indent);
 1937  12 contentIndent = 0;
 1938    }
 1939   
 1940  30 if (lexer.configuration.xHTML && node.content != null)
 1941    {
 1942  14 if (!hasCData)
 1943    {
 1944    // disable wrapping
 1945  13 int ix, savewraplen = lexer.configuration.wraplen;
 1946  13 lexer.configuration.wraplen = 0xFFFFFF; // a very large number
 1947   
 1948    // Add spaces to last text node to align w/ indent
 1949  13 if (contentIndent > 0 && linelen < contentIndent)
 1950    {
 1951  0 linelen = contentIndent;
 1952    }
 1953  13 for (ix = 0; contentIndent < indent && ix < indent - contentIndent; ++ix)
 1954    {
 1955  0 addC(' ', linelen++);
 1956    }
 1957   
 1958  13 linelen = addAsciiString(commentStart, linelen);
 1959  13 linelen = addAsciiString(CDATA_END, linelen);
 1960  13 linelen = addAsciiString(commentEnd, linelen);
 1961   
 1962    // restore wrapping
 1963  13 lexer.configuration.wraplen = savewraplen;
 1964  13 condFlushLine(fout, 0);
 1965    }
 1966    }
 1967   
 1968  30 printEndTag(mode, indent, node);
 1969   
 1970  30 if (!lexer.configuration.indentContent && node.next != null
 1971   
 1972    && !((node.tag != null && TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE))
 1973   
 1974    || node.type != Node.TEXT_NODE
 1975   
 1976    ))
 1977    {
 1978  0 flushLine(fout, indent);
 1979    }
 1980   
 1981  30 flushLine(fout, indent);
 1982    }
 1983   
 1984    /**
 1985    * Should tidy indent the give tag?
 1986    * @param node actual node
 1987    * @return <code>true</code> if line should be indented
 1988    */
 1989  11420 private boolean shouldIndent(Node node)
 1990    {
 1991  11420 TagTable tt = this.configuration.tt;
 1992   
 1993  11420 if (!this.configuration.indentContent)
 1994    {
 1995  9455 return false;
 1996    }
 1997   
 1998  1965 if (this.configuration.smartIndent)
 1999    {
 2000  1923 if (node.content != null && TidyUtils.toBoolean(node.tag.model & Dict.CM_NO_INDENT))
 2001    {
 2002  27 for (node = node.content; node != null; node = node.next)
 2003    {
 2004  35 if (node.tag != null && TidyUtils.toBoolean(node.tag.model & Dict.CM_BLOCK))
 2005    {
 2006  7 return true;
 2007    }
 2008    }
 2009   
 2010  20 return false;
 2011    }
 2012   
 2013  1896 if (TidyUtils.toBoolean(node.tag.model & Dict.CM_HEADING))
 2014    {
 2015  18 return false;
 2016    }
 2017   
 2018  1878 if (node.tag == tt.tagP)
 2019    {
 2020  1370 return false;
 2021    }
 2022   
 2023  508 if (node.tag == tt.tagTitle)
 2024    {
 2025  38 return false;
 2026    }
 2027    }
 2028   
 2029  512 if (TidyUtils.toBoolean(node.tag.model & (Dict.CM_FIELD | Dict.CM_OBJECT)))
 2030    {
 2031  1 return true;
 2032    }
 2033   
 2034  511 if (node.tag == tt.tagMap)
 2035    {
 2036  0 return true;
 2037    }
 2038   
 2039  511 return !TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE);
 2040    }
 2041   
 2042    /**
 2043    * Print just the content of the body element. Useful when you want to reuse material from other documents.
 2044    * @param fout
 2045    * @param lexer
 2046    * @param root
 2047    * @param xml
 2048    */
 2049  2 void printBody(Out fout, Lexer lexer, Node root, boolean xml)
 2050    {
 2051  2 if (root == null)
 2052    {
 2053  0 return;
 2054    }
 2055   
 2056    // Feature request #434940 - fix by Dave Raggett/Ignacio Vazquez-Abrams 21 Jun 01
 2057    // Sebastiano Vigna <vigna@dsi.unimi.it>
 2058  2 Node body = root.findBody(lexer.configuration.tt);
 2059   
 2060  2 if (body != null)
 2061    {
 2062  2 Node content;
 2063  2 for (content = body.content; content != null; content = content.next)
 2064    {
 2065  3 if (xml)
 2066    {
 2067  2 printXMLTree(fout, (short) 0, 0, lexer, content);
 2068    }
 2069    else
 2070    {
 2071  1 printTree(fout, (short) 0, 0, lexer, content);
 2072    }
 2073    }
 2074    }
 2075    }
 2076   
 2077    /**
 2078    * @param fout
 2079    * @param mode
 2080    * @param indent
 2081    * @param lexer
 2082    * @param node
 2083    */
 2084  7179 public void printTree(Out fout, short mode, int indent, Lexer lexer, Node node)
 2085    {
 2086  7179 Node content, last;
 2087  7179 TagTable tt = this.configuration.tt;
 2088   
 2089  7179 if (node == null)
 2090    {
 2091  0 return;
 2092    }
 2093   
 2094  7179 if (node.type == Node.TEXT_NODE || (node.type == Node.CDATA_TAG && lexer.configuration.escapeCdata))
 2095    {
 2096  2701 printText(fout, mode, indent, node.textarray, node.start, node.end);
 2097    }
 2098  4478 else if (node.type == Node.COMMENT_TAG)
 2099    {
 2100  335 printComment(fout, indent, node);
 2101    }
 2102  4143 else if (node.type == Node.ROOT_NODE)
 2103    {
 2104  226 for (content = node.content; content != null; content = content.next)
 2105    {
 2106  497 printTree(fout, mode, indent, lexer, content);
 2107    }
 2108    }
 2109  3917 else if (node.type == Node.DOCTYPE_TAG)
 2110    {
 2111  212 printDocType(fout, indent, lexer, node);
 2112    }
 2113  3705 else if (node.type == Node.PROC_INS_TAG)
 2114    {
 2115  2 printPI(fout, indent, node);
 2116    }
 2117  3703 else if (node.type == Node.XML_DECL)
 2118    {
 2119  24 printXmlDecl(fout, indent, node);
 2120    }
 2121  3679 else if (node.type == Node.CDATA_TAG)
 2122    {
 2123  0 printCDATA(fout, indent, node);
 2124    }
 2125  3679 else if (node.type == Node.SECTION_TAG)
 2126    {
 2127  0 printSection(fout, indent, node);
 2128    }
 2129  3679 else if (node.type == Node.ASP_TAG)
 2130    {
 2131  0 printAsp(fout, indent, node);
 2132    }
 2133  3679 else if (node.type == Node.JSTE_TAG)
 2134    {
 2135  1 printJste(fout, indent, node);
 2136    }
 2137  3678 else if (node.type == Node.PHP_TAG)
 2138    {
 2139  1 printPhp(fout, indent, node);
 2140    }
 2141  3677 else if (TidyUtils.toBoolean(node.tag.model & Dict.CM_EMPTY)
 2142    || (node.type == Node.START_END_TAG && !configuration.xHTML))
 2143    {
 2144  592 if (!TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE))
 2145    {
 2146  156 condFlushLine(fout, indent);
 2147    }
 2148   
 2149  592 if (node.tag == tt.tagBr
 2150    && node.prev != null
 2151    && node.prev.tag != tt.tagBr
 2152    && this.configuration.breakBeforeBR)
 2153    {
 2154  0 flushLine(fout, indent);
 2155    }
 2156   
 2157  592 if (this.configuration.makeClean && node.tag == tt.tagWbr)
 2158    {
 2159  0 printString(" ");
 2160    }
 2161    else
 2162    {
 2163  592 printTag(lexer, fout, mode, indent, node);
 2164    }
 2165   
 2166  592 if (node.tag == tt.tagParam || node.tag == tt.tagArea)
 2167    {
 2168  4 condFlushLine(fout, indent);
 2169    }
 2170  588 else if (node.tag == tt.tagBr || node.tag == tt.tagHr)
 2171    {
 2172  352 flushLine(fout, indent);
 2173    }
 2174    }
 2175    else
 2176    {
 2177  3085 if (node.type == Node.START_END_TAG)
 2178    {
 2179  0 node.type = Node.START_TAG;
 2180    }
 2181   
 2182    // some kind of container element
 2183  3085 if (node.tag != null && node.tag.getParser() == ParserImpl.PRE)
 2184    {
 2185  5 condFlushLine(fout, indent);
 2186   
 2187  5 indent = 0;
 2188  5 condFlushLine(fout, indent);
 2189  5 printTag(lexer, fout, mode, indent, node);
 2190  5 flushLine(fout, indent);
 2191   
 2192  5 for (content = node.content; content != null; content = content.next)
 2193    {
 2194  21 printTree(fout, (short) (mode | PREFORMATTED | NOWRAP), indent, lexer, content);
 2195    }
 2196   
 2197  5 condFlushLine(fout, indent);
 2198  5 printEndTag(mode, indent, node);
 2199  5 flushLine(fout, indent);
 2200   
 2201  5 if (!this.configuration.indentContent && node.next != null)
 2202    {
 2203  0 flushLine(fout, indent);
 2204    }
 2205    }
 2206  3080 else if (node.tag == tt.tagStyle || node.tag == tt.tagScript)
 2207    {
 2208  30 printScriptStyle(fout, (short) (mode | PREFORMATTED | NOWRAP | CDATA), indent, lexer, node);
 2209    }
 2210  3050 else if (TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE))
 2211    {
 2212  495 if (this.configuration.makeClean)
 2213    {
 2214    // discards <font> and </font> tags
 2215  265 if (node.tag == tt.tagFont)
 2216    {
 2217  0 for (content = node.content; content != null; content = content.next)
 2218    {
 2219  0 printTree(fout, mode, indent, lexer, content);
 2220    }
 2221  0 return;
 2222    }
 2223   
 2224    // replace <nobr> ... </nobr> by &nbsp; or &#160; etc.
 2225  265 if (node.tag == tt.tagNobr)
 2226    {
 2227  0 for (content = node.content; content != null; content = content.next)
 2228    {
 2229  0 printTree(fout, (short) (mode | NOWRAP), indent, lexer, content);
 2230    }
 2231  0 return;
 2232    }
 2233    }
 2234   
 2235    // otherwise a normal inline element
 2236   
 2237  495 printTag(lexer, fout, mode, indent, node);
 2238   
 2239    // indent content for SELECT, TEXTAREA, MAP, OBJECT and APPLET
 2240   
 2241  495 if (shouldIndent(node))
 2242    {
 2243  1 condFlushLine(fout, indent);
 2244  1 indent += this.configuration.spaces;
 2245   
 2246  1 for (content = node.content; content != null; content = content.next)
 2247    {
 2248  1 printTree(fout, mode, indent, lexer, content);
 2249    }
 2250   
 2251  1 condFlushLine(fout, indent);
 2252  1 indent -= this.configuration.spaces;
 2253  1 condFlushLine(fout, indent);
 2254    }
 2255    else
 2256    {
 2257   
 2258  494 for (content = node.content; content != null; content = content.next)
 2259    {
 2260  538 printTree(fout, mode, indent, lexer, content);
 2261    }
 2262    }
 2263   
 2264  495 printEndTag(mode, indent, node);
 2265    }
 2266    else
 2267    {
 2268    // other tags
 2269  2555 condFlushLine(fout, indent);
 2270   
 2271  2555 if (this.configuration.smartIndent && node.prev != null)
 2272    {
 2273  47 flushLine(fout, indent);
 2274    }
 2275   
 2276    // do not omit elements with attributes
 2277  2555 if (!this.configuration.hideEndTags
 2278    || !(node.tag != null && TidyUtils.toBoolean(node.tag.model & Dict.CM_OMITST))
 2279    || node.attributes != null)
 2280    {
 2281  2553 printTag(lexer, fout, mode, indent, node);
 2282   
 2283  2553 if (shouldIndent(node))
 2284    {
 2285  63 condFlushLine(fout, indent);
 2286    }
 2287  2490 else if (TidyUtils.toBoolean(node.tag.model & Dict.CM_HTML)
 2288    || node.tag == tt.tagNoframes
 2289    || (TidyUtils.toBoolean(node.tag.model & Dict.CM_HEAD) && !(node.tag == tt.tagTitle)))
 2290    {
 2291  646 flushLine(fout, indent);
 2292    }
 2293    }
 2294   
 2295  2555 if (node.tag == tt.tagBody && this.configuration.burstSlides)
 2296    {
 2297  26 printSlide(fout, mode, (this.configuration.indentContent
 2298    ? indent + this.configuration.spaces
 2299    : indent), lexer);
 2300    }
 2301    else
 2302    {
 2303  2529 last = null;
 2304   
 2305  2529 for (content = node.content; content != null; content = content.next)
 2306    {
 2307    // kludge for naked text before block level tag
 2308  5817 if (last != null
 2309    && !this.configuration.indentContent
 2310    && last.type == Node.TEXT_NODE
 2311    && content.tag != null
 2312    && !TidyUtils.toBoolean(content.tag.model & Dict.CM_INLINE))
 2313    {
 2314  19 flushLine(fout, indent);
 2315    }
 2316   
 2317  5817 printTree(
 2318    fout,
 2319    mode,
 2320  5817 (shouldIndent(node) ? indent + this.configuration.spaces : indent),
 2321    lexer,
 2322    content);
 2323   
 2324  5817 last = content;
 2325    }
 2326    }
 2327   
 2328    // don't flush line for td and th
 2329  2555 if (shouldIndent(node)
 2330    || ((TidyUtils.toBoolean(node.tag.model & Dict.CM_HTML) || node.tag == tt.tagNoframes || //
 2331    (TidyUtils.toBoolean(node.tag.model & Dict.CM_HEAD) && !(node.tag == tt.tagTitle))) && //
 2332    !this.configuration.hideEndTags))
 2333    {
 2334  708 condFlushLine(
 2335    fout,
 2336  708 (this.configuration.indentContent ? indent + this.configuration.spaces : indent));
 2337   
 2338  708 if (!this.configuration.hideEndTags || !TidyUtils.toBoolean(node.tag.model & Dict.CM_OPT))
 2339    {
 2340  708 printEndTag(mode, indent, node);
 2341   
 2342    // #603128 tidy adds newslines after </html> tag
 2343    // Fix by Fabrizio Giustina 12-02-2004
 2344    // fix is different from the one in original tidy
 2345  708 if (!lexer.seenEndHtml)
 2346    {
 2347  17 flushLine(fout, indent);
 2348    }
 2349    }
 2350    }
 2351    else
 2352    {
 2353  1847 if (!this.configuration.hideEndTags || !TidyUtils.toBoolean(node.tag.model & Dict.CM_OPT))
 2354    {
 2355  1844 printEndTag(mode, indent, node);
 2356    }
 2357   
 2358  1847 flushLine(fout, indent);
 2359    }
 2360   
 2361    // FG commented out: double newlines
 2362    // if (!this.configuration.indentContent
 2363    // && node.next != null
 2364    // && !this.configuration.hideEndTags
 2365    // && (node.tag.model
 2366    // & TidyUtils.toBoolean(Dict.CM_BLOCK | Dict.CM_TABLE | Dict.CM_LIST | Dict.CM_DEFLIST)))
 2367    // {
 2368    // flushLine(fout, indent);
 2369    // }
 2370    }
 2371    }
 2372    }
 2373   
 2374    /**
 2375    * @param fout
 2376    * @param mode
 2377    * @param indent
 2378    * @param lexer
 2379    * @param node
 2380    */
 2381  124 public void printXMLTree(Out fout, short mode, int indent, Lexer lexer, Node node)
 2382    {
 2383  124 TagTable tt = this.configuration.tt;
 2384   
 2385  124 if (node == null)
 2386    {
 2387  0 return;
 2388    }
 2389   
 2390  124 if (node.type == Node.TEXT_NODE || (node.type == Node.CDATA_TAG && lexer.configuration.escapeCdata))
 2391    {
 2392  27 printText(fout, mode, indent, node.textarray, node.start, node.end);
 2393    }
 2394  97 else if (node.type == Node.COMMENT_TAG)
 2395    {
 2396  15 condFlushLine(fout, indent);
 2397  15 printComment(fout, 0, node);
 2398  15 condFlushLine(fout, 0);
 2399    }
 2400  82 else if (node.type == Node.ROOT_NODE)
 2401    {
 2402  19 Node content;
 2403   
 2404  19 for (content = node.content; content != null; content = content.next)
 2405    {
 2406  42 printXMLTree(fout, mode, indent, lexer, content);
 2407    }
 2408    }
 2409  63 else if (node.type == Node.DOCTYPE_TAG)
 2410    {
 2411  1 printDocType(fout, indent, lexer, node);
 2412    }
 2413  62 else if (node.type == Node.PROC_INS_TAG)
 2414    {
 2415  0 printPI(fout, indent, node);
 2416    }
 2417  62 else if (node.type == Node.XML_DECL)
 2418    {
 2419  11 printXmlDecl(fout, indent, node);
 2420    }
 2421  51 else if (node.type == Node.CDATA_TAG)
 2422    {
 2423  0 printCDATA(fout, indent, node);
 2424    }
 2425  51 else if (node.type == Node.SECTION_TAG)
 2426    {
 2427  0 printSection(fout, indent, node);
 2428    }
 2429  51 else if (node.type == Node.ASP_TAG)
 2430    {
 2431  0 printAsp(fout, indent, node);
 2432    }
 2433  51 else if (node.type == Node.JSTE_TAG)
 2434    {
 2435  0 printJste(fout, indent, node);
 2436    }
 2437  51 else if (node.type == Node.PHP_TAG)
 2438    {
 2439  0 printPhp(fout, indent, node);
 2440    }
 2441  51 else if (TidyUtils.toBoolean(node.tag.model & Dict.CM_EMPTY)
 2442    || node.type == Node.START_END_TAG
 2443    && !configuration.xHTML)
 2444    {
 2445  6 condFlushLine(fout, indent);
 2446  6 printTag(lexer, fout, mode, indent, node);
 2447    // fgiust: Remove empty lines between tags in XML.
 2448    // flushLine(fout, indent);
 2449   
 2450    // CPR: folks don't want so much vertical spacing in XML
 2451    // if (node.next != null) { flushLine(fout, indent); }
 2452   
 2453    }
 2454    else
 2455    {
 2456    // some kind of container element
 2457  45 Node content;
 2458  45 boolean mixed = false;
 2459  45 int cindent;
 2460   
 2461  45 for (content = node.content; content != null; content = content.next)
 2462    {
 2463  54 if (content.type == Node.TEXT_NODE)
 2464    {
 2465  22 mixed = true;
 2466  22 break;
 2467    }
 2468    }
 2469   
 2470  45 condFlushLine(fout, indent);
 2471   
 2472  45 if (ParserImpl.XMLPreserveWhiteSpace(node, tt))
 2473    {
 2474  2 indent = 0;
 2475  2 cindent = 0;
 2476  2 mixed = false;
 2477    }
 2478  43 else if (mixed)
 2479    {
 2480  20 cindent = indent;
 2481    }
 2482    else
 2483    {
 2484  23 cindent = indent + this.configuration.spaces;
 2485    }
 2486   
 2487  45 printTag(lexer, fout, mode, indent, node);
 2488   
 2489  45 if (!mixed && node.content != null)
 2490    {
 2491  23 flushLine(fout, indent);
 2492    }
 2493   
 2494  45 for (content = node.content; content != null; content = content.next)
 2495    {
 2496  61 printXMLTree(fout, mode, cindent, lexer, content);
 2497    }
 2498   
 2499  45 if (!mixed && node.content != null)
 2500    {
 2501  23 condFlushLine(fout, cindent);
 2502    }
 2503  45 printEndTag(mode, indent, node);
 2504    // condFlushLine(fout, indent);
 2505   
 2506    // CPR: folks don't want so much vertical spacing in XML
 2507    // if (node.next != null) { flushLine(fout, indent); }
 2508   
 2509    }
 2510    }
 2511   
 2512    /**
 2513    * Split parse tree by h2 elements and output to separate files. Counts number of h2 children (if any) belonging to
 2514    * node.
 2515    * @param node root node
 2516    * @return number of slides (number of h2 elements)
 2517    */
 2518  2 public int countSlides(Node node)
 2519    {
 2520    // assume minimum of 1 slide
 2521  2 int n = 1;
 2522   
 2523  2 TagTable tt = this.configuration.tt;
 2524   
 2525    // fix for [431716] avoid empty slides
 2526  2 if (node != null && node.content != null && node.content.tag == tt.tagH2)
 2527    {
 2528    // "first" slide is empty, so ignore it
 2529  2 n--;
 2530    }
 2531   
 2532  2 if (node != null)
 2533    {
 2534  2 for (node = node.content; node != null; node = node.next)
 2535    {
 2536  102 if (node.tag == tt.tagH2)
 2537    {
 2538  52 ++n;
 2539    }
 2540    }
 2541    }
 2542   
 2543  2 return n;
 2544    }
 2545   
 2546    /**
 2547    * @param fout
 2548    * @param indent
 2549    */
 2550  52 private void printNavBar(Out fout, int indent)
 2551    {
 2552  52 String buf;
 2553   
 2554  52 condFlushLine(fout, indent);
 2555  52 printString("<center><small>");
 2556   
 2557  52 NumberFormat numberFormat = NumberFormat.getInstance();
 2558  52 numberFormat.setMinimumIntegerDigits(3);
 2559   
 2560  52 if (slide > 1)
 2561    {
 2562  50 buf = "<a href=\"slide" + numberFormat.format(slide - 1) + ".html\">previous</a> | ";
 2563    // #427666 - fix by Eric Rossen 02 Aug 00
 2564  50 printString(buf);
 2565  50 condFlushLine(fout, indent);
 2566   
 2567  50 if (slide < count)
 2568    {
 2569  48 printString("<a href=\"slide001.html\">start</a> | ");
 2570    // #427666 - fix by Eric Rossen 02 Aug 00
 2571    }
 2572    else
 2573    {
 2574  2 printString("<a href=\"slide001.html\">start</a>");
 2575    // #427666 - fix by Eric Rossen 02 Aug 00
 2576    }
 2577   
 2578  50 condFlushLine(fout, indent);
 2579    }
 2580   
 2581  52 if (slide < count)
 2582    {
 2583  50 buf = "<a href=\"slide" + numberFormat.format(slide + 1) + ".html\">next</a>";
 2584    // #427666 - fix by Eric Rossen 02 Aug 00
 2585  50 printString(buf);
 2586    }
 2587   
 2588  52 printString("</small></center>");
 2589  52 condFlushLine(fout, indent);
 2590    }
 2591   
 2592    /**
 2593    * Called from printTree to print the content of a slide from the node slidecontent. On return slidecontent points
 2594    * to the node starting the next slide or null. The variables slide and count are used to customise the navigation
 2595    * bar.
 2596    * @param fout
 2597    * @param mode
 2598    * @param indent
 2599    * @param lexer
 2600    */
 2601  26 public void printSlide(Out fout, short mode, int indent, Lexer lexer)
 2602    {
 2603  26 Node content, last;
 2604  26 TagTable tt = this.configuration.tt;
 2605   
 2606  26 NumberFormat numberFormat = NumberFormat.getInstance();
 2607  26 numberFormat.setMinimumIntegerDigits(3);
 2608   
 2609    /* insert div for onclick handler */
 2610  26 String s;
 2611  26 s = "<div onclick=\"document.location='slide"
 2612  26 + numberFormat.format(slide < count ? slide + 1 : 1)
 2613    + ".html'\">";
 2614    // #427666 - fix by Eric Rossen 02 Aug 00
 2615  26 printString(s);
 2616  26 condFlushLine(fout, indent);
 2617   
 2618    /* first print the h2 element and navbar */
 2619  26 if (slidecontent != null && slidecontent.tag == tt.tagH2)
 2620    {
 2621  26 printNavBar(fout, indent);
 2622   
 2623    /* now print an hr after h2 */
 2624   
 2625  26 addC('<', linelen++);
 2626   
 2627  26 addC(TidyUtils.foldCase('h', this.configuration.upperCaseTags, this.configuration.xmlTags), linelen++);
 2628  26 addC(TidyUtils.foldCase('r', this.configuration.upperCaseTags, this.configuration.xmlTags), linelen++);
 2629   
 2630  26 if (this.configuration.xmlOut)
 2631    {
 2632  0 printString(" />");
 2633    }
 2634    else
 2635    {
 2636  26 addC('>', linelen++);
 2637    }
 2638   
 2639  26 if (this.configuration.indentContent)
 2640    {
 2641  0 condFlushLine(fout, indent);
 2642    }
 2643   
 2644    // PrintVertSpacer(fout, indent);
 2645   
 2646    // condFlushLine(fout, indent);
 2647   
 2648    // print the h2 element
 2649  26 printTree(
 2650    fout,
 2651    mode,
 2652  26 (this.configuration.indentContent ? indent + this.configuration.spaces : indent),
 2653    lexer,
 2654    slidecontent);
 2655   
 2656  26 slidecontent = slidecontent.next;
 2657    }
 2658   
 2659    // now continue until we reach the next h2
 2660   
 2661  26 last = null;
 2662  26 content = slidecontent;
 2663   
 2664  26 for (; content != null; content = content.next)
 2665    {
 2666  50 if (content.tag == tt.tagH2)
 2667    {
 2668  25 break;
 2669    }
 2670   
 2671    // kludge for naked text before block level tag
 2672  25 if (last != null
 2673    && !this.configuration.indentContent
 2674    && last.type == Node.TEXT_NODE
 2675    && content.tag != null
 2676    && TidyUtils.toBoolean(content.tag.model & Dict.CM_BLOCK))
 2677    {
 2678  0 flushLine(fout, indent);
 2679  0 flushLine(fout, indent);
 2680    }
 2681   
 2682  25 printTree(
 2683    fout,
 2684    mode,
 2685  25 (this.configuration.indentContent ? indent + this.configuration.spaces : indent),
 2686    lexer,
 2687    content);
 2688   
 2689  25 last = content;
 2690    }
 2691   
 2692  26 slidecontent = content;
 2693   
 2694    // now print epilog
 2695   
 2696  26 condFlushLine(fout, indent);
 2697   
 2698  26 printString("<br clear=\"all\">");
 2699  26 condFlushLine(fout, indent);
 2700   
 2701  26 addC('<', linelen++);
 2702   
 2703  26 addC(TidyUtils.foldCase('h', this.configuration.upperCaseTags, this.configuration.xmlTags), linelen++);
 2704  26 addC(TidyUtils.foldCase('r', this.configuration.upperCaseTags, this.configuration.xmlTags), linelen++);
 2705   
 2706  26 if (this.configuration.xmlOut)
 2707    {
 2708  0 printString(" />");
 2709    }
 2710    else
 2711    {
 2712  26 addC('>', linelen++);
 2713    }
 2714   
 2715  26 if (this.configuration.indentContent)
 2716    {
 2717  0 condFlushLine(fout, indent);
 2718    }
 2719   
 2720  26 printNavBar(fout, indent);
 2721   
 2722    // end tag for div
 2723  26 printString("</div>");
 2724  26 condFlushLine(fout, indent);
 2725    }
 2726   
 2727    /**
 2728    * Add meta element for page transition effect, this works on IE but not NS.
 2729    * @param lexer
 2730    * @param root
 2731    * @param duration
 2732    */
 2733  1 public void addTransitionEffect(Lexer lexer, Node root, double duration)
 2734    {
 2735  1 Node head = root.findHEAD(lexer.configuration.tt);
 2736  1 String transition;
 2737   
 2738  1 transition = "blendTrans(Duration=" + (new Double(duration)).toString() + ")";
 2739   
 2740  1 if (head != null)
 2741    {
 2742  1 Node meta = lexer.inferredTag("meta");
 2743  1 meta.addAttribute("http-equiv", "Page-Enter");
 2744  1 meta.addAttribute("content", transition);
 2745  1 head.insertNodeAtStart(meta);
 2746    }
 2747    }
 2748   
 2749    /**
 2750    * Creates slides from h2.
 2751    * @param lexer Lexer
 2752    * @param root root node
 2753    */
 2754  1 public void createSlides(Lexer lexer, Node root)
 2755    {
 2756  1 Node body;
 2757  1 String buf;
 2758   
 2759  1 NumberFormat numberFormat = NumberFormat.getInstance();
 2760  1 numberFormat.setMinimumIntegerDigits(3);
 2761   
 2762  1 body = root.findBody(lexer.configuration.tt);
 2763  1 count = countSlides(body);
 2764  1 slidecontent = body.content;
 2765   
 2766  1 addTransitionEffect(lexer, root, 3.0);
 2767   
 2768  1 for (slide = 1; slide <= count; ++slide)
 2769    {
 2770  26 buf = "slide" + numberFormat.format(slide) + ".html";
 2771   
 2772  26 try
 2773    {
 2774  26 FileOutputStream fis = new FileOutputStream(buf);
 2775  26 Out out = OutFactory.getOut(configuration, fis);
 2776   
 2777  26 printTree(out, (short) 0, 0, lexer, root);
 2778  26 flushLine(out, 0);
 2779   
 2780  26 fis.close();
 2781    }
 2782    catch (IOException e)
 2783    {
 2784  0 System.err.println(buf + e.toString());
 2785    }
 2786    }
 2787   
 2788    // delete superfluous slides by deleting slideN.html for N = count+1, count+2, etc.
 2789    // until no such file is found.
 2790   
 2791    // #427666 - fix by Eric Rossen 02 Aug 00
 2792  1 while ((new File("slide" + numberFormat.format(slide) + ".html")).delete())
 2793    {
 2794  0 ++slide;
 2795    }
 2796    }
 2797   
 2798    }