0001: /*
0002: * @(#)PPrint.java 1.11 2000/08/16
0003: *
0004: */
0005:
0006: package org.w3c.tidy;
0007:
0008: /**
0009: *
0010: * Pretty print parse tree
0011: *
0012: * (c) 1998-2000 (W3C) MIT, INRIA, Keio University
0013: * See Tidy.java for the copyright notice.
0014: * Derived from <a href="http://www.w3.org/People/Raggett/tidy">
0015: * HTML Tidy Release 4 Aug 2000</a>
0016: *
0017: * @author Dave Raggett <dsr@w3.org>
0018: * @author Andy Quick <ac.quick@sympatico.ca> (translation to Java)
0019: * @version 1.0, 1999/05/22
0020: * @version 1.0.1, 1999/05/29
0021: * @version 1.1, 1999/06/18 Java Bean
0022: * @version 1.2, 1999/07/10 Tidy Release 7 Jul 1999
0023: * @version 1.3, 1999/07/30 Tidy Release 26 Jul 1999
0024: * @version 1.4, 1999/09/04 DOM support
0025: * @version 1.5, 1999/10/23 Tidy Release 27 Sep 1999
0026: * @version 1.6, 1999/11/01 Tidy Release 22 Oct 1999
0027: * @version 1.7, 1999/12/06 Tidy Release 30 Nov 1999
0028: * @version 1.8, 2000/01/22 Tidy Release 13 Jan 2000
0029: * @version 1.9, 2000/06/03 Tidy Release 30 Apr 2000
0030: * @version 1.10, 2000/07/22 Tidy Release 8 Jul 2000
0031: * @version 1.11, 2000/08/16 Tidy Release 4 Aug 2000
0032: */
0033:
0034: /*
0035: Block-level and unknown elements are printed on
0036: new lines and their contents indented 2 spaces
0037:
0038: Inline elements are printed inline.
0039:
0040: Inline content is wrapped on spaces (except in
0041: attribute values or preformatted text, after
0042: start tags and before end tags
0043: */
0044:
0045: import java.io.FileOutputStream;
0046: import java.io.File;
0047:
0048: import java.io.IOException;
0049: import java.io.FileNotFoundException;
0050:
0051: public class PPrint {
0052:
0053: /* page transition effects */
0054:
0055: public static final short EFFECT_BLEND = -1;
0056: public static final short EFFECT_BOX_IN = 0;
0057: public static final short EFFECT_BOX_OUT = 1;
0058: public static final short EFFECT_CIRCLE_IN = 2;
0059: public static final short EFFECT_CIRCLE_OUT = 3;
0060: public static final short EFFECT_WIPE_UP = 4;
0061: public static final short EFFECT_WIPE_DOWN = 5;
0062: public static final short EFFECT_WIPE_RIGHT = 6;
0063: public static final short EFFECT_WIPE_LEFT = 7;
0064: public static final short EFFECT_VERT_BLINDS = 8;
0065: public static final short EFFECT_HORZ_BLINDS = 9;
0066: public static final short EFFECT_CHK_ACROSS = 10;
0067: public static final short EFFECT_CHK_DOWN = 11;
0068: public static final short EFFECT_RND_DISSOLVE = 12;
0069: public static final short EFFECT_SPLIT_VIRT_IN = 13;
0070: public static final short EFFECT_SPLIT_VIRT_OUT = 14;
0071: public static final short EFFECT_SPLIT_HORZ_IN = 15;
0072: public static final short EFFECT_SPLIT_HORZ_OUT = 16;
0073: public static final short EFFECT_STRIPS_LEFT_DOWN = 17;
0074: public static final short EFFECT_STRIPS_LEFT_UP = 18;
0075: public static final short EFFECT_STRIPS_RIGHT_DOWN = 19;
0076: public static final short EFFECT_STRIPS_RIGHT_UP = 20;
0077: public static final short EFFECT_RND_BARS_HORZ = 21;
0078: public static final short EFFECT_RND_BARS_VERT = 22;
0079: public static final short EFFECT_RANDOM = 23;
0080:
0081: private static final short NORMAL = 0;
0082: private static final short PREFORMATTED = 1;
0083: private static final short COMMENT = 2;
0084: private static final short ATTRIBVALUE = 4;
0085: private static final short NOWRAP = 8;
0086: private static final short CDATA = 16;
0087:
0088: private int[] linebuf = null;
0089: private int lbufsize = 0;
0090: private int linelen = 0;
0091: private int wraphere = 0;
0092: private boolean inAttVal = false;
0093: private boolean InString = false;
0094:
0095: private int slide = 0;
0096: private int count = 0;
0097: private Node slidecontent = null;
0098:
0099: private Configuration configuration;
0100: private TagTable tt;//wzw
0101:
0102: public PPrint(Configuration configuration) {
0103: this .configuration = configuration;
0104: }
0105:
0106: /*
0107: 1010 A
0108: 1011 B
0109: 1100 C
0110: 1101 D
0111: 1110 E
0112: 1111 F
0113: */
0114:
0115: /* return one less that the number of bytes used by UTF-8 char */
0116: /* str points to 1st byte, *ch initialized to 1st byte */
0117: public static int getUTF8(byte[] str, int start, MutableInteger ch) {
0118: int c, n, i, bytes;
0119:
0120: c = ((int) str[start]) & 0xFF; // Convert to unsigned.
0121:
0122: if ((c & 0xE0) == 0xC0) /* 110X XXXX two bytes */
0123: {
0124: n = c & 31;
0125: bytes = 2;
0126: } else if ((c & 0xF0) == 0xE0) /* 1110 XXXX three bytes */
0127: {
0128: n = c & 15;
0129: bytes = 3;
0130: } else if ((c & 0xF8) == 0xF0) /* 1111 0XXX four bytes */
0131: {
0132: n = c & 7;
0133: bytes = 4;
0134: } else if ((c & 0xFC) == 0xF8) /* 1111 10XX five bytes */
0135: {
0136: n = c & 3;
0137: bytes = 5;
0138: } else if ((c & 0xFE) == 0xFC) /* 1111 110X six bytes */
0139:
0140: {
0141: n = c & 1;
0142: bytes = 6;
0143: } else /* 0XXX XXXX one byte */
0144: {
0145: ch.value = c;
0146: return 0;
0147: }
0148:
0149: /* successor bytes should have the form 10XX XXXX */
0150: for (i = 1; i < bytes; ++i) {
0151: c = ((int) str[start + i]) & 0xFF; // Convert to unsigned.
0152: n = (n << 6) | (c & 0x3F);
0153: }
0154:
0155: ch.value = n;
0156: return bytes - 1;
0157: }
0158:
0159: /* store char c as UTF-8 encoded byte stream */
0160: public static int putUTF8(byte[] buf, int start, int c) {
0161: if (c < 128)
0162: buf[start++] = (byte) c;
0163: else if (c <= 0x7FF) {
0164: buf[start++] = (byte) (0xC0 | (c >> 6));
0165: buf[start++] = (byte) (0x80 | (c & 0x3F));
0166: } else if (c <= 0xFFFF) {
0167: buf[start++] = (byte) (0xE0 | (c >> 12));
0168: buf[start++] = (byte) (0x80 | ((c >> 6) & 0x3F));
0169: buf[start++] = (byte) (0x80 | (c & 0x3F));
0170: } else if (c <= 0x1FFFFF) {
0171: buf[start++] = (byte) (0xF0 | (c >> 18));
0172: buf[start++] = (byte) (0x80 | ((c >> 12) & 0x3F));
0173: buf[start++] = (byte) (0x80 | ((c >> 6) & 0x3F));
0174: buf[start++] = (byte) (0x80 | (c & 0x3F));
0175: } else {
0176: buf[start++] = (byte) (0xF8 | (c >> 24));
0177: buf[start++] = (byte) (0x80 | ((c >> 18) & 0x3F));
0178: buf[start++] = (byte) (0x80 | ((c >> 12) & 0x3F));
0179: buf[start++] = (byte) (0x80 | ((c >> 6) & 0x3F));
0180: buf[start++] = (byte) (0x80 | (c & 0x3F));
0181: }
0182:
0183: return start;
0184: }
0185:
0186: private void addC(int c, int index) {
0187: if (index + 1 >= lbufsize) {
0188: while (index + 1 >= lbufsize) {
0189: if (lbufsize == 0)
0190: lbufsize = 256;
0191: else
0192: lbufsize = lbufsize * 2;
0193: }
0194:
0195: int[] temp = new int[lbufsize];
0196: if (linebuf != null)
0197: System.arraycopy(linebuf, 0, temp, 0, index);
0198: linebuf = temp;
0199: }
0200:
0201: linebuf[index] = c;
0202: }
0203:
0204: private void wrapLine(Out fout, int indent) {
0205: int i, p, q;
0206:
0207: if (wraphere == 0)
0208: return;
0209:
0210: for (i = 0; i < indent; ++i)
0211: fout.outc((int) ' ');
0212:
0213: for (i = 0; i < wraphere; ++i)
0214: fout.outc(linebuf[i]);
0215:
0216: if (InString) {
0217: fout.outc((int) ' ');
0218: fout.outc((int) '\\');
0219: }
0220:
0221: fout.newline();
0222:
0223: if (linelen > wraphere) {
0224: p = 0;
0225:
0226: if (linebuf[wraphere] == ' ')
0227: ++wraphere;
0228:
0229: q = wraphere;
0230: addC('\0', linelen);
0231:
0232: while (true) {
0233: linebuf[p] = linebuf[q];
0234: if (linebuf[q] == 0)
0235: break;
0236: p++;
0237: q++;
0238: }
0239: linelen -= wraphere;
0240: } else
0241: linelen = 0;
0242:
0243: wraphere = 0;
0244: }
0245:
0246: private void wrapAttrVal(Out fout, int indent, boolean inString) {
0247: int i, p, q;
0248:
0249: for (i = 0; i < indent; ++i)
0250: fout.outc((int) ' ');
0251:
0252: for (i = 0; i < wraphere; ++i)
0253: fout.outc(linebuf[i]);
0254:
0255: fout.outc((int) ' ');
0256:
0257: if (inString)
0258: fout.outc((int) '\\');
0259:
0260: fout.newline();
0261:
0262: if (linelen > wraphere) {
0263: p = 0;
0264:
0265: if (linebuf[wraphere] == ' ')
0266: ++wraphere;
0267:
0268: q = wraphere;
0269: addC('\0', linelen);
0270:
0271: while (true) {
0272: linebuf[p] = linebuf[q];
0273: if (linebuf[q] == 0)
0274: break;
0275: p++;
0276: q++;
0277: }
0278: linelen -= wraphere;
0279: } else
0280: linelen = 0;
0281:
0282: wraphere = 0;
0283: }
0284:
0285: public void flushLine(Out fout, int indent) {
0286: int i;
0287:
0288: if (linelen > 0) {
0289: if (indent + linelen >= this .configuration.wraplen)
0290: wrapLine(fout, indent);
0291:
0292: if (!inAttVal || this .configuration.IndentAttributes) {
0293: for (i = 0; i < indent; ++i)
0294: fout.outc((int) ' ');
0295: }
0296:
0297: for (i = 0; i < linelen; ++i)
0298: fout.outc(linebuf[i]);
0299: }
0300:
0301: fout.newline();
0302: linelen = 0;
0303: wraphere = 0;
0304: inAttVal = false;
0305: }
0306:
0307: public void condFlushLine(Out fout, int indent) {
0308: int i;
0309:
0310: if (linelen > 0) {
0311: if (indent + linelen >= this .configuration.wraplen)
0312: wrapLine(fout, indent);
0313:
0314: if (!inAttVal || this .configuration.IndentAttributes) {
0315: for (i = 0; i < indent; ++i)
0316: fout.outc((int) ' ');
0317: }
0318:
0319: for (i = 0; i < linelen; ++i)
0320: fout.outc(linebuf[i]);
0321:
0322: fout.newline();
0323: linelen = 0;
0324: wraphere = 0;
0325: inAttVal = false;
0326: }
0327: }
0328:
0329: private void printChar(int c, short mode) {
0330: String entity;
0331:
0332: if (c == ' '
0333: && !((mode & (PREFORMATTED | COMMENT | ATTRIBVALUE)) != 0)) {
0334: /* coerce a space character to a non-breaking space */
0335: if ((mode & NOWRAP) != 0) {
0336: /* by default XML doesn't define */
0337: if (this .configuration.NumEntities
0338: || this .configuration.XmlTags) {
0339: addC('&', linelen++);
0340: addC('#', linelen++);
0341: addC('1', linelen++);
0342: addC('6', linelen++);
0343: addC('0', linelen++);
0344: addC(';', linelen++);
0345: } else /* otherwise use named entity */
0346: {
0347: addC('&', linelen++);
0348: addC('n', linelen++);
0349: addC('b', linelen++);
0350: addC('s', linelen++);
0351: addC('p', linelen++);
0352: addC(';', linelen++);
0353: }
0354: return;
0355: } else
0356: wraphere = linelen;
0357: }
0358:
0359: /* comment characters are passed raw */
0360: if ((mode & COMMENT) != 0) {
0361: addC(c, linelen++);
0362: return;
0363: }
0364:
0365: /* except in CDATA map < to < etc. */
0366: if (!((mode & CDATA) != 0)) {
0367: if (c == '<') {
0368: addC('&', linelen++);
0369: addC('l', linelen++);
0370: addC('t', linelen++);
0371: addC(';', linelen++);
0372: return;
0373: }
0374:
0375: if (c == '>') {
0376: addC('&', linelen++);
0377: addC('g', linelen++);
0378: addC('t', linelen++);
0379: addC(';', linelen++);
0380: return;
0381: }
0382:
0383: /*
0384: naked '&' chars can be left alone or
0385: quoted as & The latter is required
0386: for XML where naked '&' are illegal.
0387: */
0388: if (c == '&' && this .configuration.QuoteAmpersand) {
0389: addC('&', linelen++);
0390: addC('a', linelen++);
0391: addC('m', linelen++);
0392: addC('p', linelen++);
0393: addC(';', linelen++);
0394: return;
0395: }
0396:
0397: if (c == '"' && this .configuration.QuoteMarks) {
0398: addC('&', linelen++);
0399: addC('q', linelen++);
0400: addC('u', linelen++);
0401: addC('o', linelen++);
0402: addC('t', linelen++);
0403: addC(';', linelen++);
0404: return;
0405: }
0406:
0407: if (c == '\'' && this .configuration.QuoteMarks) {
0408: addC('&', linelen++);
0409: addC('#', linelen++);
0410: addC('3', linelen++);
0411: addC('9', linelen++);
0412: addC(';', linelen++);
0413: return;
0414: }
0415:
0416: if (c == 160
0417: && this .configuration.CharEncoding != Configuration.RAW) {
0418: if (this .configuration.QuoteNbsp) {
0419: addC('&', linelen++);
0420:
0421: if (this .configuration.NumEntities) {
0422: addC('#', linelen++);
0423: addC('1', linelen++);
0424: addC('6', linelen++);
0425: addC('0', linelen++);
0426: } else {
0427: addC('n', linelen++);
0428: addC('b', linelen++);
0429: addC('s', linelen++);
0430: addC('p', linelen++);
0431: }
0432:
0433: addC(';', linelen++);
0434: } else
0435: addC(c, linelen++);
0436:
0437: return;
0438: }
0439: }
0440:
0441: /* otherwise ISO 2022 characters are passed raw */
0442: if (this .configuration.CharEncoding == Configuration.ISO2022
0443: || this .configuration.CharEncoding == Configuration.RAW) {
0444: addC(c, linelen++);
0445: return;
0446: }
0447:
0448: /* if preformatted text, map to space */
0449: if (c == 160 && ((mode & PREFORMATTED) != 0)) {
0450: addC(' ', linelen++);
0451: return;
0452: }
0453:
0454: /*
0455: Filters from Word and PowerPoint often use smart
0456: quotes resulting in character codes between 128
0457: and 159. Unfortunately, the corresponding HTML 4.0
0458: entities for these are not widely supported. The
0459: following converts dashes and quotation marks to
0460: the nearest ASCII equivalent. My thanks to
0461: Andrzej Novosiolov for his help with this code.
0462: */
0463:
0464: if (this .configuration.MakeClean) {
0465: if (c >= 0x2013 && c <= 0x201E) {
0466: switch (c) {
0467: case 0x2013:
0468: case 0x2014:
0469: c = '-';
0470: break;
0471: case 0x2018:
0472: case 0x2019:
0473: case 0x201A:
0474: c = '\'';
0475: break;
0476: case 0x201C:
0477: case 0x201D:
0478: case 0x201E:
0479: c = '"';
0480: break;
0481: }
0482: }
0483: }
0484:
0485: /* don't map latin-1 chars to entities */
0486: if (this .configuration.CharEncoding == Configuration.LATIN1) {
0487: if (c > 255) /* multi byte chars */
0488: {
0489: if (!this .configuration.NumEntities) {
0490: entity = EntityTable.getDefaultEntityTable()
0491: .entityName((short) c);
0492: if (entity != null)
0493: entity = "&" + entity + ";";
0494: else
0495: entity = "&#" + c + ";";
0496: } else
0497: entity = "&#" + c + ";";
0498:
0499: for (int i = 0; i < entity.length(); i++)
0500: addC((int) entity.charAt(i), linelen++);
0501:
0502: return;
0503: }
0504:
0505: if (c > 126 && c < 160) {
0506: entity = "&#" + c + ";";
0507:
0508: for (int i = 0; i < entity.length(); i++)
0509: addC((int) entity.charAt(i), linelen++);
0510:
0511: return;
0512: }
0513:
0514: addC(c, linelen++);
0515: return;
0516: }
0517:
0518: /* don't map utf8 chars to entities */
0519: if (this .configuration.CharEncoding == Configuration.UTF8) {
0520: addC(c, linelen++);
0521: return;
0522: }
0523:
0524: /* use numeric entities only for XML */
0525: if (this .configuration.XmlTags) {
0526: /* if ASCII use numeric entities for chars > 127 */
0527: if (c > 127
0528: && this .configuration.CharEncoding == Configuration.ASCII) {
0529: entity = "&#" + c + ";";
0530:
0531: for (int i = 0; i < entity.length(); i++)
0532: addC((int) entity.charAt(i), linelen++);
0533:
0534: return;
0535: }
0536:
0537: /* otherwise output char raw */
0538: addC(c, linelen++);
0539: return;
0540: }
0541:
0542: /* default treatment for ASCII */
0543: if (c > 126 || (c < ' ' && c != '\t')) {
0544: if (!this .configuration.NumEntities) {
0545: entity = EntityTable.getDefaultEntityTable()
0546: .entityName((short) c);
0547: if (entity != null)
0548: entity = "&" + entity + ";";
0549: else
0550: entity = "&#" + c + ";";
0551: } else
0552: entity = "&#" + c + ";";
0553:
0554: for (int i = 0; i < entity.length(); i++)
0555: addC((int) entity.charAt(i), linelen++);
0556:
0557: return;
0558: }
0559:
0560: addC(c, linelen++);
0561: }
0562:
0563: /*
0564: The line buffer is uint not char so we can
0565: hold Unicode values unencoded. The translation
0566: to UTF-8 is deferred to the outc routine called
0567: to flush the line buffer.
0568: */
0569: private void printText(Out fout, short mode, int indent,
0570: byte[] textarray, int start, int end) {
0571: int i, c;
0572: MutableInteger ci = new MutableInteger();
0573:
0574: for (i = start; i < end; ++i) {
0575: if (indent + linelen >= this .configuration.wraplen)
0576: wrapLine(fout, indent);
0577:
0578: c = ((int) textarray[i]) & 0xFF; // Convert to unsigned.
0579:
0580: /* look for UTF-8 multibyte character */
0581: if (c > 0x7F) {
0582: i += getUTF8(textarray, i, ci);
0583: c = ci.value;
0584: }
0585:
0586: if (c == '\n') {
0587: flushLine(fout, indent);
0588: continue;
0589: }
0590:
0591: printChar(c, mode);
0592: }
0593: }
0594:
0595: private void printString(Out fout, int indent, String str) {
0596: for (int i = 0; i < str.length(); i++)
0597: addC((int) str.charAt(i), linelen++);
0598: }
0599:
0600: private void printAttrValue(Out fout, int indent, String value,
0601: int delim, boolean wrappable) {
0602: int c;
0603: MutableInteger ci = new MutableInteger();
0604: boolean wasinstring = false;
0605: byte[] valueChars = null;
0606: int i;
0607: short mode = (wrappable ? (short) (NORMAL | ATTRIBVALUE)
0608: : (short) (PREFORMATTED | ATTRIBVALUE));
0609:
0610: if (value != null) {
0611: valueChars = Lexer.getBytes(value);
0612: }
0613:
0614: /* look for ASP, Tango or PHP instructions for computed attribute value */
0615: if (valueChars != null && valueChars.length >= 5
0616: && valueChars[0] == '<') {
0617: if (valueChars[1] == '%' || valueChars[1] == '@'
0618: || (new String(valueChars, 0, 5)).equals("<?php"))
0619: mode |= CDATA;
0620: }
0621:
0622: if (delim == 0)
0623: delim = '"';
0624:
0625: addC('=', linelen++);
0626:
0627: /* don't wrap after "=" for xml documents */
0628: if (!this .configuration.XmlOut) {
0629:
0630: if (indent + linelen < this .configuration.wraplen)
0631: wraphere = linelen;
0632:
0633: if (indent + linelen >= this .configuration.wraplen)
0634: wrapLine(fout, indent);
0635:
0636: if (indent + linelen < this .configuration.wraplen)
0637: wraphere = linelen;
0638: else
0639: condFlushLine(fout, indent);
0640: }
0641:
0642: addC(delim, linelen++);
0643:
0644: if (value != null) {
0645: InString = false;
0646:
0647: i = 0;
0648: while (i < valueChars.length) {
0649: c = ((int) valueChars[i]) & 0xFF; // Convert to unsigned.
0650:
0651: if (wrappable
0652: && c == ' '
0653: && indent + linelen < this .configuration.wraplen) {
0654: wraphere = linelen;
0655: wasinstring = InString;
0656: }
0657:
0658: if (wrappable
0659: && wraphere > 0
0660: && indent + linelen >= this .configuration.wraplen)
0661: wrapAttrVal(fout, indent, wasinstring);
0662:
0663: if (c == delim) {
0664: String entity;
0665:
0666: entity = (c == '"' ? """ : "'");
0667:
0668: for (int j = 0; j < entity.length(); j++)
0669: addC(entity.charAt(j), linelen++);
0670:
0671: ++i;
0672: continue;
0673: } else if (c == '"') {
0674: if (this .configuration.QuoteMarks) {
0675: addC('&', linelen++);
0676: addC('q', linelen++);
0677: addC('u', linelen++);
0678: addC('o', linelen++);
0679: addC('t', linelen++);
0680: addC(';', linelen++);
0681: } else
0682: addC('"', linelen++);
0683:
0684: if (delim == '\'')
0685: InString = !InString;
0686:
0687: ++i;
0688: continue;
0689: } else if (c == '\'') {
0690: if (this .configuration.QuoteMarks) {
0691: addC('&', linelen++);
0692: addC('#', linelen++);
0693: addC('3', linelen++);
0694: addC('9', linelen++);
0695: addC(';', linelen++);
0696: } else
0697: addC('\'', linelen++);
0698:
0699: if (delim == '"')
0700: InString = !InString;
0701:
0702: ++i;
0703: continue;
0704: }
0705:
0706: /* look for UTF-8 multibyte character */
0707: if (c > 0x7F) {
0708: i += getUTF8(valueChars, i, ci);
0709: c = ci.value;
0710: }
0711:
0712: ++i;
0713:
0714: if (c == '\n') {
0715: flushLine(fout, indent);
0716: continue;
0717: }
0718:
0719: printChar(c, mode);
0720: }
0721: }
0722:
0723: InString = false;
0724: addC(delim, linelen++);
0725: }
0726:
0727: private void printAttribute(Out fout, int indent, Node node,
0728: AttVal attr) {
0729: String name;
0730: boolean wrappable = false;
0731:
0732: if (this .configuration.IndentAttributes) {
0733: flushLine(fout, indent);
0734: indent += this .configuration.spaces;
0735: }
0736:
0737: name = attr.attribute;
0738:
0739: if (indent + linelen >= this .configuration.wraplen)
0740: wrapLine(fout, indent);
0741:
0742: if (!this .configuration.XmlTags && !this .configuration.XmlOut
0743: && attr.dict != null) {
0744: if (AttributeTable.getDefaultAttributeTable()
0745: .isScript(name))
0746: wrappable = this .configuration.WrapScriptlets;
0747: else if (!attr.dict.nowrap
0748: && this .configuration.WrapAttVals)
0749: wrappable = true;
0750: }
0751:
0752: if (indent + linelen < this .configuration.wraplen) {
0753: wraphere = linelen;
0754: addC(' ', linelen++);
0755: } else {
0756: condFlushLine(fout, indent);
0757: addC(' ', linelen++);
0758: }
0759:
0760: for (int i = 0; i < name.length(); i++)
0761: addC((int) Lexer.foldCase(name.charAt(i),
0762: this .configuration.UpperCaseAttrs,
0763: // BEGIN RAVE MODIFICATIONS
0764: // this.configuration.XmlTags),
0765: this .configuration.XmlTags
0766: || this .configuration.inputJspMode),
0767: // END RAVE MODIFICATIONS
0768: linelen++);
0769:
0770: if (indent + linelen >= this .configuration.wraplen)
0771: wrapLine(fout, indent);
0772:
0773: if (attr.value == null) {
0774: if (this .configuration.XmlTags || this .configuration.XmlOut)
0775: printAttrValue(fout, indent, attr.attribute,
0776: attr.delim, true);
0777: else if (!attr.isBoolAttribute() && !Node.isNewNode(node))
0778: printAttrValue(fout, indent, "", attr.delim, true);
0779: else if (indent + linelen < this .configuration.wraplen)
0780: wraphere = linelen;
0781:
0782: } else
0783: printAttrValue(fout, indent, attr.value, attr.delim,
0784: wrappable);
0785: }
0786:
0787: private void printAttrs(Out fout, int indent, Node node, AttVal attr) {
0788: if (attr != null) {
0789: if (attr.next != null)
0790: printAttrs(fout, indent, node, attr.next);
0791:
0792: if (attr.attribute != null)
0793: printAttribute(fout, indent, node, attr);
0794: else if (attr.asp != null) {
0795: addC(' ', linelen++);
0796: printAsp(fout, indent, attr.asp);
0797: } else if (attr.php != null) {
0798: addC(' ', linelen++);
0799: printPhp(fout, indent, attr.php);
0800: }
0801: }
0802:
0803: /* add xml:space attribute to pre and other elements */
0804: if (configuration.XmlOut
0805: && configuration.XmlSpace
0806: && ParserImpl.XMLPreserveWhiteSpace(node,
0807: configuration.tt)
0808: && node.getAttrByName("xml:space") == null)
0809: printString(fout, indent, " xml:space=\"preserve\"");
0810: }
0811:
0812: /*
0813: Line can be wrapped immediately after inline start tag provided
0814: if follows a text node ending in a space, or it parent is an
0815: inline element that that rule applies to. This behaviour was
0816: reverse engineered from Netscape 3.0
0817: */
0818: private static boolean afterSpace(Node node) {
0819: Node prev;
0820: int c;
0821:
0822: if (node == null || node.tag == null
0823: || !((node.tag.model & Dict.CM_INLINE) != 0))
0824: return true;
0825:
0826: prev = node.prev;
0827:
0828: if (prev != null) {
0829: if (prev.type == Node.TextNode && prev.end > prev.start) {
0830: c = ((int) prev.textarray[prev.end - 1]) & 0xFF; // Convert to unsigned.
0831:
0832: if (c == 160 || c == ' ' || c == '\n')
0833: return true;
0834: }
0835:
0836: return false;
0837: }
0838:
0839: return afterSpace(node.parent);
0840: }
0841:
0842: private void printTag(Lexer lexer, Out fout, short mode,
0843: int indent, Node node) {
0844: char c;
0845: String p;
0846: TagTable tt = this .configuration.tt;
0847:
0848: addC('<', linelen++);
0849:
0850: if (node.type == Node.EndTag)
0851: addC('/', linelen++);
0852:
0853: p = node.element;
0854: for (int i = 0; i < p.length(); i++)
0855: addC((int) Lexer.foldCase(p.charAt(i),
0856: this .configuration.UpperCaseTags,
0857: // BEGIN RAVE MODIFICATIONS
0858: // this.configuration.XmlTags),
0859: this .configuration.XmlTags
0860: || this .configuration.inputJspMode),
0861: // END RAVE MODIFICATIONS
0862: linelen++);
0863:
0864: printAttrs(fout, indent, node, node.attributes);
0865:
0866: if ((this .configuration.XmlOut || lexer != null
0867: && lexer.isvoyager)
0868: && (node.type == Node.StartEndTag || (node.tag.model & Dict.CM_EMPTY) != 0)) {
0869: addC(' ', linelen++); /* compatibility hack */
0870: addC('/', linelen++);
0871: }
0872:
0873: addC('>', linelen++);
0874: ;
0875:
0876: if (node.type != Node.StartEndTag
0877: && !((mode & PREFORMATTED) != 0)) {
0878: if (indent + linelen >= this .configuration.wraplen)
0879: wrapLine(fout, indent);
0880:
0881: if (indent + linelen < this .configuration.wraplen) {
0882: /*
0883: wrap after start tag if is <br/> or if it's not
0884: inline or it is an empty tag followed by </a>
0885: */
0886: if (afterSpace(node)) {
0887: if (!((mode & NOWRAP) != 0)
0888: && (!((node.tag.model & Dict.CM_INLINE) != 0)
0889: || (node.tag == tt.tagBr) || (((node.tag.model & Dict.CM_EMPTY) != 0)
0890: && node.next == null && node.parent.tag == tt.tagA))) {
0891: wraphere = linelen;
0892: }
0893: }
0894: } else
0895: condFlushLine(fout, indent);
0896: }
0897:
0898: //wzw add begin.
0899: if (node.tag == tt.tagStyle || node.tag == tt.tagScript) {
0900: flushLine(fout, indent);
0901: addC('<', linelen++);
0902: addC('!', linelen++);
0903: addC('[', linelen++);
0904: addC('C', linelen++);
0905: addC('D', linelen++);
0906: addC('A', linelen++);
0907: addC('T', linelen++);
0908: addC('A', linelen++);
0909: addC('[', linelen++);
0910: }
0911: //wzw add end.
0912: }
0913:
0914: private void printEndTag(Out fout, short mode, int indent, Node node) {
0915: char c;
0916: String p;
0917:
0918: /*
0919: Netscape ignores SGML standard by not ignoring a
0920: line break before </A> or </U> etc. To avoid rendering
0921: this as an underlined space, I disable line wrapping
0922: before inline end tags by the #if 0 ... #endif
0923: */
0924: if (false) {
0925: if (indent + linelen < this .configuration.wraplen
0926: && !((mode & NOWRAP) != 0))
0927: wraphere = linelen;
0928: }
0929:
0930: //wzw add begin.
0931: if (node.tag == tt.tagStyle || node.tag == tt.tagScript) {
0932: addC(']', linelen++);
0933: addC(']', linelen++);
0934: addC('>', linelen++);
0935: flushLine(fout, indent);
0936: }
0937: //wzw add end.
0938:
0939: addC('<', linelen++);
0940: addC('/', linelen++);
0941:
0942: p = node.element;
0943: for (int i = 0; i < p.length(); i++)
0944: addC((int) Lexer.foldCase(p.charAt(i),
0945: this .configuration.UpperCaseTags,
0946: // BEGIN RAVE MODIFICATIONS
0947: // this.configuration.XmlTags),
0948: this .configuration.XmlTags
0949: || this .configuration.inputJspMode),
0950: // END RAVE MODIFICATIONS
0951: linelen++);
0952:
0953: addC('>', linelen++);
0954: }
0955:
0956: private void printComment(Out fout, int indent, Node node) {
0957: if (indent + linelen < this .configuration.wraplen)
0958: wraphere = linelen;
0959:
0960: addC('<', linelen++);
0961: addC('!', linelen++);
0962: addC('-', linelen++);
0963: addC('-', linelen++);
0964: if (false) {
0965: if (linelen < this .configuration.wraplen)
0966: wraphere = linelen;
0967: }
0968: printText(fout, COMMENT, indent, node.textarray, node.start,
0969: node.end);
0970: if (false) {
0971: if (indent + linelen < this .configuration.wraplen)
0972: wraphere = linelen;
0973: }
0974: // See Lexer.java: AQ 8Jul2000
0975: addC('-', linelen++);
0976: addC('-', linelen++);
0977: addC('>', linelen++);
0978:
0979: if (node.linebreak)
0980: flushLine(fout, indent);
0981: }
0982:
0983: private void printDocType(Out fout, int indent, Node node) {
0984: boolean q = this .configuration.QuoteMarks;
0985:
0986: this .configuration.QuoteMarks = false;
0987:
0988: if (indent + linelen < this .configuration.wraplen)
0989: wraphere = linelen;
0990:
0991: condFlushLine(fout, indent);
0992:
0993: addC('<', linelen++);
0994: addC('!', linelen++);
0995: addC('D', linelen++);
0996: addC('O', linelen++);
0997: addC('C', linelen++);
0998: addC('T', linelen++);
0999: addC('Y', linelen++);
1000: addC('P', linelen++);
1001: addC('E', linelen++);
1002: addC(' ', linelen++);
1003:
1004: if (indent + linelen < this .configuration.wraplen)
1005: wraphere = linelen;
1006:
1007: printText(fout, (short) 0, indent, node.textarray, node.start,
1008: node.end);
1009:
1010: if (linelen < this .configuration.wraplen)
1011: wraphere = linelen;
1012:
1013: addC('>', linelen++);
1014: this .configuration.QuoteMarks = q;
1015: condFlushLine(fout, indent);
1016: }
1017:
1018: private void printPI(Out fout, int indent, Node node) {
1019: if (indent + linelen < this .configuration.wraplen)
1020: wraphere = linelen;
1021:
1022: addC('<', linelen++);
1023: addC('?', linelen++);
1024:
1025: /* set CDATA to pass < and > unescaped */
1026: printText(fout, CDATA, indent, node.textarray, node.start,
1027: node.end);
1028:
1029: if (node.textarray[node.end - 1] != (byte) '?')
1030: addC('?', linelen++);
1031:
1032: addC('>', linelen++);
1033: condFlushLine(fout, indent);
1034: }
1035:
1036: /* note ASP and JSTE share <% ... %> syntax */
1037: private void printAsp(Out fout, int indent, Node node) {
1038: int savewraplen = this .configuration.wraplen;
1039:
1040: /* disable wrapping if so requested */
1041:
1042: if (!this .configuration.WrapAsp || !this .configuration.WrapJste)
1043: this .configuration.wraplen = 0xFFFFFF; /* a very large number */
1044: if (false) { //#if 0
1045: if (indent + linelen < this .configuration.wraplen)
1046: wraphere = linelen;
1047: } //#endif
1048:
1049: addC('<', linelen++);
1050: addC('%', linelen++);
1051:
1052: printText(fout, (this .configuration.WrapAsp ? CDATA : COMMENT),
1053: indent, node.textarray, node.start, node.end);
1054:
1055: addC('%', linelen++);
1056: addC('>', linelen++);
1057: /* condFlushLine(fout, indent); */
1058: this .configuration.wraplen = savewraplen;
1059: }
1060:
1061: /* JSTE also supports <# ... #> syntax */
1062: private void printJste(Out fout, int indent, Node node) {
1063: int savewraplen = this .configuration.wraplen;
1064:
1065: /* disable wrapping if so requested */
1066:
1067: if (!this .configuration.WrapJste)
1068: this .configuration.wraplen = 0xFFFFFF; /* a very large number */
1069:
1070: addC('<', linelen++);
1071: addC('#', linelen++);
1072:
1073: printText(fout,
1074: (this .configuration.WrapJste ? CDATA : COMMENT),
1075: indent, node.textarray, node.start, node.end);
1076:
1077: addC('#', linelen++);
1078: addC('>', linelen++);
1079: /* condFlushLine(fout, indent); */
1080: this .configuration.wraplen = savewraplen;
1081: }
1082:
1083: /* PHP is based on XML processing instructions */
1084: private void printPhp(Out fout, int indent, Node node) {
1085: int savewraplen = this .configuration.wraplen;
1086:
1087: /* disable wrapping if so requested */
1088:
1089: if (!this .configuration.WrapPhp)
1090: this .configuration.wraplen = 0xFFFFFF; /* a very large number */
1091:
1092: if (false) { //#if 0
1093: if (indent + linelen < this .configuration.wraplen)
1094: wraphere = linelen;
1095: } //#endif
1096: addC('<', linelen++);
1097: addC('?', linelen++);
1098:
1099: printText(fout, (this .configuration.WrapPhp ? CDATA : COMMENT),
1100: indent, node.textarray, node.start, node.end);
1101:
1102: addC('?', linelen++);
1103: addC('>', linelen++);
1104: /* PCondFlushLine(fout, indent); */
1105: this .configuration.wraplen = savewraplen;
1106: }
1107:
1108: private void printCDATA(Out fout, int indent, Node node) {
1109: int savewraplen = this .configuration.wraplen;
1110:
1111: condFlushLine(fout, indent);
1112:
1113: /* disable wrapping */
1114:
1115: this .configuration.wraplen = 0xFFFFFF; /* a very large number */
1116:
1117: addC('<', linelen++);
1118: addC('!', linelen++);
1119: addC('[', linelen++);
1120: addC('C', linelen++);
1121: addC('D', linelen++);
1122: addC('A', linelen++);
1123: addC('T', linelen++);
1124: addC('A', linelen++);
1125: addC('[', linelen++);
1126:
1127: printText(fout, COMMENT, indent, node.textarray, node.start,
1128: node.end);
1129:
1130: addC(']', linelen++);
1131: addC(']', linelen++);
1132: addC('>', linelen++);
1133: condFlushLine(fout, indent);
1134: this .configuration.wraplen = savewraplen;
1135: }
1136:
1137: private void printSection(Out fout, int indent, Node node) {
1138: int savewraplen = this .configuration.wraplen;
1139:
1140: /* disable wrapping if so requested */
1141:
1142: if (!this .configuration.WrapSection)
1143: this .configuration.wraplen = 0xFFFFFF; /* a very large number */
1144:
1145: if (false) { //#if 0
1146: if (indent + linelen < this .configuration.wraplen)
1147: wraphere = linelen;
1148: } //#endif
1149: addC('<', linelen++);
1150: addC('!', linelen++);
1151: addC('[', linelen++);
1152:
1153: printText(fout, (this .configuration.WrapSection ? CDATA
1154: : COMMENT), indent, node.textarray, node.start,
1155: node.end);
1156:
1157: addC(']', linelen++);
1158: addC('>', linelen++);
1159: /* PCondFlushLine(fout, indent); */
1160: this .configuration.wraplen = savewraplen;
1161: }
1162:
1163: private boolean shouldIndent(Node node) {
1164: TagTable tt = this .configuration.tt;
1165:
1166: if (!this .configuration.IndentContent)
1167: return false;
1168:
1169: if (this .configuration.SmartIndent) {
1170: if (node.content != null
1171: && ((node.tag.model & Dict.CM_NO_INDENT) != 0)) {
1172: for (node = node.content; node != null; node = node.next)
1173: if (node.tag != null
1174: && (node.tag.model & Dict.CM_BLOCK) != 0)
1175: return true;
1176:
1177: return false;
1178: }
1179:
1180: if ((node.tag.model & Dict.CM_HEADING) != 0)
1181: return false;
1182:
1183: if (node.tag == tt.tagP)
1184: return false;
1185:
1186: if (node.tag == tt.tagTitle)
1187: return false;
1188: }
1189:
1190: if ((node.tag.model & (Dict.CM_FIELD | Dict.CM_OBJECT)) != 0)
1191: return true;
1192:
1193: if (node.tag == tt.tagMap)
1194: return true;
1195:
1196: return !((node.tag.model & Dict.CM_INLINE) != 0);
1197: }
1198:
1199: public void printTree(Out fout, short mode, int indent,
1200: Lexer lexer, Node node) {
1201: Node content, last;
1202: tt = this .configuration.tt;//wzw
1203:
1204: if (node == null)
1205: return;
1206:
1207: if (node.type == Node.TextNode)
1208: printText(fout, mode, indent, node.textarray, node.start,
1209: node.end);
1210: else if (node.type == Node.CommentTag) {
1211: printComment(fout, indent, node);
1212: } else if (node.type == Node.RootNode) {
1213: for (content = node.content; content != null; content = content.next)
1214: printTree(fout, mode, indent, lexer, content);
1215: } else if (node.type == Node.DocTypeTag)
1216: printDocType(fout, indent, node);
1217: else if (node.type == Node.ProcInsTag)
1218: printPI(fout, indent, node);
1219: else if (node.type == Node.CDATATag)
1220: printCDATA(fout, indent, node);
1221: else if (node.type == Node.SectionTag)
1222: printSection(fout, indent, node);
1223: else if (node.type == Node.AspTag)
1224: printAsp(fout, indent, node);
1225: else if (node.type == Node.JsteTag)
1226: printJste(fout, indent, node);
1227: else if (node.type == Node.PhpTag)
1228: printPhp(fout, indent, node);
1229: else if ((node.tag.model & Dict.CM_EMPTY) != 0
1230: || node.type == Node.StartEndTag) {
1231: if (!((node.tag.model & Dict.CM_INLINE) != 0))
1232: condFlushLine(fout, indent);
1233:
1234: if (node.tag == tt.tagBr && node.prev != null
1235: && node.prev.tag != tt.tagBr
1236: && this .configuration.BreakBeforeBR)
1237: flushLine(fout, indent);
1238:
1239: if (this .configuration.MakeClean && node.tag == tt.tagWbr)
1240: printString(fout, indent, " ");
1241: else
1242: printTag(lexer, fout, mode, indent, node);
1243:
1244: if (node.tag == tt.tagParam || node.tag == tt.tagArea)
1245: condFlushLine(fout, indent);
1246: else if (node.tag == tt.tagBr || node.tag == tt.tagHr)
1247: flushLine(fout, indent);
1248: } else /* some kind of container element */
1249: {
1250: if (node.tag != null
1251: && node.tag.parser == ParserImpl.getParsePre()) {
1252: condFlushLine(fout, indent);
1253:
1254: indent = 0;
1255: condFlushLine(fout, indent);
1256: printTag(lexer, fout, mode, indent, node);
1257: flushLine(fout, indent);
1258:
1259: for (content = node.content; content != null; content = content.next)
1260: printTree(fout,
1261: (short) (mode | PREFORMATTED | NOWRAP),
1262: indent, lexer, content);
1263:
1264: condFlushLine(fout, indent);
1265: printEndTag(fout, mode, indent, node);
1266: flushLine(fout, indent);
1267:
1268: if (this .configuration.IndentContent == false
1269: && node.next != null)
1270: flushLine(fout, indent);
1271: } else if (node.tag == tt.tagStyle
1272: || node.tag == tt.tagScript) {
1273: condFlushLine(fout, indent);
1274:
1275: indent = 0;
1276: condFlushLine(fout, indent);
1277: printTag(lexer, fout, mode, indent, node);
1278: flushLine(fout, indent);
1279:
1280: for (content = node.content; content != null; content = content.next)
1281: printTree(fout, (short) (mode | PREFORMATTED
1282: | NOWRAP | CDATA), indent, lexer, content);
1283:
1284: condFlushLine(fout, indent);
1285: printEndTag(fout, mode, indent, node);
1286: flushLine(fout, indent);
1287:
1288: if (this .configuration.IndentContent == false
1289: && node.next != null)
1290: flushLine(fout, indent);
1291: } else if ((node.tag.model & Dict.CM_INLINE) != 0) {
1292: if (this .configuration.MakeClean) {
1293: /* discards <font> and </font> tags */
1294: if (node.tag == tt.tagFont) {
1295: for (content = node.content; content != null; content = content.next)
1296: printTree(fout, mode, indent, lexer,
1297: content);
1298: return;
1299: }
1300:
1301: /* replace <nobr>...</nobr> by or   etc. */
1302: if (node.tag == tt.tagNobr) {
1303: for (content = node.content; content != null; content = content.next)
1304: printTree(fout, (short) (mode | NOWRAP),
1305: indent, lexer, content);
1306: return;
1307: }
1308: }
1309:
1310: /* otherwise a normal inline element */
1311:
1312: printTag(lexer, fout, mode, indent, node);
1313:
1314: /* indent content for SELECT, TEXTAREA, MAP, OBJECT and APPLET */
1315:
1316: if (shouldIndent(node)) {
1317: condFlushLine(fout, indent);
1318: indent += this .configuration.spaces;
1319:
1320: for (content = node.content; content != null; content = content.next)
1321: printTree(fout, mode, indent, lexer, content);
1322:
1323: condFlushLine(fout, indent);
1324: indent -= this .configuration.spaces;
1325: condFlushLine(fout, indent);
1326: } else {
1327:
1328: for (content = node.content; content != null; content = content.next)
1329: printTree(fout, mode, indent, lexer, content);
1330: }
1331:
1332: printEndTag(fout, mode, indent, node);
1333: } else /* other tags */
1334: {
1335: condFlushLine(fout, indent);
1336:
1337: if (this .configuration.SmartIndent && node.prev != null)
1338: flushLine(fout, indent);
1339:
1340: if (this .configuration.HideEndTags == false
1341: || !(node.tag != null && ((node.tag.model & Dict.CM_OMITST) != 0))) {
1342: printTag(lexer, fout, mode, indent, node);
1343:
1344: if (shouldIndent(node))
1345: condFlushLine(fout, indent);
1346: else if ((node.tag.model & Dict.CM_HTML) != 0
1347: || node.tag == tt.tagNoframes
1348: || ((node.tag.model & Dict.CM_HEAD) != 0 && !(node.tag == tt.tagTitle)))
1349: flushLine(fout, indent);
1350: }
1351:
1352: if (node.tag == tt.tagBody
1353: && this .configuration.BurstSlides)
1354: printSlide(fout, mode,
1355: (this .configuration.IndentContent ? indent
1356: + this .configuration.spaces
1357: : indent), lexer);
1358: else {
1359: last = null;
1360:
1361: for (content = node.content; content != null; content = content.next) {
1362: /* kludge for naked text before block level tag */
1363: if (last != null
1364: && !this .configuration.IndentContent
1365: && last.type == Node.TextNode
1366: && content.tag != null
1367: && (content.tag.model & Dict.CM_BLOCK) != 0) {
1368: flushLine(fout, indent);
1369: flushLine(fout, indent);
1370: }
1371:
1372: printTree(fout, mode,
1373: (shouldIndent(node) ? indent
1374: + this .configuration.spaces
1375: : indent), lexer, content);
1376:
1377: last = content;
1378: }
1379: }
1380:
1381: /* don't flush line for td and th */
1382: if (shouldIndent(node)
1383: || (((node.tag.model & Dict.CM_HTML) != 0
1384: || node.tag == tt.tagNoframes || ((node.tag.model & Dict.CM_HEAD) != 0 && !(node.tag == tt.tagTitle))) && this .configuration.HideEndTags == false)) {
1385: condFlushLine(fout,
1386: (this .configuration.IndentContent ? indent
1387: + this .configuration.spaces
1388: : indent));
1389:
1390: if (this .configuration.HideEndTags == false
1391: || !((node.tag.model & Dict.CM_OPT) != 0)) {
1392: printEndTag(fout, mode, indent, node);
1393: flushLine(fout, indent);
1394: }
1395: } else {
1396: if (this .configuration.HideEndTags == false
1397: || !((node.tag.model & Dict.CM_OPT) != 0))
1398: printEndTag(fout, mode, indent, node);
1399:
1400: flushLine(fout, indent);
1401: }
1402:
1403: if (this .configuration.IndentContent == false
1404: && node.next != null
1405: && this .configuration.HideEndTags == false
1406: && (node.tag.model & (Dict.CM_BLOCK
1407: | Dict.CM_LIST | Dict.CM_DEFLIST | Dict.CM_TABLE)) != 0) {
1408: flushLine(fout, indent);
1409: }
1410: }
1411: }
1412: }
1413:
1414: public void printXMLTree(Out fout, short mode, int indent,
1415: Lexer lexer, Node node) {
1416: TagTable tt = this .configuration.tt;
1417:
1418: if (node == null)
1419: return;
1420:
1421: if (node.type == Node.TextNode) {
1422: printText(fout, mode, indent, node.textarray, node.start,
1423: node.end);
1424: } else if (node.type == Node.CommentTag) {
1425: condFlushLine(fout, indent);
1426: printComment(fout, 0, node);
1427: condFlushLine(fout, 0);
1428: } else if (node.type == Node.RootNode) {
1429: Node content;
1430:
1431: for (content = node.content; content != null; content = content.next)
1432: printXMLTree(fout, mode, indent, lexer, content);
1433: } else if (node.type == Node.DocTypeTag)
1434: printDocType(fout, indent, node);
1435: else if (node.type == Node.ProcInsTag)
1436: printPI(fout, indent, node);
1437: else if (node.type == Node.SectionTag)
1438: printSection(fout, indent, node);
1439: else if (node.type == Node.AspTag)
1440: printAsp(fout, indent, node);
1441: else if (node.type == Node.JsteTag)
1442: printJste(fout, indent, node);
1443: else if (node.type == Node.PhpTag)
1444: printPhp(fout, indent, node);
1445: else if ((node.tag.model & Dict.CM_EMPTY) != 0
1446: || node.type == Node.StartEndTag) {
1447: condFlushLine(fout, indent);
1448: printTag(lexer, fout, mode, indent, node);
1449: flushLine(fout, indent);
1450:
1451: if (node.next != null)
1452: flushLine(fout, indent);
1453: } else /* some kind of container element */
1454: {
1455: Node content;
1456: boolean mixed = false;
1457: int cindent;
1458:
1459: for (content = node.content; content != null; content = content.next) {
1460: if (content.type == Node.TextNode) {
1461: mixed = true;
1462: break;
1463: }
1464: }
1465:
1466: condFlushLine(fout, indent);
1467:
1468: if (ParserImpl.XMLPreserveWhiteSpace(node, tt)) {
1469: indent = 0;
1470: cindent = 0;
1471: mixed = false;
1472: } else if (mixed)
1473: cindent = indent;
1474: else
1475: cindent = indent + this .configuration.spaces;
1476:
1477: printTag(lexer, fout, mode, indent, node);
1478:
1479: if (!mixed)
1480: flushLine(fout, indent);
1481:
1482: for (content = node.content; content != null; content = content.next)
1483: printXMLTree(fout, mode, cindent, lexer, content);
1484:
1485: if (!mixed)
1486: condFlushLine(fout, cindent);
1487: printEndTag(fout, mode, indent, node);
1488: condFlushLine(fout, indent);
1489:
1490: if (node.next != null)
1491: flushLine(fout, indent);
1492: }
1493: }
1494:
1495: /* split parse tree by h2 elements and output to separate files */
1496:
1497: /* counts number of h2 children belonging to node */
1498: public int countSlides(Node node) {
1499: int n = 1;
1500: TagTable tt = this .configuration.tt;
1501:
1502: for (node = node.content; node != null; node = node.next)
1503: if (node.tag == tt.tagH2)
1504: ++n;
1505:
1506: return n;
1507: }
1508:
1509: /*
1510: inserts a space gif called "dot.gif" to ensure
1511: that the slide is at least n pixels high
1512: */
1513: private void printVertSpacer(Out fout, int indent) {
1514: condFlushLine(fout, indent);
1515: printString(
1516: fout,
1517: indent,
1518: "<img width=\"0\" height=\"0\" hspace=\"1\" src=\"dot.gif\" vspace=\"%d\" align=\"left\">");
1519: condFlushLine(fout, indent);
1520: }
1521:
1522: private void printNavBar(Out fout, int indent) {
1523: String buf;
1524:
1525: condFlushLine(fout, indent);
1526: printString(fout, indent, "<center><small>");
1527:
1528: if (slide > 1) {
1529: buf = "<a href=\"slide"
1530: + (new Integer(slide - 1)).toString()
1531: + ".html\">previous</a> | ";
1532: printString(fout, indent, buf);
1533: condFlushLine(fout, indent);
1534:
1535: if (slide < count)
1536: printString(fout, indent,
1537: "<a href=\"slide1.html\">start</a> | ");
1538: else
1539: printString(fout, indent,
1540: "<a href=\"slide1.html\">start</a>");
1541:
1542: condFlushLine(fout, indent);
1543: }
1544:
1545: if (slide < count) {
1546: buf = "<a href=\"slide"
1547: + (new Integer(slide + 1)).toString()
1548: + ".html\">next</a>";
1549: printString(fout, indent, buf);
1550: }
1551:
1552: printString(fout, indent, "</small></center>");
1553: condFlushLine(fout, indent);
1554: }
1555:
1556: /*
1557: Called from printTree to print the content of a slide from
1558: the node slidecontent. On return slidecontent points to the
1559: node starting the next slide or null. The variables slide
1560: and count are used to customise the navigation bar.
1561: */
1562: public void printSlide(Out fout, short mode, int indent, Lexer lexer) {
1563: Node content, last;
1564: TagTable tt = this .configuration.tt;
1565:
1566: /* insert div for onclick handler */
1567: String s;
1568: s = "<div onclick=\"document.location='slide"
1569: + (new Integer(slide < count ? slide + 1 : 1))
1570: .toString() + ".html'\">";
1571: printString(fout, indent, s);
1572: condFlushLine(fout, indent);
1573:
1574: /* first print the h2 element and navbar */
1575: if (slidecontent.tag == tt.tagH2) {
1576: printNavBar(fout, indent);
1577:
1578: /* now print an hr after h2 */
1579:
1580: addC('<', linelen++);
1581:
1582: addC((int) Lexer.foldCase('h',
1583: this .configuration.UpperCaseTags,
1584: this .configuration.XmlTags), linelen++);
1585: addC((int) Lexer.foldCase('r',
1586: this .configuration.UpperCaseTags,
1587: this .configuration.XmlTags), linelen++);
1588:
1589: if (this .configuration.XmlOut == true)
1590: printString(fout, indent, " />");
1591: else
1592: addC('>', linelen++);
1593:
1594: if (this .configuration.IndentContent == true)
1595: condFlushLine(fout, indent);
1596:
1597: /* PrintVertSpacer(fout, indent); */
1598:
1599: /*condFlushLine(fout, indent); */
1600:
1601: /* print the h2 element */
1602: printTree(fout, mode,
1603: (this .configuration.IndentContent ? indent
1604: + this .configuration.spaces : indent),
1605: lexer, slidecontent);
1606:
1607: slidecontent = slidecontent.next;
1608: }
1609:
1610: /* now continue until we reach the next h2 */
1611:
1612: last = null;
1613: content = slidecontent;
1614:
1615: for (; content != null; content = content.next) {
1616: if (content.tag == tt.tagH2)
1617: break;
1618:
1619: /* kludge for naked text before block level tag */
1620: if (last != null && !this .configuration.IndentContent
1621: && last.type == Node.TextNode
1622: && content.tag != null
1623: && (content.tag.model & Dict.CM_BLOCK) != 0) {
1624: flushLine(fout, indent);
1625: flushLine(fout, indent);
1626: }
1627:
1628: printTree(fout, mode,
1629: (this .configuration.IndentContent ? indent
1630: + this .configuration.spaces : indent),
1631: lexer, content);
1632:
1633: last = content;
1634: }
1635:
1636: slidecontent = content;
1637:
1638: /* now print epilog */
1639:
1640: condFlushLine(fout, indent);
1641:
1642: printString(fout, indent, "<br clear=\"all\">");
1643: condFlushLine(fout, indent);
1644:
1645: addC('<', linelen++);
1646:
1647: addC((int) Lexer.foldCase('h',
1648: this .configuration.UpperCaseTags,
1649: this .configuration.XmlTags), linelen++);
1650: addC((int) Lexer.foldCase('r',
1651: this .configuration.UpperCaseTags,
1652: this .configuration.XmlTags), linelen++);
1653:
1654: if (this .configuration.XmlOut == true)
1655: printString(fout, indent, " />");
1656: else
1657: addC('>', linelen++);
1658:
1659: if (this .configuration.IndentContent == true)
1660: condFlushLine(fout, indent);
1661:
1662: printNavBar(fout, indent);
1663:
1664: /* end tag for div */
1665: printString(fout, indent, "</div>");
1666: condFlushLine(fout, indent);
1667: }
1668:
1669: /*
1670: Add meta element for page transition effect, this works on IE but not NS
1671: */
1672:
1673: public void addTransitionEffect(Lexer lexer, Node root,
1674: short effect, double duration) {
1675: Node head = root.findHEAD(lexer.configuration.tt);
1676: String transition;
1677:
1678: if (0 <= effect && effect <= 23)
1679: transition = "revealTrans(Duration="
1680: + (new Double(duration)).toString()
1681: + ",Transition=" + effect + ")";
1682: else
1683: transition = "blendTrans(Duration="
1684: + (new Double(duration)).toString() + ")";
1685:
1686: if (head != null) {
1687: Node meta = lexer.inferredTag("meta");
1688: meta.addAttribute("http-equiv", "Page-Enter");
1689: meta.addAttribute("content", transition);
1690: Node.insertNodeAtStart(head, meta);
1691: }
1692: }
1693:
1694: public void createSlides(Lexer lexer, Node root) {
1695: Node body;
1696: String buf;
1697: Out out = new OutImpl();
1698:
1699: body = root.findBody(lexer.configuration.tt);
1700: count = countSlides(body);
1701: slidecontent = body.content;
1702: addTransitionEffect(lexer, root, EFFECT_BLEND, 3.0);
1703:
1704: for (slide = 1; slide <= count; ++slide) {
1705: buf = "slide" + slide + ".html";
1706: out.state = StreamIn.FSM_ASCII;
1707: out.encoding = this .configuration.CharEncoding;
1708:
1709: try {
1710: out.out = new FileOutputStream(buf);
1711: printTree(out, (short) 0, 0, lexer, root);
1712: flushLine(out, 0);
1713: out.out.close();
1714: } catch (IOException e) {
1715: System.err.println(buf + e.toString());
1716: }
1717: }
1718:
1719: /*
1720: delete superfluous slides by deleting slideN.html
1721: for N = count+1, count+2, etc. until no such file
1722: is found.
1723: */
1724:
1725: for (;;) {
1726: buf = "slide" + slide + "html";
1727:
1728: if (!(new File(buf)).delete())
1729: break;
1730:
1731: ++slide;
1732: }
1733: }
1734:
1735: }
|