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