/*
* Copyright 2010 Alibaba Group Holding Limited.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* @(#)PPrint.java 1.11 2000/08/16
*
*/
package org.w3c.tidy;
/**
*
* Pretty print parse tree
*
* (c) 1998-2000 (W3C) MIT, INRIA, Keio University
* See Tidy.java for the copyright notice.
* Derived from <a href="http://www.w3.org/People/Raggett/tidy">
* HTML Tidy Release 4 Aug 2000</a>
*
* @author Dave Raggett <dsr@w3.org>
* @author Andy Quick <ac.quick@sympatico.ca> (translation to Java)
* @version 1.0, 1999/05/22
* @version 1.0.1, 1999/05/29
* @version 1.1, 1999/06/18 Java Bean
* @version 1.2, 1999/07/10 Tidy Release 7 Jul 1999
* @version 1.3, 1999/07/30 Tidy Release 26 Jul 1999
* @version 1.4, 1999/09/04 DOM support
* @version 1.5, 1999/10/23 Tidy Release 27 Sep 1999
* @version 1.6, 1999/11/01 Tidy Release 22 Oct 1999
* @version 1.7, 1999/12/06 Tidy Release 30 Nov 1999
* @version 1.8, 2000/01/22 Tidy Release 13 Jan 2000
* @version 1.9, 2000/06/03 Tidy Release 30 Apr 2000
* @version 1.10, 2000/07/22 Tidy Release 8 Jul 2000
* @version 1.11, 2000/08/16 Tidy Release 4 Aug 2000
*/
/*
Block-level and unknown elements are printed on
new lines and their contents indented 2 spaces
Inline elements are printed inline.
Inline content is wrapped on spaces (except in
attribute values or preformatted text, after
start tags and before end tags
*/
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class PPrint {
/* page transition effects */
public static final short EFFECT_BLEND = -1;
public static final short EFFECT_BOX_IN = 0;
public static final short EFFECT_BOX_OUT = 1;
public static final short EFFECT_CIRCLE_IN = 2;
public static final short EFFECT_CIRCLE_OUT = 3;
public static final short EFFECT_WIPE_UP = 4;
public static final short EFFECT_WIPE_DOWN = 5;
public static final short EFFECT_WIPE_RIGHT = 6;
public static final short EFFECT_WIPE_LEFT = 7;
public static final short EFFECT_VERT_BLINDS = 8;
public static final short EFFECT_HORZ_BLINDS = 9;
public static final short EFFECT_CHK_ACROSS = 10;
public static final short EFFECT_CHK_DOWN = 11;
public static final short EFFECT_RND_DISSOLVE = 12;
public static final short EFFECT_SPLIT_VIRT_IN = 13;
public static final short EFFECT_SPLIT_VIRT_OUT = 14;
public static final short EFFECT_SPLIT_HORZ_IN = 15;
public static final short EFFECT_SPLIT_HORZ_OUT = 16;
public static final short EFFECT_STRIPS_LEFT_DOWN = 17;
public static final short EFFECT_STRIPS_LEFT_UP = 18;
public static final short EFFECT_STRIPS_RIGHT_DOWN = 19;
public static final short EFFECT_STRIPS_RIGHT_UP = 20;
public static final short EFFECT_RND_BARS_HORZ = 21;
public static final short EFFECT_RND_BARS_VERT = 22;
public static final short EFFECT_RANDOM = 23;
private static final short NORMAL = 0;
private static final short PREFORMATTED = 1;
private static final short COMMENT = 2;
private static final short ATTRIBVALUE = 4;
private static final short NOWRAP = 8;
private static final short CDATA = 16;
private int[] linebuf = null;
private int lbufsize = 0;
private int linelen = 0;
private int wraphere = 0;
private boolean inAttVal = false;
private boolean InString = false;
private int slide = 0;
private int count = 0;
private Node slidecontent = null;
private Configuration configuration;
public PPrint(Configuration configuration) {
this.configuration = configuration;
}
/*
* 1010 A 1011 B 1100 C 1101 D 1110 E 1111 F
*/
/* return one less that the number of bytes used by UTF-8 char */
/* str points to 1st byte,ch initialized to 1st byte */
public static int getUTF8(byte[] str, int start, MutableInteger ch) {
int c, n, i, bytes;
c = str[start] & 0xFF; // Convert to unsigned.
if ((c & 0xE0) == 0xC0) /* 110X XXXX two bytes */ {
n = c & 31;
bytes = 2;
} else if ((c & 0xF0) == 0xE0) /* 1110 XXXX three bytes */ {
n = c & 15;
bytes = 3;
} else if ((c & 0xF8) == 0xF0) /* 1111 0XXX four bytes */ {
n = c & 7;
bytes = 4;
} else if ((c & 0xFC) == 0xF8) /* 1111 10XX five bytes */ {
n = c & 3;
bytes = 5;
} else if ((c & 0xFE) == 0xFC) /* 1111 110X six bytes */
{
n = c & 1;
bytes = 6;
} else /* 0XXX XXXX one byte */ {
ch.value = c;
return 0;
}
/* successor bytes should have the form 10XX XXXX */
for (i = 1; i < bytes; ++i) {
c = str[start + i] & 0xFF; // Convert to unsigned.
n = n << 6 | c & 0x3F;
}
ch.value = n;
return bytes - 1;
}
/* store char c as UTF-8 encoded byte stream */
public static int putUTF8(byte[] buf, int start, int c) {
if (c < 128) {
buf[start++] = (byte) c;
} else if (c <= 0x7FF) {
buf[start++] = (byte) (0xC0 | c >> 6);
buf[start++] = (byte) (0x80 | c & 0x3F);
} else if (c <= 0xFFFF) {
buf[start++] = (byte) (0xE0 | c >> 12);
buf[start++] = (byte) (0x80 | c >> 6 & 0x3F);
buf[start++] = (byte) (0x80 | c & 0x3F);
} else if (c <= 0x1FFFFF) {
buf[start++] = (byte) (0xF0 | c >> 18);
buf[start++] = (byte) (0x80 | c >> 12 & 0x3F);
buf[start++] = (byte) (0x80 | c >> 6 & 0x3F);
buf[start++] = (byte) (0x80 | c & 0x3F);
} else {
buf[start++] = (byte) (0xF8 | c >> 24);
buf[start++] = (byte) (0x80 | c >> 18 & 0x3F);
buf[start++] = (byte) (0x80 | c >> 12 & 0x3F);
buf[start++] = (byte) (0x80 | c >> 6 & 0x3F);
buf[start++] = (byte) (0x80 | c & 0x3F);
}
return start;
}
private void addC(int c, int index) {
if (index + 1 >= lbufsize) {
while (index + 1 >= lbufsize) {
if (lbufsize == 0) {
lbufsize = 256;
} else {
lbufsize = lbufsize * 2;
}
}
int[] temp = new int[lbufsize];
if (linebuf != null) {
System.arraycopy(linebuf, 0, temp, 0, index);
}
linebuf = temp;
}
linebuf[index] = c;
}
private void wrapLine(Out fout, int indent) {
int i, p, q;
if (wraphere == 0) {
return;
}
for (i = 0; i < indent; ++i) {
fout.outc(' ');
}
for (i = 0; i < wraphere; ++i) {
fout.outc(linebuf[i]);
}
if (InString) {
fout.outc(' ');
fout.outc('\\');
}
fout.newline();
if (linelen > wraphere) {
p = 0;
if (linebuf[wraphere] == ' ') {
++wraphere;
}
q = wraphere;
addC('\0', linelen);
while (true) {
linebuf[p] = linebuf[q];
if (linebuf[q] == 0) {
break;
}
p++;
q++;
}
linelen -= wraphere;
} else {
linelen = 0;
}
wraphere = 0;
}
private void wrapAttrVal(Out fout, int indent, boolean inString) {
int i, p, q;
for (i = 0; i < indent; ++i) {
fout.outc(' ');
}
for (i = 0; i < wraphere; ++i) {
fout.outc(linebuf[i]);
}
fout.outc(' ');
if (inString) {
fout.outc('\\');
}
fout.newline();
if (linelen > wraphere) {
p = 0;
if (linebuf[wraphere] == ' ') {
++wraphere;
}
q = wraphere;
addC('\0', linelen);
while (true) {
linebuf[p] = linebuf[q];
if (linebuf[q] == 0) {
break;
}
p++;
q++;
}
linelen -= wraphere;
} else {
linelen = 0;
}
wraphere = 0;
}
public void flushLine(Out fout, int indent) {
int i;
if (linelen > 0) {
if (indent + linelen >= this.configuration.wraplen) {
wrapLine(fout, indent);
}
if (!inAttVal || this.configuration.IndentAttributes) {
for (i = 0; i < indent; ++i) {
fout.outc(' ');
}
}
for (i = 0; i < linelen; ++i) {
fout.outc(linebuf[i]);
}
}
fout.newline();
linelen = 0;
wraphere = 0;
inAttVal = false;
}
public void condFlushLine(Out fout, int indent) {
int i;
if (linelen > 0) {
if (indent + linelen >= this.configuration.wraplen) {
wrapLine(fout, indent);
}
if (!inAttVal || this.configuration.IndentAttributes) {
for (i = 0; i < indent; ++i) {
fout.outc(' ');
}
}
for (i = 0; i < linelen; ++i) {
fout.outc(linebuf[i]);
}
fout.newline();
linelen = 0;
wraphere = 0;
inAttVal = false;
}
}
private void printChar(int c, short mode) {
String entity;
if (c == ' ' && !((mode & (PREFORMATTED | COMMENT | ATTRIBVALUE)) != 0)) {
/* coerce a space character to a non-breaking space */
if ((mode & NOWRAP) != 0) {
/* by default XML doesn't define */
if (this.configuration.NumEntities || this.configuration.XmlTags) {
addC('&', linelen++);
addC('#', linelen++);
addC('1', linelen++);
addC('6', linelen++);
addC('0', linelen++);
addC(';', linelen++);
} else /* otherwise use named entity */ {
addC('&', linelen++);
addC('n', linelen++);
addC('b', linelen++);
addC('s', linelen++);
addC('p', linelen++);
addC(';', linelen++);
}
return;
} else {
wraphere = linelen;
}
}
/* comment characters are passed raw */
if ((mode & COMMENT) != 0) {
addC(c, linelen++);
return;
}
/* except in CDATA map < to < etc. */
if (!((mode & CDATA) != 0)) {
if (c == '<') {
addC('&', linelen++);
addC('l', linelen++);
addC('t', linelen++);
addC(';', linelen++);
return;
}
if (c == '>') {
addC('&', linelen++);
addC('g', linelen++);
addC('t', linelen++);
addC(';', linelen++);
return;
}
/*
* naked '&' chars can be left alone or quoted as & The latter
* is required for XML where naked '&' are illegal.
*/
if (c == '&' && this.configuration.QuoteAmpersand) {
addC('&', linelen++);
addC('a', linelen++);
addC('m', linelen++);
addC('p', linelen++);
addC(';', linelen++);
return;
}
if (c == '"' && this.configuration.QuoteMarks) {
addC('&', linelen++);
addC('q', linelen++);
addC('u', linelen++);
addC('o', linelen++);
addC('t', linelen++);
addC(';', linelen++);
return;
}
if (c == '\'' && this.configuration.QuoteMarks) {
addC('&', linelen++);
addC('#', linelen++);
addC('3', linelen++);
addC('9', linelen++);
addC(';', linelen++);
return;
}
if (c == 160 && this.configuration.CharEncoding != Configuration.RAW) {
if (this.configuration.QuoteNbsp) {
addC('&', linelen++);
if (this.configuration.NumEntities) {
addC('#', linelen++);
addC('1', linelen++);
addC('6', linelen++);
addC('0', linelen++);
} else {
addC('n', linelen++);
addC('b', linelen++);
addC('s', linelen++);
addC('p', linelen++);
}
addC(';', linelen++);
} else {
addC(c, linelen++);
}
return;
}
}
/* otherwise ISO 2022 characters are passed raw */
if (this.configuration.CharEncoding == Configuration.ISO2022
|| this.configuration.CharEncoding == Configuration.RAW) {
addC(c, linelen++);
return;
}
/* if preformatted text, map to space */
if (c == 160 && (mode & PREFORMATTED) != 0) {
addC(' ', linelen++);
return;
}
/*
* Filters from Word and PowerPoint often use smart quotes resulting in
* character codes between 128 and 159. Unfortunately, the corresponding
* HTML 4.0 entities for these are not widely supported. The following
* converts dashes and quotation marks to the nearest ASCII equivalent.
* My thanks to Andrzej Novosiolov for his help with this code.
*/
if (this.configuration.MakeClean) {
if (c >= 0x2013 && c <= 0x201E) {
switch (c) {
case 0x2013:
case 0x2014:
c = '-';
break;
case 0x2018:
case 0x2019:
case 0x201A:
c = '\'';
break;
case 0x201C:
case 0x201D:
case 0x201E:
c = '"';
break;
}
}
}
/* don't map latin-1 chars to entities */
if (this.configuration.CharEncoding == Configuration.LATIN1) {
if (c > 255) /* multi byte chars */ {
if (!this.configuration.NumEntities) {
entity = EntityTable.getDefaultEntityTable().entityName((short) c);
if (entity != null) {
entity = "&" + entity + ";";
} else {
entity = "" + c + ";";
}
} else {
entity = "" + c + ";";
}
for (int i = 0; i < entity.length(); i++) {
addC(entity.charAt(i), linelen++);
}
return;
}
if (c > 126 && c < 160) {
entity = "" + c + ";";
for (int i = 0; i < entity.length(); i++) {
addC(entity.charAt(i), linelen++);
}
return;
}
addC(c, linelen++);
return;
}
/* don't map utf8 chars to entities */
if (this.configuration.CharEncoding == Configuration.UTF8) {
addC(c, linelen++);
return;
}
/* use numeric entities only for XML */
if (this.configuration.XmlTags) {
/* if ASCII use numeric entities for chars > 127 */
if (c > 127 && this.configuration.CharEncoding == Configuration.ASCII) {
entity = "" + c + ";";
for (int i = 0; i < entity.length(); i++) {
addC(entity.charAt(i), linelen++);
}
return;
}
/* otherwise output char raw */
addC(c, linelen++);
return;
}
/* default treatment for ASCII */
if (c > 126 || c < ' ' && c != '\t') {
if (!this.configuration.NumEntities) {
entity = EntityTable.getDefaultEntityTable().entityName((short) c);
if (entity != null) {
entity = "&" + entity + ";";
} else {
entity = "" + c + ";";
}
} else {
entity = "" + c + ";";
}
for (int i = 0; i < entity.length(); i++) {
addC(entity.charAt(i), linelen++);
}
return;
}
addC(c, linelen++);
}
/*
* The line buffer is uint not char so we can hold Unicode values unencoded.
* The translation to UTF-8 is deferred to the outc routine called to flush
* the line buffer.
*/
private void printText(Out fout, short mode, int indent, byte[] textarray, int start, int end) {
int i, c;
MutableInteger ci = new MutableInteger();
for (i = start; i < end; ++i) {
if (indent + linelen >= this.configuration.wraplen) {
wrapLine(fout, indent);
}
c = textarray[i] & 0xFF; // Convert to unsigned.
/* look for UTF-8 multibyte character */
if (c > 0x7F) {
i += getUTF8(textarray, i, ci);
c = ci.value;
}
if (c == '\n') {
flushLine(fout, indent);
continue;
}
printChar(c, mode);
}
}
private void printString(Out fout, int indent, String str) {
for (int i = 0; i < str.length(); i++) {
addC(str.charAt(i), linelen++);
}
}
private void printAttrValue(Out fout, int indent, String value, int delim, boolean wrappable) {
int c;
MutableInteger ci = new MutableInteger();
boolean wasinstring = false;
byte[] valueChars = null;
int i;
short mode = wrappable ? (short) (NORMAL | ATTRIBVALUE) : (short) (PREFORMATTED | ATTRIBVALUE);
if (value != null) {
valueChars = Lexer.getBytes(value);
}
/* look for ASP, Tango or PHP instructions for computed attribute value */
if (valueChars != null && valueChars.length >= 5 && valueChars[0] == '<') {
if (valueChars[1] == '%' || valueChars[1] == '@' || new String(valueChars, 0, 5).equals("<?php")) {
mode |= CDATA;
}
}
if (delim == 0) {
delim = '"';
}
addC('=', linelen++);
/* don't wrap after "=" for xml documents */
if (!this.configuration.XmlOut) {
if (indent + linelen < this.configuration.wraplen) {
wraphere = linelen;
}
if (indent + linelen >= this.configuration.wraplen) {
wrapLine(fout, indent);
}
if (indent + linelen < this.configuration.wraplen) {
wraphere = linelen;
} else {
condFlushLine(fout, indent);
}
}
addC(delim, linelen++);
if (value != null) {
InString = false;
i = 0;
while (i < valueChars.length) {
c = valueChars[i] & 0xFF; // Convert to unsigned.
if (wrappable && c == ' ' && indent + linelen < this.configuration.wraplen) {
wraphere = linelen;
wasinstring = InString;
}
if (wrappable && wraphere > 0 && indent + linelen >= this.configuration.wraplen) {
wrapAttrVal(fout, indent, wasinstring);
}
if (c == delim) {
String entity;
entity = c == '"' ? """ : "'";
for (int j = 0; j < entity.length(); j++) {
addC(entity.charAt(j), linelen++);
}
++i;
continue;
} else if (c == '"') {
if (this.configuration.QuoteMarks) {
addC('&', linelen++);
addC('q', linelen++);
addC('u', linelen++);
addC('o', linelen++);
addC('t', linelen++);
addC(';', linelen++);
} else {
addC('"', linelen++);
}
if (delim == '\'') {
InString = !InString;
}
++i;
continue;
} else if (c == '\'') {
if (this.configuration.QuoteMarks) {
addC('&', linelen++);
addC('#', linelen++);
addC('3', linelen++);
addC('9', linelen++);
addC(';', linelen++);
} else {
addC('\'', linelen++);
}
if (delim == '"') {
InString = !InString;
}
++i;
continue;
}
/* look for UTF-8 multibyte character */
if (c > 0x7F) {
i += getUTF8(valueChars, i, ci);
c = ci.value;
}
++i;
if (c == '\n') {
flushLine(fout, indent);
continue;
}
printChar(c, mode);
}
}
InString = false;
addC(delim, linelen++);
}
private void printAttribute(Out fout, int indent, Node node, AttVal attr) {
String name;
boolean wrappable = false;
if (this.configuration.IndentAttributes) {
flushLine(fout, indent);
indent += this.configuration.spaces;
}
name = attr.attribute;
if (indent + linelen >= this.configuration.wraplen) {
wrapLine(fout, indent);
}
if (!this.configuration.XmlTags && !this.configuration.XmlOut && attr.dict != null) {
if (AttributeTable.getDefaultAttributeTable().isScript(name)) {
wrappable = this.configuration.WrapScriptlets;
} else if (!attr.dict.nowrap && this.configuration.WrapAttVals) {
wrappable = true;
}
}
if (indent + linelen < this.configuration.wraplen) {
wraphere = linelen;
addC(' ', linelen++);
} else {
condFlushLine(fout, indent);
addC(' ', linelen++);
}
for (int i = 0; i < name.length(); i++) {
addC(Lexer.foldCase(name.charAt(i), this.configuration.UpperCaseAttrs, this.configuration.XmlTags),
linelen++);
}
if (indent + linelen >= this.configuration.wraplen) {
wrapLine(fout, indent);
}
if (attr.value == null) {
if (this.configuration.XmlTags || this.configuration.XmlOut) {
printAttrValue(fout, indent, attr.attribute, attr.delim, true);
} else if (!attr.isBoolAttribute() && !Node.isNewNode(node)) {
printAttrValue(fout, indent, "", attr.delim, true);
} else if (indent + linelen < this.configuration.wraplen) {
wraphere = linelen;
}
} else {
printAttrValue(fout, indent, attr.value, attr.delim, wrappable);
}
}
private void printAttrs(Out fout, int indent, Node node, AttVal attr) {
if (attr != null) {
if (attr.next != null) {
printAttrs(fout, indent, node, attr.next);
}
if (attr.attribute != null) {
printAttribute(fout, indent, node, attr);
} else if (attr.asp != null) {
addC(' ', linelen++);
printAsp(fout, indent, attr.asp);
} else if (attr.php != null) {
addC(' ', linelen++);
printPhp(fout, indent, attr.php);
}
}
/* add xml:space attribute to pre and other elements */
if (configuration.XmlOut && configuration.XmlSpace && ParserImpl.XMLPreserveWhiteSpace(node, configuration.tt)
&& node.getAttrByName("xml:space") == null) {
printString(fout, indent, " xml:space=\"preserve\"");
}
}
/*
* Line can be wrapped immediately after inline start tag provided if
* follows a text node ending in a space, or it parent is an inline element
* that that rule applies to. This behaviour was reverse engineered from
* Netscape 3.0
*/
private static boolean afterSpace(Node node) {
Node prev;
int c;
if (node == null || node.tag == null || !((node.tag.model & Dict.CM_INLINE) != 0)) {
return true;
}
prev = node.prev;
if (prev != null) {
if (prev.type == Node.TextNode && prev.end > prev.start) {
c = prev.textarray[prev.end - 1] & 0xFF; // Convert to unsigned.
if (c == 160 || c == ' ' || c == '\n') {
return true;
}
}
return false;
}
return afterSpace(node.parent);
}
private void printTag(Lexer lexer, Out fout, short mode, int indent, Node node) {
char c;
String p;
TagTable tt = this.configuration.tt;
addC('<', linelen++);
if (node.type == Node.EndTag) {
addC('/', linelen++);
}
p = node.element;
for (int i = 0; i < p.length(); i++) {
addC(Lexer.foldCase(p.charAt(i), this.configuration.UpperCaseTags, this.configuration.XmlTags), linelen++);
}
printAttrs(fout, indent, node, node.attributes);
if ((this.configuration.XmlOut || lexer != null && lexer.isvoyager)
&& (node.type == Node.StartEndTag || (node.tag.model & Dict.CM_EMPTY) != 0)) {
addC(' ', linelen++); /* compatibility hack */
addC('/', linelen++);
}
addC('>', linelen++);
;
if (node.type != Node.StartEndTag && !((mode & PREFORMATTED) != 0)) {
if (indent + linelen >= this.configuration.wraplen) {
wrapLine(fout, indent);
}
if (indent + linelen < this.configuration.wraplen) {
/*
* wrap after start tag if is <br/> or if it's not inline or it
* is an empty tag followed by </a>
*/
if (afterSpace(node)) {
if (!((mode & NOWRAP) != 0)
&& (!((node.tag.model & Dict.CM_INLINE) != 0) || node.tag == tt.tagBr || (node.tag.model & Dict.CM_EMPTY) != 0
&& node.next == null && node.parent.tag == tt.tagA)) {
wraphere = linelen;
}
}
} else {
condFlushLine(fout, indent);
}
}
}
private void printEndTag(Out fout, short mode, int indent, Node node) {
char c;
String p;
/*
* Netscape ignores SGML standard by not ignoring a line break before
* </A> or </U> etc. To avoid rendering this as an underlined space, I
* disable line wrapping before inline end tags by the #if 0 ... #endif
*/
if (false) {
if (indent + linelen < this.configuration.wraplen && !((mode & NOWRAP) != 0)) {
wraphere = linelen;
}
}
addC('<', linelen++);
addC('/', linelen++);
p = node.element;
for (int i = 0; i < p.length(); i++) {
addC(Lexer.foldCase(p.charAt(i), this.configuration.UpperCaseTags, this.configuration.XmlTags), linelen++);
}
addC('>', linelen++);
}
private void printComment(Out fout, int indent, Node node) {
if (indent + linelen < this.configuration.wraplen) {
wraphere = linelen;
}
addC('<', linelen++);
addC('!', linelen++);
addC('-', linelen++);
addC('-', linelen++);
if (false) {
if (linelen < this.configuration.wraplen) {
wraphere = linelen;
}
}
printText(fout, COMMENT, indent, node.textarray, node.start, node.end);
if (false) {
if (indent + linelen < this.configuration.wraplen) {
wraphere = linelen;
}
}
// See Lexer.java: AQ 8Jul2000
addC('-', linelen++);
addC('-', linelen++);
addC('>', linelen++);
if (node.linebreak) {
flushLine(fout, indent);
}
}
private void printDocType(Out fout, int indent, Node node) {
boolean q = this.configuration.QuoteMarks;
this.configuration.QuoteMarks = false;
if (indent + linelen < this.configuration.wraplen) {
wraphere = linelen;
}
condFlushLine(fout, indent);
addC('<', linelen++);
addC('!', linelen++);
addC('D', linelen++);
addC('O', linelen++);
addC('C', linelen++);
addC('T', linelen++);
addC('Y', linelen++);
addC('P', linelen++);
addC('E', linelen++);
addC(' ', linelen++);
if (indent + linelen < this.configuration.wraplen) {
wraphere = linelen;
}
printText(fout, (short) 0, indent, node.textarray, node.start, node.end);
if (linelen < this.configuration.wraplen) {
wraphere = linelen;
}
addC('>', linelen++);
this.configuration.QuoteMarks = q;
condFlushLine(fout, indent);
}
private void printPI(Out fout, int indent, Node node) {
if (indent + linelen < this.configuration.wraplen) {
wraphere = linelen;
}
addC('<', linelen++);
addC('?', linelen++);
/* set CDATA to pass < and > unescaped */
printText(fout, CDATA, indent, node.textarray, node.start, node.end);
if (node.textarray[node.end - 1] != (byte) '?') {
addC('?', linelen++);
}
addC('>', linelen++);
condFlushLine(fout, indent);
}
/* note ASP and JSTE share <% ... %> syntax */
private void printAsp(Out fout, int indent, Node node) {
int savewraplen = this.configuration.wraplen;
/* disable wrapping if so requested */
if (!this.configuration.WrapAsp || !this.configuration.WrapJste) {
this.configuration.wraplen = 0xFFFFFF; /* a very large number */
}
if (false) { //#if 0
if (indent + linelen < this.configuration.wraplen) {
wraphere = linelen;
}
} //#endif
addC('<', linelen++);
addC('%', linelen++);
printText(fout, this.configuration.WrapAsp ? CDATA : COMMENT, indent, node.textarray, node.start, node.end);
addC('%', linelen++);
addC('>', linelen++);
/* condFlushLine(fout, indent); */
this.configuration.wraplen = savewraplen;
}
/* JSTE also supports <# ... #> syntax */
private void printJste(Out fout, int indent, Node node) {
int savewraplen = this.configuration.wraplen;
/* disable wrapping if so requested */
if (!this.configuration.WrapJste) {
this.configuration.wraplen = 0xFFFFFF; /* a very large number */
}
addC('<', linelen++);
addC('#', linelen++);
printText(fout, this.configuration.WrapJste ? CDATA : COMMENT, indent, node.textarray, node.start, node.end);
addC('#', linelen++);
addC('>', linelen++);
/* condFlushLine(fout, indent); */
this.configuration.wraplen = savewraplen;
}
/* PHP is based on XML processing instructions */
private void printPhp(Out fout, int indent, Node node) {
int savewraplen = this.configuration.wraplen;
/* disable wrapping if so requested */
if (!this.configuration.WrapPhp) {
this.configuration.wraplen = 0xFFFFFF; /* a very large number */
}
if (false) { //#if 0
if (indent + linelen < this.configuration.wraplen) {
wraphere = linelen;
}
} //#endif
addC('<', linelen++);
addC('?', linelen++);
printText(fout, this.configuration.WrapPhp ? CDATA : COMMENT, indent, node.textarray, node.start, node.end);
addC('?', linelen++);
addC('>', linelen++);
/* PCondFlushLine(fout, indent); */
this.configuration.wraplen = savewraplen;
}
private void printCDATA(Out fout, int indent, Node node) {
int savewraplen = this.configuration.wraplen;
condFlushLine(fout, indent);
/* disable wrapping */
this.configuration.wraplen = 0xFFFFFF; /* a very large number */
addC('<', linelen++);
addC('!', linelen++);
addC('[', linelen++);
addC('C', linelen++);
addC('D', linelen++);
addC('A', linelen++);
addC('T', linelen++);
addC('A', linelen++);
addC('[', linelen++);
printText(fout, COMMENT, indent, node.textarray, node.start, node.end);
addC(']', linelen++);
addC(']', linelen++);
addC('>', linelen++);
condFlushLine(fout, indent);
this.configuration.wraplen = savewraplen;
}
private void printSection(Out fout, int indent, Node node) {
int savewraplen = this.configuration.wraplen;
/* disable wrapping if so requested */
if (!this.configuration.WrapSection) {
this.configuration.wraplen = 0xFFFFFF; /* a very large number */
}
if (false) { //#if 0
if (indent + linelen < this.configuration.wraplen) {
wraphere = linelen;
}
} //#endif
addC('<', linelen++);
addC('!', linelen++);
addC('[', linelen++);
printText(fout, this.configuration.WrapSection ? CDATA : COMMENT, indent, node.textarray, node.start, node.end);
addC(']', linelen++);
addC('>', linelen++);
/* PCondFlushLine(fout, indent); */
this.configuration.wraplen = savewraplen;
}
private boolean shouldIndent(Node node) {
TagTable tt = this.configuration.tt;
if (!this.configuration.IndentContent) {
return false;
}
if (this.configuration.SmartIndent) {
if (node.content != null && (node.tag.model & Dict.CM_NO_INDENT) != 0) {
for (node = node.content; node != null; node = node.next) {
if (node.tag != null && (node.tag.model & Dict.CM_BLOCK) != 0) {
return true;
}
}
return false;
}
if ((node.tag.model & Dict.CM_HEADING) != 0) {
return false;
}
if (node.tag == tt.tagP) {
return false;
}
if (node.tag == tt.tagTitle) {
return false;
}
}
if ((node.tag.model & (Dict.CM_FIELD | Dict.CM_OBJECT)) != 0) {
return true;
}
if (node.tag == tt.tagMap) {
return true;
}
return !((node.tag.model & Dict.CM_INLINE) != 0);
}
public void printTree(Out fout, short mode, int indent, Lexer lexer, Node node) {
Node content, last;
TagTable tt = this.configuration.tt;
if (node == null) {
return;
}
if (node.type == Node.TextNode) {
printText(fout, mode, indent, node.textarray, node.start, node.end);
} else if (node.type == Node.CommentTag) {
printComment(fout, indent, node);
} else if (node.type == Node.RootNode) {
for (content = node.content; content != null; content = content.next) {
printTree(fout, mode, indent, lexer, content);
}
} else if (node.type == Node.DocTypeTag) {
printDocType(fout, indent, node);
} else if (node.type == Node.ProcInsTag) {
printPI(fout, indent, node);
} else if (node.type == Node.CDATATag) {
printCDATA(fout, indent, node);
} else if (node.type == Node.SectionTag) {
printSection(fout, indent, node);
} else if (node.type == Node.AspTag) {
printAsp(fout, indent, node);
} else if (node.type == Node.JsteTag) {
printJste(fout, indent, node);
} else if (node.type == Node.PhpTag) {
printPhp(fout, indent, node);
} else if ((node.tag.model & Dict.CM_EMPTY) != 0 || node.type == Node.StartEndTag) {
if (!((node.tag.model & Dict.CM_INLINE) != 0)) {
condFlushLine(fout, indent);
}
if (node.tag == tt.tagBr && node.prev != null && node.prev.tag != tt.tagBr
&& this.configuration.BreakBeforeBR) {
flushLine(fout, indent);
}
if (this.configuration.MakeClean && node.tag == tt.tagWbr) {
printString(fout, indent, " ");
} else {
printTag(lexer, fout, mode, indent, node);
}
if (node.tag == tt.tagParam || node.tag == tt.tagArea) {
condFlushLine(fout, indent);
} else if (node.tag == tt.tagBr || node.tag == tt.tagHr) {
flushLine(fout, indent);
}
} else /* some kind of container element */ {
if (node.tag != null && node.tag.parser == ParserImpl.getParsePre()) {
condFlushLine(fout, indent);
indent = 0;
condFlushLine(fout, indent);
printTag(lexer, fout, mode, indent, node);
flushLine(fout, indent);
for (content = node.content; content != null; content = content.next) {
printTree(fout, (short) (mode | PREFORMATTED | NOWRAP), indent, lexer, content);
}
condFlushLine(fout, indent);
printEndTag(fout, mode, indent, node);
flushLine(fout, indent);
if (this.configuration.IndentContent == false && node.next != null) {
flushLine(fout, indent);
}
} else if (node.tag == tt.tagStyle || node.tag == tt.tagScript) {
condFlushLine(fout, indent);
indent = 0;
condFlushLine(fout, indent);
printTag(lexer, fout, mode, indent, node);
flushLine(fout, indent);
for (content = node.content; content != null; content = content.next) {
printTree(fout, (short) (mode | PREFORMATTED | NOWRAP | CDATA), indent, lexer, content);
}
condFlushLine(fout, indent);
printEndTag(fout, mode, indent, node);
flushLine(fout, indent);
if (this.configuration.IndentContent == false && node.next != null) {
flushLine(fout, indent);
}
} else if ((node.tag.model & Dict.CM_INLINE) != 0) {
if (this.configuration.MakeClean) {
/* discards <font> and </font> tags */
if (node.tag == tt.tagFont) {
for (content = node.content; content != null; content = content.next) {
printTree(fout, mode, indent, lexer, content);
}
return;
}
/* replace <nobr>...</nobr> by or etc. */
if (node.tag == tt.tagNobr) {
for (content = node.content; content != null; content = content.next) {
printTree(fout, (short) (mode | NOWRAP), indent, lexer, content);
}
return;
}
}
/* otherwise a normal inline element */
printTag(lexer, fout, mode, indent, node);
/* indent content for SELECT, TEXTAREA, MAP, OBJECT and APPLET */
if (shouldIndent(node)) {
condFlushLine(fout, indent);
indent += this.configuration.spaces;
for (content = node.content; content != null; content = content.next) {
printTree(fout, mode, indent, lexer, content);
}
condFlushLine(fout, indent);
indent -= this.configuration.spaces;
condFlushLine(fout, indent);
} else {
for (content = node.content; content != null; content = content.next) {
printTree(fout, mode, indent, lexer, content);
}
}
printEndTag(fout, mode, indent, node);
} else /* other tags */ {
condFlushLine(fout, indent);
if (this.configuration.SmartIndent && node.prev != null) {
flushLine(fout, indent);
}
if (this.configuration.HideEndTags == false
|| !(node.tag != null && (node.tag.model & Dict.CM_OMITST) != 0)) {
printTag(lexer, fout, mode, indent, node);
if (shouldIndent(node)) {
condFlushLine(fout, indent);
} else if ((node.tag.model & Dict.CM_HTML) != 0 || node.tag == tt.tagNoframes
|| (node.tag.model & Dict.CM_HEAD) != 0 && !(node.tag == tt.tagTitle)) {
flushLine(fout, indent);
}
}
if (node.tag == tt.tagBody && this.configuration.BurstSlides) {
printSlide(fout, mode, this.configuration.IndentContent ? indent + this.configuration.spaces
: indent, lexer);
} else {
last = null;
for (content = node.content; content != null; content = content.next) {
/* kludge for naked text before block level tag */
if (last != null && !this.configuration.IndentContent && last.type == Node.TextNode
&& content.tag != null && (content.tag.model & Dict.CM_BLOCK) != 0) {
flushLine(fout, indent);
flushLine(fout, indent);
}
printTree(fout, mode, shouldIndent(node) ? indent + this.configuration.spaces : indent, lexer,
content);
last = content;
}
}
/* don't flush line for td and th */
if (shouldIndent(node)
|| ((node.tag.model & Dict.CM_HTML) != 0 || node.tag == tt.tagNoframes || (node.tag.model & Dict.CM_HEAD) != 0
&& !(node.tag == tt.tagTitle)) && this.configuration.HideEndTags == false) {
condFlushLine(fout, this.configuration.IndentContent ? indent + this.configuration.spaces : indent);
if (this.configuration.HideEndTags == false || !((node.tag.model & Dict.CM_OPT) != 0)) {
printEndTag(fout, mode, indent, node);
flushLine(fout, indent);
}
} else {
if (this.configuration.HideEndTags == false || !((node.tag.model & Dict.CM_OPT) != 0)) {
printEndTag(fout, mode, indent, node);
}
flushLine(fout, indent);
}
if (this.configuration.IndentContent == false && node.next != null
&& this.configuration.HideEndTags == false
&& (node.tag.model & (Dict.CM_BLOCK | Dict.CM_LIST | Dict.CM_DEFLIST | Dict.CM_TABLE)) != 0) {
flushLine(fout, indent);
}
}
}
}
public void printXMLTree(Out fout, short mode, int indent, Lexer lexer, Node node) {
TagTable tt = this.configuration.tt;
if (node == null) {
return;
}
if (node.type == Node.TextNode) {
printText(fout, mode, indent, node.textarray, node.start, node.end);
} else if (node.type == Node.CommentTag) {
condFlushLine(fout, indent);
printComment(fout, 0, node);
condFlushLine(fout, 0);
} else if (node.type == Node.RootNode) {
Node content;
for (content = node.content; content != null; content = content.next) {
printXMLTree(fout, mode, indent, lexer, content);
}
} else if (node.type == Node.DocTypeTag) {
printDocType(fout, indent, node);
} else if (node.type == Node.ProcInsTag) {
printPI(fout, indent, node);
} else if (node.type == Node.SectionTag) {
printSection(fout, indent, node);
} else if (node.type == Node.AspTag) {
printAsp(fout, indent, node);
} else if (node.type == Node.JsteTag) {
printJste(fout, indent, node);
} else if (node.type == Node.PhpTag) {
printPhp(fout, indent, node);
} else if ((node.tag.model & Dict.CM_EMPTY) != 0 || node.type == Node.StartEndTag) {
condFlushLine(fout, indent);
printTag(lexer, fout, mode, indent, node);
flushLine(fout, indent);
if (node.next != null) {
flushLine(fout, indent);
}
} else /* some kind of container element */ {
Node content;
boolean mixed = false;
int cindent;
for (content = node.content; content != null; content = content.next) {
if (content.type == Node.TextNode) {
mixed = true;
break;
}
}
condFlushLine(fout, indent);
if (ParserImpl.XMLPreserveWhiteSpace(node, tt)) {
indent = 0;
cindent = 0;
mixed = false;
} else if (mixed) {
cindent = indent;
} else {
cindent = indent + this.configuration.spaces;
}
printTag(lexer, fout, mode, indent, node);
if (!mixed) {
flushLine(fout, indent);
}
for (content = node.content; content != null; content = content.next) {
printXMLTree(fout, mode, cindent, lexer, content);
}
if (!mixed) {
condFlushLine(fout, cindent);
}
printEndTag(fout, mode, indent, node);
condFlushLine(fout, indent);
if (node.next != null) {
flushLine(fout, indent);
}
}
}
/* split parse tree by h2 elements and output to separate files */
/* counts number of h2 children belonging to node */
public int countSlides(Node node) {
int n = 1;
TagTable tt = this.configuration.tt;
for (node = node.content; node != null; node = node.next) {
if (node.tag == tt.tagH2) {
++n;
}
}
return n;
}
/*
* inserts a space gif called "dot.gif" to ensure that the slide is at least
* n pixels high
*/
private void printVertSpacer(Out fout, int indent) {
condFlushLine(fout, indent);
printString(fout, indent,
"<img width=\"0\" height=\"0\" hspace=\"1\" src=\"dot.gif\" vspace=\"%d\" align=\"left\">");
condFlushLine(fout, indent);
}
private void printNavBar(Out fout, int indent) {
String buf;
condFlushLine(fout, indent);
printString(fout, indent, "<center><small>");
if (slide > 1) {
buf = "<a href=\"slide" + new Integer(slide - 1).toString() + ".html\">previous</a> | ";
printString(fout, indent, buf);
condFlushLine(fout, indent);
if (slide < count) {
printString(fout, indent, "<a href=\"slide1.html\">start</a> | ");
} else {
printString(fout, indent, "<a href=\"slide1.html\">start</a>");
}
condFlushLine(fout, indent);
}
if (slide < count) {
buf = "<a href=\"slide" + new Integer(slide + 1).toString() + ".html\">next</a>";
printString(fout, indent, buf);
}
printString(fout, indent, "</small></center>");
condFlushLine(fout, indent);
}
/*
* Called from printTree to print the content of a slide from the node
* slidecontent. On return slidecontent points to the node starting the next
* slide or null. The variables slide and count are used to customise the
* navigation bar.
*/
public void printSlide(Out fout, short mode, int indent, Lexer lexer) {
Node content, last;
TagTable tt = this.configuration.tt;
/* insert div for onclick handler */
String s;
s = "<div onclick=\"document.location='slide" + new Integer(slide < count ? slide + 1 : 1).toString()
+ ".html'\">";
printString(fout, indent, s);
condFlushLine(fout, indent);
/* first print the h2 element and navbar */
if (slidecontent.tag == tt.tagH2) {
printNavBar(fout, indent);
/* now print an hr after h2 */
addC('<', linelen++);
addC(Lexer.foldCase('h', this.configuration.UpperCaseTags, this.configuration.XmlTags), linelen++);
addC(Lexer.foldCase('r', this.configuration.UpperCaseTags, this.configuration.XmlTags), linelen++);
if (this.configuration.XmlOut == true) {
printString(fout, indent, " />");
} else {
addC('>', linelen++);
}
if (this.configuration.IndentContent == true) {
condFlushLine(fout, indent);
}
/* PrintVertSpacer(fout, indent); */
/* condFlushLine(fout, indent); */
/* print the h2 element */
printTree(fout, mode, this.configuration.IndentContent ? indent + this.configuration.spaces : indent,
lexer, slidecontent);
slidecontent = slidecontent.next;
}
/* now continue until we reach the next h2 */
last = null;
content = slidecontent;
for (; content != null; content = content.next) {
if (content.tag == tt.tagH2) {
break;
}
/* kludge for naked text before block level tag */
if (last != null && !this.configuration.IndentContent && last.type == Node.TextNode && content.tag != null
&& (content.tag.model & Dict.CM_BLOCK) != 0) {
flushLine(fout, indent);
flushLine(fout, indent);
}
printTree(fout, mode, this.configuration.IndentContent ? indent + this.configuration.spaces : indent,
lexer, content);
last = content;
}
slidecontent = content;
/* now print epilog */
condFlushLine(fout, indent);
printString(fout, indent, "<br clear=\"all\">");
condFlushLine(fout, indent);
addC('<', linelen++);
addC(Lexer.foldCase('h', this.configuration.UpperCaseTags, this.configuration.XmlTags), linelen++);
addC(Lexer.foldCase('r', this.configuration.UpperCaseTags, this.configuration.XmlTags), linelen++);
if (this.configuration.XmlOut == true) {
printString(fout, indent, " />");
} else {
addC('>', linelen++);
}
if (this.configuration.IndentContent == true) {
condFlushLine(fout, indent);
}
printNavBar(fout, indent);
/* end tag for div */
printString(fout, indent, "</div>");
condFlushLine(fout, indent);
}
/*
* Add meta element for page transition effect, this works on IE but not NS
*/
public void addTransitionEffect(Lexer lexer, Node root, short effect, double duration) {
Node head = root.findHEAD(lexer.configuration.tt);
String transition;
if (0 <= effect && effect <= 23) {
transition = "revealTrans(Duration=" + new Double(duration).toString() + ",Transition=" + effect + ")";
} else {
transition = "blendTrans(Duration=" + new Double(duration).toString() + ")";
}
if (head != null) {
Node meta = lexer.inferredTag("meta");
meta.addAttribute("http-equiv", "Page-Enter");
meta.addAttribute("content", transition);
Node.insertNodeAtStart(head, meta);
}
}
public void createSlides(Lexer lexer, Node root) {
Node body;
String buf;
Out out = new OutImpl();
body = root.findBody(lexer.configuration.tt);
count = countSlides(body);
slidecontent = body.content;
addTransitionEffect(lexer, root, EFFECT_BLEND, 3.0);
for (slide = 1; slide <= count; ++slide) {
buf = "slide" + slide + ".html";
out.state = StreamIn.FSM_ASCII;
out.encoding = this.configuration.CharEncoding;
try {
out.out = new FileOutputStream(buf);
printTree(out, (short) 0, 0, lexer, root);
flushLine(out, 0);
out.out.close();
} catch (IOException e) {
System.err.println(buf + e.toString());
}
}
/*
* delete superfluous slides by deleting slideN.html for N = count+1,
* count+2, etc. until no such file is found.
*/
for (; ; ) {
buf = "slide" + slide + "html";
if (!new File(buf).delete()) {
break;
}
++slide;
}
}
}