~ubuntu-branches/ubuntu/precise/xom/precise

« back to all changes in this revision

Viewing changes to src/nu/xom/converters/SAXConverter.java

  • Committer: Bazaar Package Importer
  • Author(s): Varun Hiremath
  • Date: 2007-11-25 15:50:40 UTC
  • Revision ID: james.westby@ubuntu.com-20071125155040-r75ikcqf1vu0cei7
Tags: upstream-1.1
ImportĀ upstreamĀ versionĀ 1.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright 2002-2005 Elliotte Rusty Harold
 
2
   
 
3
   This library is free software; you can redistribute it and/or modify
 
4
   it under the terms of version 2.1 of the GNU Lesser General Public 
 
5
   License as published by the Free Software Foundation.
 
6
   
 
7
   This library is distributed in the hope that it will be useful,
 
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 
10
   GNU Lesser General Public License for more details.
 
11
   
 
12
   You should have received a copy of the GNU Lesser General Public
 
13
   License along with this library; if not, write to the 
 
14
   Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
 
15
   Boston, MA 02111-1307  USA
 
16
   
 
17
   You can contact Elliotte Rusty Harold by sending e-mail to
 
18
   elharo@metalab.unc.edu. Please include the word "XOM" in the
 
19
   subject line. The XOM home page is located at http://www.xom.nu/
 
20
*/
 
21
 
 
22
package nu.xom.converters;
 
23
 
 
24
import nu.xom.Attribute;
 
25
import nu.xom.Comment;
 
26
import nu.xom.DocType;
 
27
import nu.xom.Document;
 
28
import nu.xom.Element;
 
29
import nu.xom.Node;
 
30
import nu.xom.Nodes;
 
31
import nu.xom.ParentNode;
 
32
import nu.xom.ProcessingInstruction;
 
33
import nu.xom.Text;
 
34
 
 
35
import org.xml.sax.ContentHandler;
 
36
import org.xml.sax.SAXException;
 
37
import org.xml.sax.ext.LexicalHandler;
 
38
import org.xml.sax.helpers.AttributesImpl;
 
39
import org.xml.sax.helpers.LocatorImpl;
 
40
 
 
41
/**
 
42
 * <p>
 
43
 * Feeds a XOM <code>Document</code> into a
 
44
 * SAX2 <code>ContentHandler</code>.
 
45
 * </p>
 
46
 * 
 
47
 * @author Elliotte Rusty Harold
 
48
 * @version 1.1b2 
 
49
 */
 
