KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > event > HTMLIndenter


1 package net.sf.saxon.event;
2 import net.sf.saxon.trans.XPathException;
3
4 import java.util.Properties JavaDoc;
5
6 /**
7 * HTMLIndenter: This ProxyEmitter indents HTML elements, by adding whitespace
8 * character data where appropriate.
9 * The character data is never added when within an inline element.
10 * The string used for indentation defaults to four spaces, but may be set using the
11 * indent-chars property
12 *
13 * @author Michael Kay
14 */

15
16
17 public class HTMLIndenter extends ProxyReceiver {
18
19     private int level = 0;
20     private int indentSpaces = 3;
21     private String JavaDoc indentChars = " ";
22     private boolean sameLine = false;
23     private boolean isInlineTag = false;
24     private boolean inFormattedTag = false;
25     private boolean afterInline = false;
26     private boolean afterFormatted = true; // to prevent a newline at the start
27
private int[] propertyStack = new int[20];
28
29
30     // the list of inline tags is from the HTML 4.0 (loose) spec. The significance is that we
31
// mustn't add spaces immediately before or after one of these elements.
32

33     protected static String JavaDoc[] inlineTags = {
34         "tt", "i", "b", "u", "s", "strike", "big", "small", "em", "strong", "dfn", "code", "samp",
35          "kbd", "var", "cite", "abbr", "acronym", "a", "img", "applet", "object", "font",
36          "basefont", "br", "script", "map", "q", "sub", "sup", "span", "bdo", "iframe", "input",
37          "select", "textarea", "label", "button", "ins", "del" };
38
39         // INS and DEL are not actually inline elements, but the SGML DTD for HTML
40
// (apparently) permits them to be used as if they were.
41

42     private static HTMLTagHashSet inlineTable = new HTMLTagHashSet(101);
43
44     static {
45         for (int j=0; j<inlineTags.length; j++) {
46             inlineTable.add(inlineTags[j]);
47         }
48     }
49
50     protected static final int IS_INLINE = 1;
51     protected static final int IS_FORMATTED = 2;
52
53     /**
54      * Classify an element name as inline, formatted, or both or neither.
55      * This method is overridden in the XHTML indenter
56      * @param nameCode the element name
57      * @return a bit-significant integer containing flags IS_INLINE and/or IS_FORMATTED
58      */

59     protected int classifyTag(int nameCode) {
60         int r = 0;
61         String JavaDoc tag = getNamePool().getDisplayName(nameCode);
62         if (inlineTable.contains(tag)) {
63             r |= IS_INLINE;
64         }
65         if (formattedTable.contains(tag)) {
66             r |= IS_FORMATTED;
67         }
68         return r;
69     }
70
71     // Table of preformatted elements
72

73     private static HTMLTagHashSet formattedTable = new HTMLTagHashSet(23);
74     protected static String JavaDoc[] formattedTags = {"pre", "script", "style", "textarea", "xmp"};
75                                     // "xmp" is obsolete but still encountered!
76

77     static {
78         for (int i=0; i<formattedTags.length; i++) {
79             formattedTable.add(formattedTags[i]);
80         }
81     }
82
83     public HTMLIndenter() {}
84
85     /**
86     * Set the properties for this indenter
87     */

88
89     public void setOutputProperties(Properties JavaDoc props) {
90         String JavaDoc s = props.getProperty(SaxonOutputKeys.INDENT_SPACES);
91         if (s==null) {
92             indentSpaces = 3;
93         } else {
94             try {
95                 indentSpaces = Integer.parseInt(s);
96             } catch (NumberFormatException JavaDoc err) {
97                 indentSpaces = 3;
98             }
99         }
100     }
101
102     /**
103     * Output element start tag
104     */

105
106     public void startElement(int nameCode, int typeCode, int locationId, int properties) throws XPathException {
107         int tagProps = classifyTag(nameCode);
108         if (level >= propertyStack.length) {
109             int[] p2 = new int[level*2];
110             System.arraycopy(propertyStack, 0, p2, 0, propertyStack.length);
111             propertyStack = p2;
112         }
113         propertyStack[level] = tagProps;
114         isInlineTag = (tagProps & IS_INLINE) != 0;
115         inFormattedTag = inFormattedTag || ((tagProps & IS_FORMATTED) != 0);
116         if (!isInlineTag && !inFormattedTag &&
117              !afterInline && !afterFormatted) {
118             indent();
119         }
120
121         super.startElement(nameCode, typeCode, locationId, properties);
122         level++;
123         sameLine = true;
124         afterInline = false;
125         afterFormatted = false;
126     }
127
128     /**
129     * Output element end tag
130     */

131
132     public void endElement() throws XPathException {
133         level--;
134         boolean thisInline = (propertyStack[level] & IS_INLINE) != 0;
135         boolean thisFormatted = (propertyStack[level] & IS_FORMATTED) != 0;
136         if (!thisInline && !thisFormatted && !afterInline &&
137                  !sameLine && !afterFormatted && !inFormattedTag) {
138             indent();
139             afterInline = false;
140             afterFormatted = false;
141         } else {
142             afterInline = thisInline;
143             afterFormatted = thisFormatted;
144         }
145         super.endElement();
146         inFormattedTag = inFormattedTag && !thisFormatted;
147         sameLine = false;
148     }
149
150     /**
151     * Output character data
152     */

153
154     public void characters(CharSequence JavaDoc chars, int locationId, int properties) throws XPathException {
155         if (inFormattedTag) {
156             super.characters(chars, locationId, properties);
157         } else {
158             int lastNL = 0;
159
160             for (int i=0; i<chars.length(); i++) {
161                 if (chars.charAt(i)=='\n' || (i-lastNL > 120 && chars.charAt(i)==' ')) {
162                     sameLine = false;
163                     super.characters(chars.subSequence(lastNL, i), locationId, properties);
164                     indent();
165                     lastNL = i+1;
166                     while (lastNL<chars.length() && chars.charAt(lastNL)==' ') {
167                         lastNL++;
168                     }
169                 }
170             }
171             if (lastNL < chars.length()) {
172                 super.characters(chars.subSequence(lastNL, chars.length()), locationId, properties);
173             }
174         }
175         afterInline = false;
176     }
177
178     /**
179     * Output a comment
180     */

181
182     public void comment(CharSequence JavaDoc chars, int locationId, int properties) throws XPathException {
183         indent();
184         super.comment(chars, locationId, properties);
185     }
186
187     /**
188     * Output white space to reflect the current indentation level
189     */

190
191     private void indent() throws XPathException {
192         int spaces = level * indentSpaces;
193         while (spaces > indentChars.length()) {
194             indentChars += indentChars;
195         }
196         super.characters("\n", 0, 0);
197         super.characters(indentChars.subSequence(0, spaces), 0, 0);
198         sameLine = false;
199     }
200
201 };
202
203 //
204
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
205
// you may not use this file except in compliance with the License. You may obtain a copy of the
206
// License at http://www.mozilla.org/MPL/
207
//
208
// Software distributed under the License is distributed on an "AS IS" basis,
209
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
210
// See the License for the specific language governing rights and limitations under the License.
211
//
212
// The Original Code is: all this file.
213
//
214
// The Initial Developer of the Original Code is Michael H. Kay.
215
//
216
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
217
//
218
// Contributor(s): none.
219
//
220

221
Popular Tags