001    /*
002    // $Id: //open/mondrian/src/main/mondrian/xmla/impl/DefaultSaxWriter.java#7 $
003    // This software is subject to the terms of the Common Public License
004    // Agreement, available at the following URL:
005    // http://www.opensource.org/licenses/cpl.html.
006    // Copyright (C) 2005-2008 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package mondrian.xmla.impl;
011    
012    import java.io.OutputStream;
013    import java.io.OutputStreamWriter;
014    import java.io.PrintWriter;
015    import java.io.UnsupportedEncodingException;
016    import java.io.Writer;
017    import java.util.Stack;
018    
019    import mondrian.xmla.SaxWriter;
020    
021    import org.eigenbase.xom.XMLUtil;
022    import org.eigenbase.xom.XOMUtil;
023    import org.xml.sax.Attributes;
024    
025    /**
026     * Default implementation of {@link SaxWriter}.
027     *
028     * @author jhyde
029     * @author Gang Chen
030     * @since 27 April, 2003
031     */
032    public class DefaultSaxWriter implements SaxWriter {
033        /** Inside the tag of an element. */
034        private static final int STATE_IN_TAG = 0;
035        /** After the tag at the end of an element. */
036        private static final int STATE_END_ELEMENT = 1;
037        /** After the tag at the start of an element. */
038        private static final int STATE_AFTER_TAG = 2;
039        /** After a burst of character data. */
040        private static final int STATE_CHARACTERS = 3;
041    
042        private final PrintWriter writer;
043        private int indent;
044        private String indentStr = "  ";
045        private final Stack stack = new Stack();
046        private int state = STATE_END_ELEMENT;
047    
048    
049        /**
050         * Creates a <code>SAXWriter</code> writing to an {@link java.io.OutputStream}.
051         */
052        public DefaultSaxWriter(OutputStream stream) {
053            this(new OutputStreamWriter(stream));
054        }
055        public DefaultSaxWriter(OutputStream stream, String xmlEncoding)
056            throws UnsupportedEncodingException {
057            this(new OutputStreamWriter(stream, xmlEncoding));
058        }
059    
060        /**
061         * Creates a <code>SAXWriter</code> writing to a {@link java.io.Writer}.
062         *
063         * <p>If <code>writer</code> is a {@link java.io.PrintWriter},
064         * {@link #DefaultSaxWriter(java.io.OutputStream)} is preferred.
065         */
066        public DefaultSaxWriter(Writer writer) {
067            this(new PrintWriter(writer), 0);
068        }
069    
070        /**
071         * Creates a <code>SAXWriter</code> writing to a {@link java.io.PrintWriter}.
072         * @param writer
073         * @param initialIndent
074         */
075        public DefaultSaxWriter(PrintWriter writer, int initialIndent) {
076            this.writer = writer;
077            this.indent = initialIndent;
078        }
079    
080        private void _startElement(
081                String namespaceURI, String localName,
082                String qName, Attributes atts) {
083            _checkTag();
084            if (indent > 0) {
085                writer.println();
086            }
087            for (int i = 0; i < indent; i++) {
088                writer.write(indentStr);
089            }
090            indent++;
091            writer.write('<');
092            writer.write(qName);
093            for (int i = 0; i < atts.getLength(); i++) {
094                XMLUtil.printAtt(writer, atts.getQName(i), atts.getValue(i));
095            }
096            state = STATE_IN_TAG;
097        }
098    
099        private void _checkTag() {
100            if (state == STATE_IN_TAG) {
101                state = STATE_AFTER_TAG;
102                writer.print(">");
103            }
104        }
105    
106        private void _endElement(
107                String namespaceURI, String localName, String qName) {
108            indent--;
109            if (state == STATE_IN_TAG) {
110                writer.write("/>");
111            } else {
112                if (state != STATE_CHARACTERS) {
113                    writer.println();
114                    for (int i = 0; i < indent; i++) {
115                        writer.write(indentStr);
116                    }
117                }
118                writer.write("</");
119                writer.write(qName);
120                writer.write('>');
121            }
122            state = STATE_END_ELEMENT;
123        }
124    
125        private void _characters(char ch[], int start, int length) {
126            _checkTag();
127    
128            // Display the string, quoting in <![CDATA[ ... ]]> if necessary,
129            // or using XML escapes as a last result.
130            String s = new String(ch, start, length);
131            if (XOMUtil.stringHasXMLSpecials(s)) {
132                XMLUtil.stringEncodeXML(s, writer);
133    /*
134                if (s.indexOf("]]>") < 0) {
135                    writer.print("<![CDATA[");
136                    writer.print(s);
137                    writer.print("]]>");
138                } else {
139                    XMLUtil.stringEncodeXML(s, writer);
140                }
141    */
142            } else {
143                writer.print(s);
144            }
145    
146            state = STATE_CHARACTERS;
147        }
148    
149    
150        //
151        // Simplifying methods
152    
153        public void characters(String s) {
154            if (s != null && s.length() > 0) {
155                _characters(s.toCharArray(), 0, s.length());
156            }
157        }
158    
159        public void element(String tagName, String[] attributes) {
160            startElement(tagName, attributes);
161            endElement();
162        }
163    
164        public void startElement(String tagName) {
165            _startElement(null, null, tagName, EmptyAttributes);
166            stack.push(tagName);
167        }
168    
169        public void startElement(String tagName, String[] attributes) {
170            _startElement(null, null, tagName, new StringAttributes(attributes));
171            stack.push(tagName);
172        }
173    
174        public void endElement() {
175            String tagName = (String) stack.pop();
176            _endElement(null, null, tagName);
177        }
178    
179        public void startDocument() {
180            if (stack.size() != 0) {
181                throw new IllegalStateException("Document already started");
182            }
183        }
184    
185        public void endDocument() {
186            if (stack.size() != 0) {
187                throw new IllegalStateException("Document may have unbalanced elements");
188            }
189            writer.flush();
190        }
191    
192        public void completeBeforeElement(String tagName) {
193            if (stack.indexOf(tagName) == -1) {
194                return;
195            }
196    
197            String currentTagName  = (String) stack.peek();
198            while (!tagName.equals(currentTagName)) {
199                _endElement(null, null, currentTagName);
200                stack.pop();
201                currentTagName = (String) stack.peek();
202            }
203        }
204    
205        public void verbatim(String text) {
206            _checkTag();
207            writer.print(text);
208        }
209    
210        public void flush() {
211            writer.flush();
212        }
213    
214        static private final Attributes EmptyAttributes = new Attributes() {
215            public int getLength() {
216                return 0;
217            }
218    
219            public String getURI(int index) {
220                return null;
221            }
222    
223            public String getLocalName(int index) {
224                return null;
225            }
226    
227            public String getQName(int index) {
228                return null;
229            }
230    
231            public String getType(int index) {
232                return null;
233            }
234    
235            public String getValue(int index) {
236                return null;
237            }
238    
239            public int getIndex(String uri, String localName) {
240                return 0;
241            }
242    
243            public int getIndex(String qName) {
244                return 0;
245            }
246    
247            public String getType(String uri, String localName) {
248                return null;
249            }
250    
251            public String getType(String qName) {
252                return null;
253            }
254    
255            public String getValue(String uri, String localName) {
256                return null;
257            }
258    
259            public String getValue(String qName) {
260                return null;
261            }
262        };
263    
264        /**
265         * List of SAX attributes based upon a string array.
266         */
267        public static class StringAttributes implements Attributes {
268            private final String[] strings;
269    
270            public StringAttributes(String[] strings) {
271                this.strings = strings;
272            }
273    
274            public int getLength() {
275                return strings.length / 2;
276            }
277    
278            public String getURI(int index) {
279                return null;
280            }
281    
282            public String getLocalName(int index) {
283                return null;
284            }
285    
286            public String getQName(int index) {
287                return strings[index * 2];
288            }
289    
290            public String getType(int index) {
291                return null;
292            }
293    
294            public String getValue(int index) {
295                return strings[index * 2 + 1];
296            }
297    
298            public int getIndex(String uri, String localName) {
299                return -1;
300            }
301    
302            public int getIndex(String qName) {
303                final int count = strings.length / 2;
304                for (int i = 0; i < count; i++) {
305                    String string = strings[i * 2];
306                    if (string.equals(qName)) {
307                        return i;
308                    }
309                }
310                return -1;
311            }
312    
313            public String getType(String uri, String localName) {
314                return null;
315            }
316    
317            public String getType(String qName) {
318                return null;
319            }
320    
321            public String getValue(String uri, String localName) {
322                return null;
323            }
324    
325            public String getValue(String qName) {
326                final int index = getIndex(qName);
327                if (index < 0) {
328                    return null;
329                } else {
330                    return strings[index * 2 + 1];
331                }
332            }
333        }
334    }
335    
336    // End DefaultSaxWriter.java