import org.eclipse.ui.internal.dialogs.ResourceSorter; replaced by import org.eclips...
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / tidy / PPrint.java
1 /*
2  * @(#)PPrint.java   1.11 2000/08/16
3  *
4  */
5
6 package net.sourceforge.phpdt.tidy;
7
8 /**
9  *
10  * Pretty print parse tree
11  *
12  * (c) 1998-2000 (W3C) MIT, INRIA, Keio University
13  * See Tidy.java for the copyright notice.
14  * Derived from <a href="http://www.w3.org/People/Raggett/tidy">
15  * HTML Tidy Release 4 Aug 2000</a>
16  *
17  * @author  Dave Raggett <dsr@w3.org>
18  * @author  Andy Quick <ac.quick@sympatico.ca> (translation to Java)
19  * @version 1.0, 1999/05/22
20  * @version 1.0.1, 1999/05/29
21  * @version 1.1, 1999/06/18 Java Bean
22  * @version 1.2, 1999/07/10 Tidy Release 7 Jul 1999
23  * @version 1.3, 1999/07/30 Tidy Release 26 Jul 1999
24  * @version 1.4, 1999/09/04 DOM support
25  * @version 1.5, 1999/10/23 Tidy Release 27 Sep 1999
26  * @version 1.6, 1999/11/01 Tidy Release 22 Oct 1999
27  * @version 1.7, 1999/12/06 Tidy Release 30 Nov 1999
28  * @version 1.8, 2000/01/22 Tidy Release 13 Jan 2000
29  * @version 1.9, 2000/06/03 Tidy Release 30 Apr 2000
30  * @version 1.10, 2000/07/22 Tidy Release 8 Jul 2000
31  * @version 1.11, 2000/08/16 Tidy Release 4 Aug 2000
32  */
33
34 /*
35   Block-level and unknown elements are printed on
36   new lines and their contents indented 2 spaces
37
38   Inline elements are printed inline.
39
40   Inline content is wrapped on spaces (except in
41   attribute values or preformatted text, after
42   start tags and before end tags
43 */
44
45 import java.io.File;
46 import java.io.FileOutputStream;
47 import java.io.IOException;
48
49 public class PPrint {
50
51     /* page transition effects */
52
53     public static final short EFFECT_BLEND               = -1;
54     public static final short EFFECT_BOX_IN              = 0;
55     public static final short EFFECT_BOX_OUT             = 1;
56     public static final short EFFECT_CIRCLE_IN           = 2;
57     public static final short EFFECT_CIRCLE_OUT          = 3;
58     public static final short EFFECT_WIPE_UP             = 4;
59     public static final short EFFECT_WIPE_DOWN           = 5;
60     public static final short EFFECT_WIPE_RIGHT          = 6;
61     public static final short EFFECT_WIPE_LEFT           = 7;
62     public static final short EFFECT_VERT_BLINDS         = 8;
63     public static final short EFFECT_HORZ_BLINDS         = 9;
64     public static final short EFFECT_CHK_ACROSS          = 10;
65     public static final short EFFECT_CHK_DOWN            = 11;
66     public static final short EFFECT_RND_DISSOLVE        = 12;
67     public static final short EFFECT_SPLIT_VIRT_IN       = 13;
68     public static final short EFFECT_SPLIT_VIRT_OUT      = 14;
69     public static final short EFFECT_SPLIT_HORZ_IN       = 15;
70     public static final short EFFECT_SPLIT_HORZ_OUT      = 16;
71     public static final short EFFECT_STRIPS_LEFT_DOWN    = 17;
72     public static final short EFFECT_STRIPS_LEFT_UP      = 18;
73     public static final short EFFECT_STRIPS_RIGHT_DOWN   = 19;
74     public static final short EFFECT_STRIPS_RIGHT_UP     = 20;
75     public static final short EFFECT_RND_BARS_HORZ       = 21;
76     public static final short EFFECT_RND_BARS_VERT       = 22;
77     public static final short EFFECT_RANDOM              = 23;
78
79     private static final short NORMAL        = 0;
80     private static final short PREFORMATTED  = 1;
81     private static final short COMMENT       = 2;
82     private static final short ATTRIBVALUE   = 4;
83     private static final short NOWRAP        = 8;
84     private static final short CDATA         = 16;
85
86     private int[] linebuf = null;
87     private int lbufsize = 0;
88     private int linelen = 0;
89     private int wraphere = 0;
90     private boolean inAttVal = false;
91     private boolean InString = false;
92
93     private int slide = 0;
94     private int count = 0;
95     private Node slidecontent = null;
96
97     private Configuration configuration;
98
99     public PPrint(Configuration configuration)
100     {
101         this.configuration = configuration;
102     }
103
104     /*
105       1010  A
106       1011  B
107       1100  C
108       1101  D
109       1110  E
110       1111  F
111     */
112
113     /* return one less that the number of bytes used by UTF-8 char */
114     /* str points to 1st byte, *ch initialized to 1st byte */
115     public static int getUTF8(byte[] str, int start, MutableInteger ch)
116     {
117         int c, n, i, bytes;
118
119         c = ((int)str[start]) & 0xFF;  // Convert to unsigned.
120
121         if ((c & 0xE0) == 0xC0)  /* 110X XXXX  two bytes */
122         {
123             n = c & 31;
124             bytes = 2;
125         }
126         else if ((c & 0xF0) == 0xE0)  /* 1110 XXXX  three bytes */
127         {
128             n = c & 15;
129             bytes = 3;
130         }
131         else if ((c & 0xF8) == 0xF0)  /* 1111 0XXX  four bytes */
132         {
133             n = c & 7;
134             bytes = 4;
135         }
136         else if ((c & 0xFC) == 0xF8)  /* 1111 10XX  five bytes */
137         {
138             n = c & 3;
139             bytes = 5;
140         }
141         else if ((c & 0xFE) == 0xFC)       /* 1111 110X  six bytes */
142
143         {
144             n = c & 1;
145             bytes = 6;
146         }
147         else  /* 0XXX XXXX one byte */
148         {
149             ch.value = c;
150             return 0;
151         }
152
153         /* successor bytes should have the form 10XX XXXX */
154         for (i = 1; i < bytes; ++i)
155         {
156             c = ((int)str[start + i])  & 0xFF;  // Convert to unsigned.
157             n = (n << 6) | (c & 0x3F);
158         }
159
160         ch.value = n;
161         return bytes - 1;
162     }
163
164     /* store char c as UTF-8 encoded byte stream */
165     public static int putUTF8(byte[] buf, int start, int c)
166     {
167         if (c < 128)
168             buf[start++] = (byte)c;
169         else if (c <= 0x7FF)
170         {
171             buf[start++] = (byte)(0xC0 | (c >> 6));
172             buf[start++] = (byte)(0x80 | (c & 0x3F));
173         }
174         else if (c <= 0xFFFF)
175         {
176             buf[start++] =  (byte)(0xE0 | (c >> 12));
177             buf[start++] =  (byte)(0x80 | ((c >> 6) & 0x3F));
178             buf[start++] =  (byte)(0x80 | (c & 0x3F));
179         }
180         else if (c <= 0x1FFFFF)
181         {
182             buf[start++] =  (byte)(0xF0 | (c >> 18));
183             buf[start++] =  (byte)(0x80 | ((c >> 12) & 0x3F));
184             buf[start++] =  (byte)(0x80 | ((c >> 6) & 0x3F));
185             buf[start++] =  (byte)(0x80 | (c & 0x3F));
186         }
187         else
188         {
189             buf[start++] =  (byte)(0xF8 | (c >> 24));
190             buf[start++] =  (byte)(0x80 | ((c >> 18) & 0x3F));
191             buf[start++] =  (byte)(0x80 | ((c >> 12) & 0x3F));
192             buf[start++] =  (byte)(0x80 | ((c >> 6) & 0x3F));
193             buf[start++] =  (byte)(0x80 | (c & 0x3F));
194         }
195
196         return start;
197     }
198
199     private void addC(int c, int index)
200     {
201         if (index + 1 >= lbufsize)
202         {
203             while (index + 1 >= lbufsize)
204             {
205                 if (lbufsize == 0)
206                     lbufsize = 256;
207                 else
208                     lbufsize = lbufsize * 2;
209             }
210
211            int[] temp = new int[ lbufsize ];
212            if (linebuf != null)
213                System.arraycopy(linebuf, 0, temp, 0, index);
214            linebuf = temp;
215         }
216
217         linebuf[index] = c;
218     }
219
220     private void wrapLine(Out fout, int indent)
221     {
222         int i, p, q;
223
224         if (wraphere == 0)
225             return;
226
227         for (i = 0; i < indent; ++i)
228             fout.outc((int)' ');
229
230         for (i = 0; i < wraphere; ++i)
231             fout.outc(linebuf[i]);
232
233         if (InString)
234         {
235             fout.outc((int)' ');
236             fout.outc((int)'\\');
237         }
238
239         fout.newline();
240
241         if (linelen > wraphere)
242         {
243             p = 0;
244
245             if (linebuf[wraphere] == ' ')
246                 ++wraphere;
247
248             q = wraphere;
249             addC('\0', linelen);
250
251             while (true)
252             {
253                 linebuf[p] = linebuf[q];
254                 if (linebuf[q] == 0) break;
255                 p++;
256                 q++;
257             }
258             linelen -= wraphere;
259         }
260         else
261             linelen = 0;
262
263         wraphere = 0;
264     }
265
266     private void wrapAttrVal(Out fout, int indent, boolean inString)
267     {
268         int i, p, q;
269
270         for (i = 0; i < indent; ++i)
271             fout.outc((int)' ');
272
273         for (i = 0; i < wraphere; ++i)
274             fout.outc(linebuf[i]);
275
276         fout.outc((int)' ');
277
278         if (inString)
279             fout.outc((int)'\\');
280
281         fout.newline();
282
283         if (linelen > wraphere)
284         {
285             p = 0;
286
287             if (linebuf[wraphere] == ' ')
288                 ++wraphere;
289
290             q = wraphere;
291             addC('\0', linelen);
292
293             while (true)
294             {
295                 linebuf[p] = linebuf[q];
296                 if (linebuf[q] == 0) break;
297                 p++;
298                 q++;
299             }
300             linelen -= wraphere;
301         }
302         else
303             linelen = 0;
304
305         wraphere = 0;
306     }
307
308     public void flushLine(Out fout, int indent)
309     {
310         int i;
311
312         if (linelen > 0)
313         {
314             if (indent + linelen >= this.configuration.wraplen)
315                 wrapLine(fout, indent);
316
317             if (!inAttVal || this.configuration.IndentAttributes)
318             {
319                 for (i = 0; i < indent; ++i)
320                     fout.outc((int)' ');
321             }
322
323             for (i = 0; i < linelen; ++i)
324                 fout.outc(linebuf[i]);
325         }
326
327         fout.newline();
328         linelen = 0;
329         wraphere = 0;
330         inAttVal = false;
331     }
332
333     public void condFlushLine(Out fout, int indent)
334     {
335         int i;
336
337         if (linelen > 0)
338         {
339             if (indent + linelen >= this.configuration.wraplen)
340                 wrapLine(fout, indent);
341
342             if (!inAttVal || this.configuration.IndentAttributes)
343             {
344                 for (i = 0; i < indent; ++i)
345                     fout.outc((int)' ');
346             }
347
348             for (i = 0; i < linelen; ++i)
349                 fout.outc(linebuf[i]);
350
351             fout.newline();
352             linelen = 0;
353             wraphere = 0;
354             inAttVal = false;
355         }
356     }
357
358     private void printChar(int c, short mode)
359     {
360         String entity;
361
362         if (c == ' ' && !((mode & (PREFORMATTED | COMMENT | ATTRIBVALUE)) != 0))
363         {
364             /* coerce a space character to a non-breaking space */
365             if ((mode & NOWRAP) != 0)
366             {
367                 /* by default XML doesn't define &nbsp; */
368                 if (this.configuration.NumEntities || this.configuration.XmlTags)
369                 {
370                     addC('&', linelen++);
371                     addC('#', linelen++);
372                     addC('1', linelen++);
373                     addC('6', linelen++);
374                     addC('0', linelen++);
375                     addC(';', linelen++);
376                 }
377                 else /* otherwise use named entity */
378                 {
379                     addC('&', linelen++);
380                     addC('n', linelen++);
381                     addC('b', linelen++);
382                     addC('s', linelen++);
383                     addC('p', linelen++);
384                     addC(';', linelen++);
385                 }
386                 return;
387             }
388             else
389                 wraphere = linelen;
390         }
391
392         /* comment characters are passed raw */
393         if ((mode & COMMENT) != 0)
394         {
395             addC(c, linelen++);
396             return;
397         }
398
399         /* except in CDATA map < to &lt; etc. */
400         if (! ((mode & CDATA) != 0) )
401         {
402             if (c == '<')
403             {
404                 addC('&', linelen++);
405                 addC('l', linelen++);
406                 addC('t', linelen++);
407                 addC(';', linelen++);
408                 return;
409             }
410             
411             if (c == '>')
412             {
413                 addC('&', linelen++);
414                 addC('g', linelen++);
415                 addC('t', linelen++);
416                 addC(';', linelen++);
417                 return;
418             }
419
420             /*
421               naked '&' chars can be left alone or
422               quoted as &amp; The latter is required
423               for XML where naked '&' are illegal.
424             */
425             if (c == '&' && this.configuration.QuoteAmpersand)
426             {
427                 addC('&', linelen++);
428                 addC('a', linelen++);
429                 addC('m', linelen++);
430                 addC('p', linelen++);
431                 addC(';', linelen++);
432                 return;
433             }
434
435             if (c == '"' && this.configuration.QuoteMarks)
436             {
437                 addC('&', linelen++);
438                 addC('q', linelen++);
439                 addC('u', linelen++);
440                 addC('o', linelen++);
441                 addC('t', linelen++);
442                 addC(';', linelen++);
443                 return;
444             }
445
446             if (c == '\'' && this.configuration.QuoteMarks)
447             {
448                 addC('&', linelen++);
449                 addC('#', linelen++);
450                 addC('3', linelen++);
451                 addC('9', linelen++);
452                 addC(';', linelen++);
453                 return;
454             }
455
456             if (c == 160 && this.configuration.CharEncoding != Configuration.RAW)
457             {
458                 if (this.configuration.QuoteNbsp)
459                 {
460                     addC('&', linelen++);
461
462                     if (this.configuration.NumEntities)
463                     {
464                         addC('#', linelen++);
465                         addC('1', linelen++);
466                         addC('6', linelen++);
467                         addC('0', linelen++);
468                     }
469                     else
470                     {
471                         addC('n', linelen++);
472                         addC('b', linelen++);
473                         addC('s', linelen++);
474                         addC('p', linelen++);
475                     }
476
477                     addC(';', linelen++);
478                 }
479                 else
480                     addC(c, linelen++);
481
482                 return;
483             }
484         }
485
486         /* otherwise ISO 2022 characters are passed raw */
487         if (this.configuration.CharEncoding == Configuration.ISO2022 ||
488             this.configuration.CharEncoding == Configuration.RAW)
489         {
490             addC(c, linelen++);
491             return;
492         }
493
494         /* if preformatted text, map &nbsp; to space */
495         if (c == 160 && ((mode & PREFORMATTED) != 0))
496         {
497             addC(' ', linelen++);
498             return;
499         }
500
501         /*
502          Filters from Word and PowerPoint often use smart
503          quotes resulting in character codes between 128
504          and 159. Unfortunately, the corresponding HTML 4.0
505          entities for these are not widely supported. The
506          following converts dashes and quotation marks to
507          the nearest ASCII equivalent. My thanks to
508          Andrzej Novosiolov for his help with this code.
509         */
510
511         if (this.configuration.MakeClean)
512         {
513             if (c >= 0x2013 && c <= 0x201E)
514             {
515                 switch (c) {
516                 case 0x2013:
517                 case 0x2014:
518                   c = '-';
519                   break;
520                 case 0x2018:
521                 case 0x2019:
522                 case 0x201A:
523                   c = '\'';
524                   break;
525                 case 0x201C:
526                 case 0x201D:
527                 case 0x201E:
528                   c = '"';
529                   break;
530                 }
531             }
532         }
533
534         /* don't map latin-1 chars to entities */
535         if (this.configuration.CharEncoding == Configuration.LATIN1)
536         {
537             if (c > 255)  /* multi byte chars */
538             {
539                 if (!this.configuration.NumEntities)
540                 {
541                     entity = EntityTable.getDefaultEntityTable().entityName((short)c);
542                     if (entity != null)
543                         entity = "&" + entity + ";";
544                     else
545                         entity = "&#" + c + ";";
546                 }
547                 else
548                     entity = "&#" + c + ";";
549
550                 for (int i = 0; i < entity.length(); i++)
551                     addC((int)entity.charAt(i), linelen++);
552
553                 return;
554             }
555
556             if (c > 126 && c < 160)
557             {
558                 entity = "&#" + c + ";";
559
560                 for (int i = 0; i < entity.length(); i++)
561                     addC((int)entity.charAt(i), linelen++);
562
563                 return;
564             }
565
566             addC(c, linelen++);
567             return;
568         }
569
570         /* don't map utf8 chars to entities */
571         if (this.configuration.CharEncoding == Configuration.UTF8)
572         {
573             addC(c, linelen++);
574             return;
575         }
576
577         /* use numeric entities only  for XML */
578         if (this.configuration.XmlTags)
579         {
580             /* if ASCII use numeric entities for chars > 127 */
581             if (c > 127 && this.configuration.CharEncoding == Configuration.ASCII)
582             {
583                 entity = "&#" + c + ";";
584
585                 for (int i = 0; i < entity.length(); i++)
586                     addC((int)entity.charAt(i), linelen++);
587
588                 return;
589             }
590
591             /* otherwise output char raw */
592             addC(c, linelen++);
593             return;
594         }
595
596         /* default treatment for ASCII */
597         if (c > 126 || (c < ' ' && c != '\t'))
598         {
599             if (!this.configuration.NumEntities)
600             {
601                 entity = EntityTable.getDefaultEntityTable().entityName((short)c);
602                 if (entity != null)
603                     entity = "&" + entity + ";";
604                 else
605                     entity = "&#" + c + ";";
606             }
607             else
608                 entity = "&#" + c + ";";
609
610             for (int i = 0; i < entity.length(); i++)
611                 addC((int)entity.charAt(i), linelen++);
612
613             return;
614         }
615
616         addC(c, linelen++);
617     }
618
619     /* 
620       The line buffer is uint not char so we can
621       hold Unicode values unencoded. The translation
622       to UTF-8 is deferred to the outc routine called
623       to flush the line buffer.
624     */
625     private void printText(Out fout, short mode, int indent,
626                            byte[] textarray, int start, int end)
627     {
628         int i, c;
629         MutableInteger ci = new MutableInteger();
630
631         for (i = start; i < end; ++i)
632         {
633             if (indent + linelen >= this.configuration.wraplen)
634                 wrapLine(fout, indent);
635
636             c = ((int)textarray[i]) & 0xFF;  // Convert to unsigned.
637
638             /* look for UTF-8 multibyte character */
639             if (c > 0x7F)
640             {
641                  i += getUTF8(textarray, i, ci);
642                  c = ci.value;
643             }
644
645             if (c == '\n')
646             {
647                 flushLine(fout, indent);
648                 continue;
649             }
650
651             printChar(c, mode);
652         }
653     }
654
655     private void printString(Out fout, int indent, String str)
656     {
657         for (int i = 0; i < str.length(); i++ )
658             addC((int)str.charAt(i), linelen++);
659     }
660
661     private void printAttrValue(Out fout, int indent, String value, int delim, boolean wrappable)
662     {
663         int c;
664         MutableInteger ci = new MutableInteger();
665         boolean wasinstring = false;
666         byte[] valueChars = null;
667         int i;
668         short mode = (wrappable ? (short)(NORMAL | ATTRIBVALUE) :
669                                   (short)(PREFORMATTED | ATTRIBVALUE));
670
671         if (value != null)
672         {
673             valueChars = Lexer.getBytes(value);
674         }
675
676         /* look for ASP, Tango or PHP instructions for computed attribute value */
677         if (valueChars != null && valueChars.length >= 5 && valueChars[0] == '<')
678         {
679             if (valueChars[1] == '%' || valueChars[1] == '@'||
680                 (new String(valueChars, 0, 5)).equals("<?php"))
681                 mode |= CDATA;
682         }
683
684         if (delim == 0)
685             delim = '"';
686
687         addC('=', linelen++);
688
689         /* don't wrap after "=" for xml documents */
690         if (!this.configuration.XmlOut) {
691
692             if (indent + linelen < this.configuration.wraplen)
693                 wraphere = linelen;
694
695             if (indent + linelen >= this.configuration.wraplen)
696                 wrapLine(fout, indent);
697
698             if (indent + linelen < this.configuration.wraplen)
699                 wraphere = linelen;
700             else
701                 condFlushLine(fout, indent);
702         }
703
704         addC(delim, linelen++);
705
706         if (value != null)
707         {
708             InString = false;
709
710             i = 0;
711             while (i < valueChars.length)
712             {
713                 c = ((int)valueChars[i]) & 0xFF;  // Convert to unsigned.
714
715                 if (wrappable && c == ' ' && indent + linelen < this.configuration.wraplen)
716                 {
717                     wraphere = linelen;
718                     wasinstring = InString;
719                 }
720
721                 if (wrappable && wraphere > 0 && indent + linelen >= this.configuration.wraplen)
722                     wrapAttrVal(fout, indent, wasinstring);
723
724                 if (c == delim)
725                 {
726                     String entity;
727
728                     entity = (c == '"' ? "&quot;" : "&#39;");
729
730                     for (int j = 0; j < entity.length(); j++ )
731                         addC(entity.charAt(j), linelen++);
732
733                     ++i;
734                     continue;
735                 }
736                 else if (c == '"')
737                 {
738                     if (this.configuration.QuoteMarks)
739                     {
740                         addC('&', linelen++);
741                         addC('q', linelen++);
742                         addC('u', linelen++);
743                         addC('o', linelen++);
744                         addC('t', linelen++);
745                         addC(';', linelen++);
746                     }
747                     else
748                         addC('"', linelen++);
749
750                     if (delim == '\'')
751                         InString = !InString;
752
753                     ++i;
754                     continue;
755                 }
756                 else if (c == '\'')
757                 {
758                     if (this.configuration.QuoteMarks)
759                     {
760                         addC('&', linelen++);
761                         addC('#', linelen++);
762                         addC('3', linelen++);
763                         addC('9', linelen++);
764                         addC(';', linelen++);
765                     }
766                     else
767                         addC('\'', linelen++);
768
769                     if (delim == '"')
770                         InString = !InString;
771
772                     ++i;
773                     continue;
774                 }
775
776                 /* look for UTF-8 multibyte character */
777                 if (c > 0x7F)
778                 {
779                      i += getUTF8(valueChars, i, ci);
780                      c = ci.value;
781                 }
782
783                 ++i;
784
785                 if (c == '\n')
786                 {
787                     flushLine(fout, indent);
788                     continue;
789                 }
790
791                 printChar(c, mode);
792             }
793         }
794
795         InString = false;
796         addC(delim, linelen++);
797     }
798
799     private void printAttribute(Out fout, int indent, Node node, AttVal attr)
800     {
801         String name;
802         boolean wrappable = false;
803
804         if (this.configuration.IndentAttributes)
805         {
806             flushLine(fout, indent);
807             indent += this.configuration.spaces;
808         }
809
810         name = attr.attribute;
811
812         if (indent + linelen >= this.configuration.wraplen)
813             wrapLine(fout, indent);
814
815         if (!this.configuration.XmlTags && !this.configuration.XmlOut && attr.dict != null)
816         {
817             if (AttributeTable.getDefaultAttributeTable().isScript(name))
818                 wrappable = this.configuration.WrapScriptlets;
819             else if (!attr.dict.nowrap && this.configuration.WrapAttVals)
820                 wrappable = true;
821         }
822
823         if (indent + linelen < this.configuration.wraplen)
824         {
825             wraphere = linelen;
826             addC(' ', linelen++);
827         }
828         else
829         {
830             condFlushLine(fout, indent);
831             addC(' ', linelen++);
832         }
833
834         for (int i = 0; i < name.length(); i++ )
835             addC((int)Lexer.foldCase(name.charAt(i),
836                                      this.configuration.UpperCaseAttrs,
837                                      this.configuration.XmlTags),
838                  linelen++);
839
840         if (indent + linelen >= this.configuration.wraplen)
841             wrapLine(fout, indent);
842  
843         if (attr.value == null)
844         {
845             if (this.configuration.XmlTags || this.configuration.XmlOut)
846                 printAttrValue(fout, indent, attr.attribute, attr.delim, true);
847             else if (!attr.isBoolAttribute() && !Node.isNewNode(node))
848                 printAttrValue(fout, indent, "", attr.delim, true);
849             else if (indent + linelen < this.configuration.wraplen)
850                 wraphere = linelen;
851
852         }
853         else
854             printAttrValue(fout, indent, attr.value, attr.delim, wrappable);
855     }
856
857     private void printAttrs(Out fout, int indent,
858                             Node node, AttVal attr)
859     {
860         if (attr != null)
861         {
862             if (attr.next != null)
863                 printAttrs(fout, indent, node, attr.next);
864
865             if (attr.attribute != null)
866                 printAttribute(fout, indent, node, attr);
867             else if (attr.asp != null)
868             {
869                 addC(' ', linelen++);
870                 printAsp(fout, indent, attr.asp);
871             }
872             else if (attr.php != null)
873             {
874                 addC(' ', linelen++);
875                 printPhp(fout, indent, attr.php);
876             }
877         }
878
879         /* add xml:space attribute to pre and other elements */
880         if (configuration.XmlOut &&
881                 configuration.XmlSpace &&
882                 ParserImpl.XMLPreserveWhiteSpace(node, configuration.tt) &&
883                 node.getAttrByName("xml:space") == null)
884             printString(fout, indent, " xml:space=\"preserve\"");
885     }
886
887     /*
888      Line can be wrapped immediately after inline start tag provided
889      if follows a text node ending in a space, or it parent is an
890      inline element that that rule applies to. This behaviour was
891      reverse engineered from Netscape 3.0
892     */
893     private static boolean afterSpace(Node node)
894     {
895         Node prev;
896         int c;
897
898         if (node == null || node.tag == null || !((node.tag.model & Dict.CM_INLINE) != 0))
899             return true;
900
901         prev = node.prev;
902
903         if (prev != null)
904         {
905             if (prev.type == Node.TextNode && prev.end > prev.start)
906             {
907                 c = ((int)prev.textarray[prev.end - 1]) & 0xFF;  // Convert to unsigned.
908
909                 if (c == 160 || c == ' ' || c == '\n')
910                     return true;
911             }
912
913             return false;
914         }
915
916         return afterSpace(node.parent);
917     }
918
919     private void printTag(Lexer lexer, Out fout, short mode, int indent, Node node)
920     {
921         char c;
922         String p;
923         TagTable tt = this.configuration.tt;
924
925         addC('<', linelen++);
926
927         if (node.type == Node.EndTag)
928             addC('/', linelen++);
929
930         p = node.element;
931         for (int i = 0; i < p.length(); i++ )
932             addC((int)Lexer.foldCase(p.charAt(i),
933                                      this.configuration.UpperCaseTags,
934                                      this.configuration.XmlTags),
935                  linelen++);
936
937         printAttrs(fout, indent, node, node.attributes);
938
939         if ((this.configuration.XmlOut || lexer != null && lexer.isvoyager) &&
940                 (node.type == Node.StartEndTag || (node.tag.model & Dict.CM_EMPTY) != 0))
941         {
942             addC(' ', linelen++);   /* compatibility hack */
943             addC('/', linelen++);
944         }
945
946         addC('>', linelen++);;
947
948         if (node.type != Node.StartEndTag && !((mode & PREFORMATTED) != 0))
949         {
950             if (indent + linelen >= this.configuration.wraplen)
951                 wrapLine(fout, indent);
952
953             if (indent + linelen < this.configuration.wraplen)
954             {
955                 /*
956                  wrap after start tag if is <br/> or if it's not
957                  inline or it is an empty tag followed by </a>
958                 */
959                 if (afterSpace(node))
960                 {
961                     if (!((mode & NOWRAP) != 0) &&
962                         (!((node.tag.model & Dict.CM_INLINE) != 0) ||
963                           (node.tag == tt.tagBr) ||
964                           (((node.tag.model & Dict.CM_EMPTY) != 0) && 
965                           node.next == null &&
966                           node.parent.tag == tt.tagA)))
967                     {
968                         wraphere = linelen;
969                     }
970                 }
971             }
972             else
973                 condFlushLine(fout, indent);
974         }
975     }
976
977     private void printEndTag(Out fout, short mode, int indent, Node node)
978     {
979         char c;
980         String p;
981
982        /*
983          Netscape ignores SGML standard by not ignoring a
984          line break before </A> or </U> etc. To avoid rendering 
985          this as an underlined space, I disable line wrapping
986          before inline end tags by the #if 0 ... #endif
987        */
988 if (false) {
989         if (indent + linelen < this.configuration.wraplen && !((mode & NOWRAP) != 0))
990             wraphere = linelen;
991 }
992
993         addC('<', linelen++);
994         addC('/', linelen++);
995
996         p = node.element;
997         for (int i = 0; i < p.length(); i++ )
998             addC((int)Lexer.foldCase(p.charAt(i),
999                                      this.configuration.UpperCaseTags,
1000                                      this.configuration.XmlTags),
1001                  linelen++);
1002
1003         addC('>', linelen++);
1004     }
1005
1006     private void printComment(Out fout, int indent, Node node)
1007     {
1008         if (indent + linelen < this.configuration.wraplen)
1009             wraphere = linelen;
1010
1011         addC('<', linelen++);
1012         addC('!', linelen++);
1013         addC('-', linelen++);
1014         addC('-', linelen++);
1015 if (false) {
1016         if (linelen < this.configuration.wraplen)
1017             wraphere = linelen;
1018 }
1019         printText(fout, COMMENT, indent,
1020                         node.textarray, node.start, node.end);
1021 if (false) {
1022         if (indent + linelen < this.configuration.wraplen)
1023             wraphere = linelen;
1024 }
1025         // See Lexer.java: AQ 8Jul2000
1026         addC('-', linelen++);
1027         addC('-', linelen++);
1028         addC('>', linelen++);
1029
1030         if (node.linebreak)
1031             flushLine(fout, indent);
1032     }
1033
1034     private void printDocType(Out fout, int indent, Node node)
1035     {
1036         boolean q = this.configuration.QuoteMarks;
1037
1038         this.configuration.QuoteMarks = false;
1039
1040         if (indent + linelen < this.configuration.wraplen)
1041             wraphere = linelen;
1042
1043         condFlushLine(fout, indent);
1044
1045         addC('<', linelen++);
1046         addC('!', linelen++);
1047         addC('D', linelen++);
1048         addC('O', linelen++);
1049         addC('C', linelen++);
1050         addC('T', linelen++);
1051         addC('Y', linelen++);
1052         addC('P', linelen++);
1053         addC('E', linelen++);
1054         addC(' ', linelen++);
1055
1056         if (indent + linelen < this.configuration.wraplen)
1057             wraphere = linelen;
1058
1059         printText(fout, (short)0, indent,
1060                         node.textarray, node.start, node.end);
1061
1062         if (linelen < this.configuration.wraplen)
1063             wraphere = linelen;
1064
1065         addC('>', linelen++);
1066         this.configuration.QuoteMarks = q;
1067         condFlushLine(fout, indent);
1068     }
1069
1070     private void printPI(Out fout, int indent, Node node)
1071     {
1072         if (indent + linelen < this.configuration.wraplen)
1073             wraphere = linelen;
1074
1075         addC('<', linelen++);
1076         addC('?', linelen++);
1077
1078         /* set CDATA to pass < and > unescaped */
1079         printText(fout, CDATA, indent,
1080                     node.textarray, node.start, node.end);
1081
1082         if (node.textarray[node.end - 1] != (byte)'?')
1083             addC('?', linelen++);
1084
1085         addC('>', linelen++);
1086         condFlushLine(fout, indent);
1087     }
1088
1089     /* note ASP and JSTE share <% ... %> syntax */
1090     private void printAsp(Out fout, int indent, Node node)
1091     {
1092         int savewraplen = this.configuration.wraplen;
1093
1094         /* disable wrapping if so requested */
1095
1096         if (!this.configuration.WrapAsp || !this.configuration.WrapJste)
1097             this.configuration.wraplen = 0xFFFFFF;  /* a very large number */
1098 if (false) { //#if 0
1099         if (indent + linelen < this.configuration.wraplen)
1100             wraphere = linelen;
1101 } //#endif
1102
1103         addC('<', linelen++);
1104         addC('%', linelen++);
1105
1106         printText(fout, (this.configuration.WrapAsp ? CDATA : COMMENT), indent,
1107                     node.textarray, node.start, node.end);
1108
1109         addC('%', linelen++);
1110         addC('>', linelen++);
1111         /* condFlushLine(fout, indent); */
1112         this.configuration.wraplen = savewraplen;
1113     }
1114
1115     /* JSTE also supports <# ... #> syntax */
1116     private void printJste(Out fout, int indent, Node node)
1117     {
1118         int savewraplen = this.configuration.wraplen;
1119
1120         /* disable wrapping if so requested */
1121
1122         if (!this.configuration.WrapJste)
1123             this.configuration.wraplen = 0xFFFFFF;  /* a very large number */
1124
1125         addC('<', linelen++);
1126         addC('#', linelen++);
1127
1128         printText(fout, (this.configuration.WrapJste ? CDATA : COMMENT), indent,
1129                     node.textarray, node.start, node.end);
1130
1131         addC('#', linelen++);
1132         addC('>', linelen++);
1133         /* condFlushLine(fout, indent); */
1134         this.configuration.wraplen = savewraplen;
1135     }
1136
1137     /* PHP is based on XML processing instructions */
1138     private void printPhp(Out fout, int indent, Node node)
1139     {
1140         int savewraplen = this.configuration.wraplen;
1141
1142         /* disable wrapping if so requested */
1143
1144         if (!this.configuration.WrapPhp)
1145             this.configuration.wraplen = 0xFFFFFF;  /* a very large number */
1146
1147 if (false) { //#if 0
1148         if (indent + linelen < this.configuration.wraplen)
1149             wraphere = linelen;
1150 } //#endif
1151         addC('<', linelen++);
1152         addC('?', linelen++);
1153
1154         printText(fout, (this.configuration.WrapPhp ? CDATA : COMMENT), indent,
1155                         node.textarray, node.start, node.end);
1156
1157         addC('?', linelen++);
1158         addC('>', linelen++);
1159         /* PCondFlushLine(fout, indent); */
1160         this.configuration.wraplen = savewraplen;
1161     }
1162
1163     private void printCDATA(Out fout, int indent, Node node)
1164     {
1165         int savewraplen = this.configuration.wraplen;
1166
1167         condFlushLine(fout, indent);
1168
1169         /* disable wrapping */
1170
1171         this.configuration.wraplen = 0xFFFFFF;  /* a very large number */
1172
1173         addC('<', linelen++);
1174         addC('!', linelen++);
1175         addC('[', linelen++);
1176         addC('C', linelen++);
1177         addC('D', linelen++);
1178         addC('A', linelen++);
1179         addC('T', linelen++);
1180         addC('A', linelen++);
1181         addC('[', linelen++);
1182
1183         printText(fout, COMMENT, indent,
1184                         node.textarray, node.start, node.end);
1185
1186         addC(']', linelen++);
1187         addC(']', linelen++);
1188         addC('>', linelen++);
1189         condFlushLine(fout, indent);
1190         this.configuration.wraplen = savewraplen;
1191     }
1192
1193     private void printSection(Out fout, int indent, Node node)
1194     {
1195         int savewraplen = this.configuration.wraplen;
1196
1197         /* disable wrapping if so requested */
1198
1199         if (!this.configuration.WrapSection)
1200             this.configuration.wraplen = 0xFFFFFF;  /* a very large number */
1201
1202 if (false) { //#if 0
1203         if (indent + linelen < this.configuration.wraplen)
1204             wraphere = linelen;
1205 } //#endif
1206         addC('<', linelen++);
1207         addC('!', linelen++);
1208         addC('[', linelen++);
1209
1210         printText(fout, (this.configuration.WrapSection ? CDATA : COMMENT), indent,
1211                         node.textarray, node.start, node.end);
1212
1213         addC(']', linelen++);
1214         addC('>', linelen++);
1215         /* PCondFlushLine(fout, indent); */
1216         this.configuration.wraplen = savewraplen;
1217     }
1218
1219     private boolean shouldIndent(Node node)
1220     {
1221         TagTable tt = this.configuration.tt;
1222
1223         if (!this.configuration.IndentContent)
1224             return false;
1225
1226         if (this.configuration.SmartIndent)
1227         {
1228             if (node.content != null && ((node.tag.model & Dict.CM_NO_INDENT) != 0))
1229             {
1230                 for (node = node.content; node != null; node = node.next)
1231                     if (node.tag != null && (node.tag.model & Dict.CM_BLOCK) != 0)
1232                         return true;
1233
1234                 return false;
1235             }
1236
1237             if ((node.tag.model & Dict.CM_HEADING) != 0)
1238                 return false;
1239
1240             if (node.tag == tt.tagP)
1241                 return false;
1242
1243             if (node.tag == tt.tagTitle)
1244                 return false;
1245         }
1246
1247         if ((node.tag.model & (Dict.CM_FIELD | Dict.CM_OBJECT)) != 0)
1248             return true;
1249
1250         if (node.tag == tt.tagMap)
1251             return true;
1252
1253         return !((node.tag.model & Dict.CM_INLINE) != 0);
1254     }
1255
1256     public void printTree(Out fout, short mode, int indent,
1257                           Lexer lexer, Node node)
1258     {
1259         Node content, last;
1260         TagTable tt = this.configuration.tt;
1261
1262         if (node == null)
1263             return;
1264
1265         if (node.type == Node.TextNode)
1266             printText(fout, mode, indent,
1267                         node.textarray, node.start, node.end);
1268         else if (node.type == Node.CommentTag)
1269         {
1270             printComment(fout, indent, node);
1271         }
1272         else if (node.type == Node.RootNode)
1273         {
1274             for (content = node.content;
1275                     content != null;
1276                     content = content.next)
1277                printTree(fout, mode, indent, lexer, content);
1278         }
1279         else if (node.type == Node.DocTypeTag)
1280             printDocType(fout, indent, node);
1281         else if (node.type == Node.ProcInsTag)
1282             printPI(fout, indent, node);
1283         else if (node.type == Node.CDATATag)
1284             printCDATA(fout, indent, node);
1285         else if (node.type == Node.SectionTag)
1286             printSection(fout, indent, node);
1287         else if (node.type == Node.AspTag)
1288             printAsp(fout, indent, node);
1289         else if (node.type == Node.JsteTag)
1290             printJste(fout, indent, node);
1291         else if (node.type == Node.PhpTag)
1292             printPhp(fout, indent, node);
1293         else if ((node.tag.model & Dict.CM_EMPTY) != 0 || node.type == Node.StartEndTag)
1294         {
1295             if (!((node.tag.model & Dict.CM_INLINE) != 0))
1296                 condFlushLine(fout, indent);
1297
1298             if (node.tag == tt.tagBr && node.prev != null &&
1299                 node.prev.tag != tt.tagBr && this.configuration.BreakBeforeBR)
1300                 flushLine(fout, indent);
1301
1302             if (this.configuration.MakeClean && node.tag == tt.tagWbr)
1303                 printString(fout, indent, " ");
1304             else
1305                 printTag(lexer, fout, mode, indent, node);
1306
1307             if (node.tag == tt.tagParam || node.tag == tt.tagArea)
1308                 condFlushLine(fout, indent);
1309             else if (node.tag == tt.tagBr || node.tag == tt.tagHr)
1310                 flushLine(fout, indent);
1311         }
1312         else /* some kind of container element */
1313         {
1314             if (node.tag != null && node.tag.parser == ParserImpl.getParsePre())
1315             {
1316                 condFlushLine(fout, indent);
1317
1318                 indent = 0;
1319                 condFlushLine(fout, indent);
1320                 printTag(lexer, fout, mode, indent, node);
1321                 flushLine(fout, indent);
1322
1323                 for (content = node.content;
1324                         content != null;
1325                         content = content.next)
1326                     printTree(fout, (short)(mode | PREFORMATTED | NOWRAP), indent, lexer, content);
1327
1328                 condFlushLine(fout, indent);
1329                 printEndTag(fout, mode, indent, node);
1330                 flushLine(fout, indent);
1331
1332                 if (this.configuration.IndentContent == false && node.next != null)
1333                     flushLine(fout, indent);
1334             }
1335             else if (node.tag == tt.tagStyle || node.tag == tt.tagScript)
1336             {
1337                 condFlushLine(fout, indent);
1338
1339                 indent = 0;
1340                 condFlushLine(fout, indent);
1341                 printTag(lexer, fout, mode, indent, node);
1342                 flushLine(fout, indent);
1343
1344                 for (content = node.content;
1345                         content != null;
1346                         content = content.next)
1347                     printTree(fout, (short)(mode | PREFORMATTED | NOWRAP |CDATA), indent, lexer, content);
1348
1349                 condFlushLine(fout, indent);
1350                 printEndTag(fout, mode, indent, node);
1351                 flushLine(fout, indent);
1352
1353                 if (this.configuration.IndentContent == false && node.next != null)
1354                     flushLine(fout, indent);
1355             }
1356             else if ((node.tag.model & Dict.CM_INLINE) != 0)
1357             {
1358                 if (this.configuration.MakeClean)
1359                 {
1360                     /* discards <font> and </font> tags */
1361                     if (node.tag == tt.tagFont)
1362                     {
1363                         for (content = node.content;
1364                                 content != null;
1365                                 content = content.next)
1366                             printTree(fout, mode, indent, lexer, content);
1367                         return;
1368                     }
1369
1370                     /* replace <nobr>...</nobr> by &nbsp; or &#160; etc. */
1371                     if (node.tag == tt.tagNobr)
1372                     {
1373                         for (content = node.content;
1374                                 content != null;
1375                                 content = content.next)
1376                             printTree(fout, (short)(mode|NOWRAP), indent, lexer, content);
1377                         return;
1378                     }
1379                 }
1380
1381                 /* otherwise a normal inline element */
1382
1383                 printTag(lexer, fout, mode, indent, node);
1384
1385                 /* indent content for SELECT, TEXTAREA, MAP, OBJECT and APPLET */
1386
1387                 if (shouldIndent(node))
1388                 {
1389                     condFlushLine(fout, indent);
1390                     indent += this.configuration.spaces;
1391
1392                     for (content = node.content;
1393                             content != null;
1394                             content = content.next)
1395                         printTree(fout, mode, indent, lexer, content);
1396
1397                     condFlushLine(fout, indent);
1398                     indent -= this.configuration.spaces;
1399                     condFlushLine(fout, indent);
1400                 }
1401                 else
1402                 {
1403
1404                     for (content = node.content;
1405                             content != null;
1406                             content = content.next)
1407                         printTree(fout, mode, indent, lexer, content);
1408                 }
1409
1410                 printEndTag(fout, mode, indent, node);
1411             }
1412             else /* other tags */
1413             {
1414                 condFlushLine(fout, indent);
1415
1416                 if (this.configuration.SmartIndent && node.prev != null)
1417                     flushLine(fout, indent);
1418
1419                 if (this.configuration.HideEndTags == false ||
1420                     !(node.tag != null && ((node.tag.model & Dict.CM_OMITST) != 0)))
1421                 {
1422                     printTag(lexer, fout, mode, indent, node);
1423
1424                     if (shouldIndent(node))
1425                         condFlushLine(fout, indent);
1426                     else if ((node.tag.model & Dict.CM_HTML) != 0 ||
1427                              node.tag == tt.tagNoframes ||
1428                                 ((node.tag.model & Dict.CM_HEAD) != 0 &&
1429                                 !(node.tag == tt.tagTitle)))
1430                         flushLine(fout, indent);
1431                 }
1432
1433                 if (node.tag == tt.tagBody && this.configuration.BurstSlides)
1434                     printSlide(fout, mode, (this.configuration.IndentContent ? indent+this.configuration.spaces : indent), lexer);
1435                 else
1436                 {
1437                     last = null;
1438
1439                     for (content = node.content;
1440                             content != null; content = content.next)
1441                     {
1442                         /* kludge for naked text before block level tag */
1443                         if (last != null && !this.configuration.IndentContent && last.type == Node.TextNode &&
1444                             content.tag != null && (content.tag.model & Dict.CM_BLOCK) != 0)
1445                         {
1446                             flushLine(fout, indent);
1447                             flushLine(fout, indent);
1448                         }
1449
1450                         printTree(fout, mode,
1451                             (shouldIndent(node) ? indent+this.configuration.spaces : indent), lexer, content);
1452
1453                         last = content;
1454                     }
1455                 }
1456
1457                 /* don't flush line for td and th */
1458                 if (shouldIndent(node) ||
1459                     (((node.tag.model & Dict.CM_HTML) != 0 || node.tag == tt.tagNoframes ||
1460                         ((node.tag.model & Dict.CM_HEAD) != 0 && !(node.tag == tt.tagTitle)))
1461                         && this.configuration.HideEndTags == false))
1462                 {
1463                     condFlushLine(fout, (this.configuration.IndentContent ? indent+this.configuration.spaces : indent));
1464
1465                     if (this.configuration.HideEndTags == false || !((node.tag.model & Dict.CM_OPT) != 0))
1466                     {
1467                         printEndTag(fout, mode, indent, node);
1468                         flushLine(fout, indent);
1469                     }
1470                 }
1471                 else
1472                 {
1473                     if (this.configuration.HideEndTags == false || !((node.tag.model & Dict.CM_OPT) != 0))
1474                         printEndTag(fout, mode, indent, node);
1475
1476                     flushLine(fout, indent);
1477                 }
1478
1479                 if (this.configuration.IndentContent == false &&
1480                     node.next != null &&
1481                     this.configuration.HideEndTags == false &&
1482                     (node.tag.model & (Dict.CM_BLOCK|Dict.CM_LIST|Dict.CM_DEFLIST|Dict.CM_TABLE)) != 0)
1483                 {
1484                     flushLine(fout, indent);
1485                 }
1486             }
1487         }
1488     }
1489
1490     public void printXMLTree(Out fout, short mode, int indent,
1491                              Lexer lexer, Node node)
1492     {
1493         TagTable tt = this.configuration.tt;
1494
1495         if (node == null)
1496             return;
1497
1498         if (node.type == Node.TextNode)
1499         {
1500             printText(fout, mode, indent,
1501                         node.textarray, node.start, node.end);
1502         }
1503         else if (node.type == Node.CommentTag)
1504         {
1505             condFlushLine(fout, indent);
1506             printComment(fout, 0, node);
1507             condFlushLine(fout, 0);
1508         }
1509         else if (node.type == Node.RootNode)
1510         {
1511             Node content;
1512
1513             for (content = node.content;
1514                     content != null;
1515                     content = content.next)
1516                printXMLTree(fout, mode, indent, lexer, content);
1517         }
1518         else if (node.type == Node.DocTypeTag)
1519             printDocType(fout, indent, node);
1520         else if (node.type == Node.ProcInsTag)
1521             printPI(fout, indent, node);
1522         else if (node.type == Node.SectionTag)
1523             printSection(fout, indent, node);
1524         else if (node.type == Node.AspTag)
1525             printAsp(fout, indent, node);
1526         else if (node.type == Node.JsteTag)
1527             printJste(fout, indent, node);
1528         else if (node.type == Node.PhpTag)
1529             printPhp(fout, indent, node);
1530         else if ((node.tag.model & Dict.CM_EMPTY) != 0 || node.type == Node.StartEndTag)
1531         {
1532             condFlushLine(fout, indent);
1533             printTag(lexer, fout, mode, indent, node);
1534             flushLine(fout, indent);
1535
1536             if (node.next != null)
1537                 flushLine(fout, indent);
1538         }
1539         else /* some kind of container element */
1540         {
1541             Node content;
1542             boolean mixed = false;
1543             int cindent;
1544
1545             for (content = node.content; content != null; content = content.next)
1546             {
1547                 if (content.type == Node.TextNode)
1548                 {
1549                     mixed = true;
1550                     break;
1551                 }
1552             }
1553
1554             condFlushLine(fout, indent);
1555
1556             if (ParserImpl.XMLPreserveWhiteSpace(node, tt))
1557             {
1558                 indent = 0;
1559                 cindent = 0;
1560                 mixed = false;
1561             }
1562             else if (mixed)
1563                 cindent = indent;
1564             else
1565                 cindent = indent + this.configuration.spaces;
1566
1567             printTag(lexer, fout, mode, indent, node);
1568
1569             if (!mixed)
1570                 flushLine(fout, indent);
1571  
1572             for (content = node.content;
1573                     content != null;
1574                     content = content.next)
1575                 printXMLTree(fout, mode, cindent, lexer, content);
1576
1577             if (!mixed)
1578                 condFlushLine(fout, cindent);
1579             printEndTag(fout, mode, indent, node);
1580             condFlushLine(fout, indent);
1581
1582             if (node.next != null)
1583                 flushLine(fout, indent);
1584         }
1585     }
1586
1587
1588     /* split parse tree by h2 elements and output to separate files */
1589
1590     /* counts number of h2 children belonging to node */
1591     public int countSlides(Node node)
1592     {
1593         int n = 1;
1594         TagTable tt = this.configuration.tt;
1595
1596         for (node = node.content; node != null; node = node.next)
1597             if (node.tag == tt.tagH2)
1598                 ++n;
1599
1600         return n;
1601     }
1602
1603     /*
1604        inserts a space gif called "dot.gif" to ensure
1605        that the  slide is at least n pixels high
1606      */
1607     private void printVertSpacer(Out fout, int indent)
1608     {
1609         condFlushLine(fout, indent);
1610         printString(fout, indent , 
1611         "<img width=\"0\" height=\"0\" hspace=\"1\" src=\"dot.gif\" vspace=\"%d\" align=\"left\">");
1612         condFlushLine(fout, indent);
1613     }
1614
1615     private void printNavBar(Out fout, int indent)
1616     {
1617         String buf;
1618
1619         condFlushLine(fout, indent);
1620         printString(fout, indent , "<center><small>");
1621
1622         if (slide > 1)
1623         {
1624             buf = "<a href=\"slide" +
1625                   (new Integer(slide - 1)).toString() +
1626                   ".html\">previous</a> | ";
1627             printString(fout, indent , buf);
1628             condFlushLine(fout, indent);
1629
1630             if (slide < count)
1631                 printString(fout, indent , "<a href=\"slide1.html\">start</a> | ");
1632             else
1633                 printString(fout, indent , "<a href=\"slide1.html\">start</a>");
1634
1635             condFlushLine(fout, indent);
1636         }
1637
1638         if (slide < count)
1639         {
1640             buf = "<a href=\"slide" +
1641                   (new Integer(slide + 1)).toString() +
1642                   ".html\">next</a>";
1643             printString(fout, indent , buf);
1644         }
1645
1646         printString(fout, indent , "</small></center>");
1647         condFlushLine(fout, indent);
1648     }
1649
1650     /*
1651       Called from printTree to print the content of a slide from
1652       the node slidecontent. On return slidecontent points to the
1653       node starting the next slide or null. The variables slide
1654       and count are used to customise the navigation bar.
1655     */
1656     public void printSlide(Out fout, short mode, int indent, Lexer lexer)
1657     {
1658         Node content, last;
1659         TagTable tt = this.configuration.tt;
1660
1661         /* insert div for onclick handler */
1662         String s;
1663         s = "<div onclick=\"document.location='slide" +
1664             (new Integer(slide < count ? slide + 1 : 1)).toString() +
1665             ".html'\">";
1666         printString(fout, indent, s);
1667         condFlushLine(fout, indent);
1668
1669         /* first print the h2 element and navbar */
1670         if (slidecontent.tag == tt.tagH2)
1671         {
1672             printNavBar(fout, indent);
1673
1674             /* now print an hr after h2 */
1675
1676             addC('<', linelen++);
1677
1678
1679             addC((int)Lexer.foldCase('h',
1680                                      this.configuration.UpperCaseTags,
1681                                      this.configuration.XmlTags),
1682                  linelen++);
1683             addC((int)Lexer.foldCase('r',
1684                                      this.configuration.UpperCaseTags,
1685                                      this.configuration.XmlTags),
1686                  linelen++);
1687
1688             if (this.configuration.XmlOut == true)
1689                 printString(fout, indent , " />");
1690             else
1691                 addC('>', linelen++);
1692
1693
1694             if (this.configuration.IndentContent == true)
1695                 condFlushLine(fout, indent);
1696
1697             /* PrintVertSpacer(fout, indent); */
1698
1699             /*condFlushLine(fout, indent); */
1700
1701             /* print the h2 element */
1702             printTree(fout, mode,
1703                 (this.configuration.IndentContent ? indent+this.configuration.spaces : indent), lexer, slidecontent);
1704
1705             slidecontent = slidecontent.next;
1706         }
1707     
1708         /* now continue until we reach the next h2 */
1709
1710         last = null;
1711         content = slidecontent;
1712
1713         for (; content != null; content = content.next)
1714         {
1715             if (content.tag == tt.tagH2)
1716                 break;
1717
1718             /* kludge for naked text before block level tag */
1719             if (last != null && !this.configuration.IndentContent && last.type == Node.TextNode &&
1720                 content.tag != null && (content.tag.model & Dict.CM_BLOCK) != 0)
1721             {
1722                 flushLine(fout, indent);
1723                 flushLine(fout, indent);
1724             }
1725
1726             printTree(fout, mode,
1727                 (this.configuration.IndentContent ? indent+this.configuration.spaces : indent), lexer, content);
1728
1729             last = content;
1730         }
1731
1732         slidecontent = content;
1733
1734         /* now print epilog */
1735
1736         condFlushLine(fout, indent);
1737
1738         printString(fout, indent , "<br clear=\"all\">");
1739         condFlushLine(fout, indent);
1740
1741         addC('<', linelen++);
1742
1743
1744         addC((int)Lexer.foldCase('h',
1745                                  this.configuration.UpperCaseTags,
1746                                  this.configuration.XmlTags),
1747              linelen++);
1748         addC((int)Lexer.foldCase('r',
1749                                  this.configuration.UpperCaseTags,
1750                                  this.configuration.XmlTags),
1751              linelen++);
1752
1753         if (this.configuration.XmlOut == true)
1754             printString(fout, indent , " />");
1755         else
1756             addC('>', linelen++);
1757
1758
1759         if (this.configuration.IndentContent == true)
1760             condFlushLine(fout, indent);
1761
1762         printNavBar(fout, indent);
1763
1764         /* end tag for div */
1765         printString(fout, indent, "</div>");
1766         condFlushLine(fout, indent);
1767     }
1768
1769
1770     /*
1771     Add meta element for page transition effect, this works on IE but not NS
1772     */
1773
1774     public void addTransitionEffect(Lexer lexer, Node root, short effect, double duration)
1775     {
1776         Node head = root.findHEAD(lexer.configuration.tt);
1777         String transition;
1778
1779         if (0 <= effect && effect <= 23)
1780             transition = "revealTrans(Duration=" +
1781                          (new Double(duration)).toString() +
1782                          ",Transition=" + effect + ")";
1783         else
1784             transition = "blendTrans(Duration=" +
1785                          (new Double(duration)).toString() + ")";
1786
1787         if (head != null)
1788         {
1789             Node meta = lexer.inferredTag("meta");
1790             meta.addAttribute("http-equiv", "Page-Enter");
1791             meta.addAttribute("content", transition);
1792             Node.insertNodeAtStart(head, meta);
1793         }
1794     }
1795
1796     public void createSlides(Lexer lexer, Node root)
1797     {
1798         Node body;
1799         String buf;
1800         Out out = new OutImpl();
1801
1802         body = root.findBody(lexer.configuration.tt);
1803         count = countSlides(body);
1804         slidecontent = body.content;
1805         addTransitionEffect(lexer, root, EFFECT_BLEND, 3.0);
1806
1807         for (slide = 1; slide <= count; ++slide)
1808         {
1809             buf = "slide" + slide + ".html";
1810             out.state = StreamIn.FSM_ASCII;
1811             out.encoding = this.configuration.CharEncoding;
1812
1813             try
1814             {
1815                 out.out = new FileOutputStream(buf);
1816                 printTree(out, (short)0, 0, lexer, root);
1817                 flushLine(out, 0);
1818                 out.out.close();
1819             }
1820             catch (IOException e)
1821             {
1822                 System.err.println(buf + e.toString() );
1823             }
1824         }
1825
1826         /*
1827          delete superfluous slides by deleting slideN.html
1828          for N = count+1, count+2, etc. until no such file
1829          is found.     
1830         */
1831
1832         for (;;)
1833         {
1834             buf = "slide" + slide + "html";
1835
1836             if (!(new File(buf)).delete())
1837                 break;
1838
1839             ++slide;
1840         }
1841     }
1842
1843 }