SAXEventSerializer.java revision 524:dcaa586ab756
1/*
2 * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 *
25 * THIS FILE WAS MODIFIED BY SUN MICROSYSTEMS, INC.
26 */
27
28package com.sun.xml.internal.fastinfoset.tools;
29
30import java.io.IOException;
31import java.io.OutputStream;
32import java.io.OutputStreamWriter;
33import java.io.Writer;
34import java.util.ArrayList;
35import java.util.List;
36import java.util.Stack;
37
38import org.xml.sax.Attributes;
39import org.xml.sax.SAXException;
40import org.xml.sax.ext.LexicalHandler;
41import org.xml.sax.helpers.DefaultHandler;
42import com.sun.xml.internal.fastinfoset.CommonResourceBundle;
43
44public class SAXEventSerializer extends DefaultHandler
45        implements LexicalHandler {
46
47    private Writer _writer;
48    private boolean _charactersAreCDATA;
49    private StringBuffer _characters;
50
51    private Stack _namespaceStack = new Stack();
52    protected List _namespaceAttributes;
53
54    public SAXEventSerializer(OutputStream s) throws IOException {
55        _writer = new OutputStreamWriter(s);
56        _charactersAreCDATA = false;
57    }
58
59    // -- ContentHandler interface ---------------------------------------
60
61    public void startDocument() throws SAXException {
62        try {
63            _writer.write("<sax xmlns=\"http://www.sun.com/xml/sax-events\">\n");
64            _writer.write("<startDocument/>\n");
65            _writer.flush();
66        }
67        catch (IOException e) {
68            throw new SAXException(e);
69        }
70    }
71
72    public void endDocument() throws SAXException {
73        try {
74            _writer.write("<endDocument/>\n");
75            _writer.write("</sax>");
76            _writer.flush();
77            _writer.close();
78        }
79        catch (IOException e) {
80            throw new SAXException(e);
81        }
82    }
83
84
85    public void startPrefixMapping(String prefix, String uri)
86        throws SAXException
87    {
88        if (_namespaceAttributes == null) {
89            _namespaceAttributes = new ArrayList();
90        }
91
92        String qName = (prefix.length() == 0) ? "xmlns" : "xmlns" + prefix;
93        AttributeValueHolder attribute = new AttributeValueHolder(
94                qName,
95                prefix,
96                uri,
97                null,
98                null);
99        _namespaceAttributes.add(attribute);
100    }
101
102    public void endPrefixMapping(String prefix)
103        throws SAXException
104    {
105        /*
106        try {
107            outputCharacters();
108
109            _writer.write("<endPrefixMapping prefix=\"" +
110                prefix + "\"/>\n");
111            _writer.flush();
112        }
113        catch (IOException e) {
114            throw new SAXException(e);
115        }
116         */
117    }
118
119    public void startElement(String uri, String localName,
120            String qName, Attributes attributes)
121        throws SAXException
122    {
123        try {
124            outputCharacters();
125
126            if (_namespaceAttributes != null) {
127
128                AttributeValueHolder[] attrsHolder = new AttributeValueHolder[0];
129                attrsHolder = (AttributeValueHolder[])_namespaceAttributes.toArray(attrsHolder);
130
131                // Sort attributes
132                quicksort(attrsHolder, 0, attrsHolder.length - 1);
133
134                for (int i = 0; i < attrsHolder.length; i++) {
135                    _writer.write("<startPrefixMapping prefix=\"" +
136                        attrsHolder[i].localName + "\" uri=\"" + attrsHolder[i].uri + "\"/>\n");
137                    _writer.flush();
138                }
139
140                _namespaceStack.push(attrsHolder);
141                _namespaceAttributes = null;
142            } else {
143                _namespaceStack.push(null);
144            }
145
146            AttributeValueHolder[] attrsHolder =
147                new AttributeValueHolder[attributes.getLength()];
148            for (int i = 0; i < attributes.getLength(); i++) {
149                attrsHolder[i] = new AttributeValueHolder(
150                    attributes.getQName(i),
151                    attributes.getLocalName(i),
152                    attributes.getURI(i),
153                    attributes.getType(i),
154                    attributes.getValue(i));
155            }
156
157            // Sort attributes
158            quicksort(attrsHolder, 0, attrsHolder.length - 1);
159
160            int attributeCount = 0;
161            for (int i = 0; i < attrsHolder.length; i++) {
162                if (attrsHolder[i].uri.equals("http://www.w3.org/2000/xmlns/")) {
163                    // Ignore XMLNS attributes
164                    continue;
165                }
166                attributeCount++;
167            }
168
169            if (attributeCount == 0) {
170                _writer.write("<startElement uri=\"" + uri
171                    + "\" localName=\"" + localName + "\" qName=\""
172                    + qName + "\"/>\n");
173                return;
174            }
175
176            _writer.write("<startElement uri=\"" + uri
177                + "\" localName=\"" + localName + "\" qName=\""
178                + qName + "\">\n");
179
180            // Serialize attributes as children
181            for (int i = 0; i < attrsHolder.length; i++) {
182                if (attrsHolder[i].uri.equals("http://www.w3.org/2000/xmlns/")) {
183                    // Ignore XMLNS attributes
184                    continue;
185                }
186                _writer.write(
187                    "  <attribute qName=\"" + attrsHolder[i].qName +
188                    "\" localName=\"" + attrsHolder[i].localName +
189                    "\" uri=\"" + attrsHolder[i].uri +
190                    // "\" type=\"" + attrsHolder[i].type +
191                    "\" value=\"" + attrsHolder[i].value +
192                    "\"/>\n");
193            }
194
195            _writer.write("</startElement>\n");
196            _writer.flush();
197        }
198        catch (IOException e) {
199            throw new SAXException(e);
200        }
201    }
202
203    public void endElement(String uri, String localName, String qName)
204            throws SAXException
205    {
206        try {
207            outputCharacters();
208
209            _writer.write("<endElement uri=\"" + uri
210                + "\" localName=\"" + localName + "\" qName=\""
211                + qName + "\"/>\n");
212            _writer.flush();
213
214            // Write out the end prefix here rather than waiting
215            // for the explicit events
216            AttributeValueHolder[] attrsHolder = (AttributeValueHolder[])_namespaceStack.pop();
217            if (attrsHolder != null) {
218                for (int i = 0; i < attrsHolder.length; i++) {
219                    _writer.write("<endPrefixMapping prefix=\"" +
220                        attrsHolder[i].localName  + "\"/>\n");
221                    _writer.flush();
222                }
223            }
224
225        }
226        catch (IOException e) {
227            throw new SAXException(e);
228        }
229    }
230
231    public void characters(char[] ch, int start, int length)
232            throws SAXException
233    {
234        if (length == 0) {
235            return;
236        }
237
238        if (_characters == null) {
239            _characters = new StringBuffer();
240        }
241
242        // Coalesce multiple character events
243        _characters.append(ch, start, length);
244
245        /*
246        try {
247            _writer.write("<characters>" +
248                (_charactersAreCDATA ? "<![CDATA[" : "") +
249                new String(ch, start, length) +
250                (_charactersAreCDATA ? "]]>" : "") +
251                "</characters>\n");
252            _writer.flush();
253        }
254        catch (IOException e) {
255            throw new SAXException(e);
256        }
257         */
258    }
259
260    private void outputCharacters() throws SAXException {
261        if (_characters == null) {
262            return;
263        }
264
265        try {
266            _writer.write("<characters>" +
267                (_charactersAreCDATA ? "<![CDATA[" : "") +
268                _characters +
269                (_charactersAreCDATA ? "]]>" : "") +
270                "</characters>\n");
271            _writer.flush();
272
273            _characters = null;
274        } catch (IOException e) {
275            throw new SAXException(e);
276        }
277    }
278
279    public void ignorableWhitespace(char[] ch, int start, int length)
280            throws SAXException
281    {
282        // Report ignorable ws as characters (assumes validation off)
283        characters(ch, start, length);
284    }
285
286    public void processingInstruction(String target, String data)
287            throws SAXException
288    {
289        try {
290            outputCharacters();
291
292            _writer.write("<processingInstruction target=\"" + target
293                + "\" data=\"" + data + "\"/>\n");
294            _writer.flush();
295        }
296        catch (IOException e) {
297            throw new SAXException(e);
298        }
299    }
300
301    // -- LexicalHandler interface ---------------------------------------
302
303    public void startDTD(String name, String publicId, String systemId)
304            throws SAXException {
305        // Not implemented
306    }
307
308    public void endDTD()
309            throws SAXException {
310        // Not implemented
311    }
312
313    public void startEntity(String name)
314            throws SAXException {
315        // Not implemented
316    }
317
318    public void endEntity(String name)
319            throws SAXException {
320        // Not implemented
321    }
322
323    public void startCDATA()
324            throws SAXException {
325        _charactersAreCDATA = true;
326    }
327
328    public void endCDATA()
329            throws SAXException {
330        _charactersAreCDATA = false;
331    }
332
333    public void comment(char[] ch, int start, int length)
334            throws SAXException
335    {
336        try {
337            outputCharacters();
338
339            _writer.write("<comment>" +
340                new String(ch, start, length) +
341                "</comment>\n");
342            _writer.flush();
343        }
344        catch (IOException e) {
345            throw new SAXException(e);
346        }
347    }
348
349    // -- Utility methods ------------------------------------------------
350
351    private void quicksort(AttributeValueHolder[] attrs, int p, int r) {
352        while (p < r) {
353            final int q = partition(attrs, p, r);
354            quicksort(attrs, p, q);
355            p = q + 1;
356        }
357    }
358
359    private int partition(AttributeValueHolder[] attrs, int p, int r) {
360        AttributeValueHolder x = attrs[(p + r) >>> 1];
361        int i = p - 1;
362        int j = r + 1;
363        while (true) {
364            while (x.compareTo(attrs[--j]) < 0);
365            while (x.compareTo(attrs[++i]) > 0);
366            if (i < j) {
367                final AttributeValueHolder t = attrs[i];
368                attrs[i] = attrs[j];
369                attrs[j] = t;
370            }
371            else {
372                return j;
373            }
374        }
375    }
376
377    public static class AttributeValueHolder implements Comparable {
378        public final String qName;
379        public final String localName;
380        public final String uri;
381        public final String type;
382        public final String value;
383
384        public AttributeValueHolder(String qName,
385            String localName,
386            String uri,
387            String type,
388            String value)
389        {
390            this.qName = qName;
391            this.localName = localName;
392            this.uri = uri;
393            this.type = type;
394            this.value = value;
395        }
396
397        public int compareTo(Object o) {
398            try {
399                return qName.compareTo(((AttributeValueHolder) o).qName);
400            } catch (Exception e) {
401                throw new RuntimeException(CommonResourceBundle.getInstance().getString("message.AttributeValueHolderExpected"));
402            }
403        }
404
405        @Override
406        public boolean equals(Object o) {
407            try {
408                return (o instanceof AttributeValueHolder) &&
409                        qName.equals(((AttributeValueHolder) o).qName);
410            } catch (Exception e) {
411                throw new RuntimeException(CommonResourceBundle.getInstance().getString("message.AttributeValueHolderExpected"));
412            }
413        }
414
415        @Override
416        public int hashCode() {
417            int hash = 7;
418            hash = 97 * hash + (this.qName != null ? this.qName.hashCode() : 0);
419            return hash;
420        }
421    }
422
423}
424