50
public class SAXConverter {
 
51
 
 
52
    
 
53
    private ContentHandler contentHandler;
 
54
    private LexicalHandler lexicalHandler;
 
55
    private LocatorImpl    locator;
 
56
    private boolean        stripBaseAttributes = true;
 
57
 
 
58
    
 
59
    /**
 
60
     * <p>
 
61
     * Creates a new <code>SAXConverter</code>.
 
62
     * </p>
 
63
     * 
 
64
     * @param handler the SAX2 content handler 
 
65
     *     that receives the data
 
66
     * 
 
67
     * @throws NullPointerException if handler is null
 
68
     * 
 
69
     */
 
70
    public SAXConverter(ContentHandler handler) {
 
71
        setContentHandler(handler);
 
72
    }
 
73
 
 
74
    
 
75
    /**
 
76
     * <p>
 
77
     * Set the content handler for this converter.
 
78
     * </p>
 
79
     * 
 
80
     * @param handler SAX2 content handler that 
 
81
     *     receives the data
 
82
     * 
 
83
     * @throws NullPointerException if handler is null
 
84
     * 
 
85
     */
 
86
    public void setContentHandler(ContentHandler handler) {
 
87
        
 
88
        if (handler == null) {
 
89
            throw new NullPointerException(
 
90
              "ContentHandler must be non-null."
 
91
            );
 
92
        }
 
93
        // unbelievably skanky hack to allow xml:base attributes
 
94
        // to be passed to XSL transforms without mucking with the 
 
95
        // public API. This would be so much easier if Java had friend 
 
96
        // functions.
 
97
        else if ("nu.xom.xslt.XSLTHandler".equals(handler.getClass().getName())) {
 
98
            this.stripBaseAttributes = false;
 
99
        }
 
100
        else {
 
101
            this.contentHandler = handler;
 
102
        }
 
103
        
 
104
    }
 
105
 
 
106
    
 
107
    /**
 
108
     * <p>
 
109
     * Returns the content handler.
 
110
     * </p>
 
111
     * 
 
112
     * @return SAX2 content handler that receives the data
 
113
     */
 
114
    public ContentHandler getContentHandler() {
 
115
        return this.contentHandler;
 
116
    }
 
117
 
 
118
 
 
119
    /**
 
120
     * <p>
 
121
     * Sets the optional lexical handler for this converter.
 
122
     * The only lexical events the converter supplies 
 
123
     * are comments.
 
124
     * </p>
 
125
     * 
 
126
     * @param handler the lexical handler; 
 
127
     *     may be null to turn off lexical events
 
128
     */
 
129
    public void setLexicalHandler(LexicalHandler handler) {
 
130
        this.lexicalHandler = handler;
 
131
    }
 
132
 
 
133
    
 
134
    /**
 
135
     * <p>
 
136
     * Returns the <code>LexicalHandler</code> for this
 
137
     * converter. This is only used for comments.
 
138
     * </p>
 
139
     * 
 
140
     * @return SAX2 lexical handler that receives 
 
141
     *     lexical events
 
142
     */
 
143
    public LexicalHandler getLexicalHandler() {
 
144
        return this.lexicalHandler;
 
145
    }
 
146
    
 
147
    
 
148
    // Not necessary to worry about parser exceptions passed to
 
149
    // fatalError() because we're starting with a known good document.
 
150
    // Only exceptions that can arise are thrown by 
 
151
    // the supplied ContentHandler, and we don't want to pass those
 
152
    // to the ErrorHandler, or call endDocument() if such an exception
 
153
    // is thrown
 
154
    /**
 
155
     * <p>
 
156
     * Feed a document through this converter.
 
157
     * </p>
 
158
     * 
 
159
     * @param doc the document to pass to SAX
 
160
     * 
 
161
     * @throws SAXException if the content handler
 
162
     *      or lexical handler throws an exception
 
163
     */
 
164
    public void convert(Document doc) throws SAXException {
 
165
        
 
166
        locator = new LocatorImpl();
 
167
        locator.setSystemId(doc.getBaseURI());
 
168
        contentHandler.setDocumentLocator(locator);
 
169
        contentHandler.startDocument();
 
170
        for (int i = 0; i < doc.getChildCount(); i++) {
 
171
             process(doc.getChild(i));
 
172
        }                 
 
173
        contentHandler.endDocument();
 
174
        
 
175
    }
 
176
    
 
177
    
 
178
    private void process(Node node) throws SAXException {
 
179
        
 
180
        if (node instanceof Element) {
 
181
            convertElement((Element) node);
 
182
        }
 
183
        else if (node instanceof Text) {
 
184
            String data = node.getValue();
 
185
            contentHandler.characters(
 
186
              data.toCharArray(), 0, data.length());
 
187
        }
 
188
        else if (node instanceof ProcessingInstruction) {
 
189
            ProcessingInstruction instruction 
 
190
              = (ProcessingInstruction) node;
 
191
            
 
192
            contentHandler.processingInstruction(
 
193
              instruction.getTarget(), instruction.getValue());
 
194
        }
 
195
        else if (node instanceof Comment && lexicalHandler != null) {
 
196
            String data = node.getValue();
 
197
            lexicalHandler.comment(
 
198
              data.toCharArray(), 0, data.length());            
 
199
        }
 
200
        else if (node instanceof DocType && lexicalHandler != null) {
 
201
            DocType type = (DocType) node;
 
202
            lexicalHandler.startDTD(type.getRootElementName(), 
 
203
              type.getPublicID(), type.getSystemID());              
 
204
            lexicalHandler.endDTD();          
 
205
        }
 
206
        // all other types are ignored
 
207
        
 
208
    }
 
209
    
 
210
    
 
211
    /**
 
212
     * @param element the context in which the prefix is mapped
 
213
     * @param prefix the prefix to pass to statPrefixMapping
 
214
     * @return true if and only if startPrefixMapping was called
 
215
     * @throws SAXException if the ContentHandler throws an exception
 
216
     */
 
217
    private boolean convertNamespace(Element element, String prefix)
 
218
      throws SAXException {
 
219
        
 
220
        String uri = element.getNamespaceURI(prefix);
 
221
        ParentNode parentNode = element.getParent();
 
222
        Element parent = null;
 
223
        if (parentNode instanceof Element) {
 
224
            parent = (Element) parentNode;   
 
225
        }
 
226
        
 
227
        if (parent != null && uri.equals(parent.getNamespaceURI(prefix))) {
 
228
            return false; 
 
229
        }
 
230
        else if (parent == null && "".equals(uri)) {
 
231
            // Do not fire startPrefixMapping event for no namespace
 
232
            // on root element
 
233
            return false;
 
234
        }
 
235
        contentHandler.startPrefixMapping(prefix, uri); 
 
236
        return true; // i.e. converted
 
237
        
 
238
    }
 
239
 
 
240
    
 
241
    private void convertElement(Element element) throws SAXException {
 
242
        
 
243
        locator.setSystemId(element.getBaseURI());
 
244
        
 
245
        // start prefix mapping
 
246
        int namespaceCount = element.getNamespaceDeclarationCount();
 
247
        String[] prefixes = new String[namespaceCount];
 
248
        int prefixCount = 0;
 
249
        for (int i = 0; i < namespaceCount; i++) {
 
250
            String prefix = element.getNamespacePrefix(i);
 
251
            boolean converted = convertNamespace(element, prefix);
 
252
            if (converted) {
 
253
                prefixes[prefixCount] = prefix;
 
254
                prefixCount++;
 
255
            }
 
256
        }
 
257
        
 
258
        // prepare attributes
 
259
        AttributesImpl saxAttributes = new AttributesImpl();
 
260
        int attributeCount = element.getAttributeCount();
 
261
        for (int i = 0; i < attributeCount; i++) {
 
262
            Attribute attribute = element.getAttribute(i);
 
263
            // The base URIs provided by the locator have already 
 
264
            // accounted for any xml:base attributes. We do not
 
265
            // also pass in xml:base attributes or some relative base 
 
266
            // URIs could be applied twice.
 
267
            if ("base".equals(attribute.getLocalName())
 
268
              && "http://www.w3.org/XML/1998/namespace".equals(attribute.getNamespaceURI())
 
269
              && stripBaseAttributes) {
 
270
                continue;   
 
271
            }
 
272
            saxAttributes.addAttribute(attribute.getNamespaceURI(),
 
273
              attribute.getLocalName(),
 
274
              attribute.getQualifiedName(),
 
275
              getSAXType(attribute),
 
276
              attribute.getValue());
 
277
        }
 
278
        
 
279
        contentHandler.startElement(
 
280
          element.getNamespaceURI(),
 
281
          element.getLocalName(), 
 
282
          element.getQualifiedName(), 
 
283
          saxAttributes);
 
284
        int childCount = element.getChildCount();
 
285
        for (int i = 0; i < childCount; i++) {
 
286
            process(element.getChild(i));   
 
287
        }
 
288
        contentHandler.endElement(element.getNamespaceURI(),
 
289
          element.getLocalName(), element.getQualifiedName());
 
290
        
 
291
        // end prefix mappings
 
292
        for (int i = 0; i < prefixCount; i++) {
 
293
            contentHandler.endPrefixMapping(prefixes[i]);
 
294
        }
 
295
    
 
296
    }
 
297
    
 
298
    
 
299
    private static String getSAXType(Attribute attribute) {
 
300
 
 
301
        Attribute.Type type = attribute.getType();
 
302
        if (type.equals(Attribute.Type.UNDECLARED))  return "CDATA";
 
303
        if (type.equals(Attribute.Type.CDATA))       return "CDATA";
 
304
        if (type.equals(Attribute.Type.ID))          return "ID";
 
305
        if (type.equals(Attribute.Type.IDREF))       return "IDREF";
 
306
        if (type.equals(Attribute.Type.IDREFS))      return "IDREFS";
 
307
        if (type.equals(Attribute.Type.NMTOKEN))     return "NMTOKEN";
 
308
        if (type.equals(Attribute.Type.NMTOKENS))    return "NMTOKENS";
 
309
        if (type.equals(Attribute.Type.ENTITY))      return "ENTITY";
 
310
        if (type.equals(Attribute.Type.ENTITIES))    return "ENTITIES";
 
311
        if (type.equals(Attribute.Type.NOTATION))    return "NOTATION";
 
312
        return "NMTOKEN"; // ENUMERATED
 
313
        
 
314
    }
 
315
 
 
316
 
 
317
    /**
 
318
     * <p>
 
319
     * Converts a <code>Nodes</code> list into SAX by firing events
 
320
     * into the registered handlers. This method calls 
 
321
     * <code>startDocument</code> before processing the list
 
322
     * of nodes, and calls <code>endDocument</code> after processing 
 
323
     * all of them. 
 
324
     * </p>
 
325
     * 
 
326
     * @param nodes the nodes to pass to SAX
 
327
     * 
 
328
     * @throws SAXException if the content handler
 
329
     *      or lexical handler throws an exception
 
330
     */
 
331
    public void convert(Nodes nodes) throws SAXException {
 
332
        
 
333
        if (nodes.size() == 1 && nodes.get(0) instanceof Document) {
 
334
            convert((Document) nodes.get(0));
 
335
        }
 
336
        else {
 
337
            locator = new LocatorImpl();
 
338
            contentHandler.setDocumentLocator(locator);
 
339
            contentHandler.startDocument();
 
340
            for (int i = 0; i < nodes.size(); i++) {
 
341
                process(nodes.get(i));
 
342
            }
 
343
            contentHandler.endDocument();
 
344
        }
 
345
        
 
346
    }
 
347
 
 
348
    
 
349
}