KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > nu > xom > xslt > XSLTHandler


1 /* Copyright 2002-2004 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.xslt;
23
24 import java.util.Stack JavaDoc;
25
26 import nu.xom.Attribute;
27 import nu.xom.Element;
28 import nu.xom.NamespaceConflictException;
29 import nu.xom.Node;
30 import nu.xom.NodeFactory;
31 import nu.xom.Nodes;
32 import nu.xom.ParentNode;
33 import nu.xom.XMLException;
34
35 import org.xml.sax.Attributes JavaDoc;
36 import org.xml.sax.ContentHandler JavaDoc;
37 import org.xml.sax.Locator JavaDoc;
38 import org.xml.sax.SAXException JavaDoc;
39 import org.xml.sax.ext.LexicalHandler JavaDoc;
40 import org.xml.sax.helpers.AttributesImpl JavaDoc;
41
42 /**
43  * <p>
44  * As currently designed this class is non-public and never
45  * reused. A new XSLTHandler is used for each call to transform().
46  * Therefore we do not actually need to reset. This is important
47  * because some XSLT processors call startDocument() and
48  * endDocument() and some don't, especially when the output
49  * of a transform is a document fragment.
50  * </p>
51  *
52  * @author Elliotte Rusty Harold
53  * @version 1.0
54  *
55  */

56 class XSLTHandler
57   implements ContentHandler JavaDoc, LexicalHandler JavaDoc {
58
59     private Nodes result;
60     private Stack JavaDoc parents;
61     private NodeFactory factory;
62     private StringBuffer JavaDoc buffer;
63     
64     
65     XSLTHandler(NodeFactory factory) {
66         this.factory = factory;
67         result = new Nodes();
68         parents = new Stack JavaDoc();
69         buffer = new StringBuffer JavaDoc();
70     }
71     
72     
73     Nodes getResult() {
74         flushText(); // to handle case where there's no endDocument
75
return result;
76     }
77     
78     
79     public void setDocumentLocator(Locator JavaDoc locator) {}
80     public void startDocument() {}
81     public void endDocument() {}
82   
83     private Element current;
84     
85     public void startElement(String JavaDoc namespaceURI, String JavaDoc localName,
86      String JavaDoc qualifiedName, Attributes JavaDoc attributes) {
87         
88         flushText();
89         
90         // mix namespaceDeclarations into attributes
91
for (int i = 0; i < attributes.getLength(); i++) {
92             namespaceDeclarations.addAttribute(
93               attributes.getURI(i),
94               attributes.getLocalName(i),
95               attributes.getQName(i),
96               attributes.getType(i),
97               attributes.getValue(i)
98             );
99         }
100         attributes = namespaceDeclarations;
101         
102         Element element
103           = factory.startMakingElement(qualifiedName, namespaceURI);
104         
105         if (parents.isEmpty()) {
106             // won't append until finishMakingElement()
107
current = element;
108         }
109         else {
110             ParentNode parent = (ParentNode) parents.peek();
111             parent.appendChild(element);
112         }
113         parents.push(element);
114         
115         // Attach the attributes
116
for (int i = 0; i < attributes.getLength(); i++) {
117             String JavaDoc attributeName = attributes.getQName(i);
118             // handle namespaces later
119
if (attributeName.equals("xmlns")
120               || attributeName.startsWith("xmlns:")) {
121                 continue;
122             }
123             String JavaDoc namespace = attributes.getURI(i);
124             String JavaDoc value = attributes.getValue(i);
125             
126             Nodes nodes = factory.makeAttribute(
127               attributeName,
128               namespace,
129               value,
130               Attribute.Type.UNDECLARED
131             );
132             for (int j=0; j < nodes.size(); j++) {
133                 Node node = nodes.get(j);
134                 if (node instanceof Attribute) {
135                     Attribute attribute = (Attribute) node;
136                     while (true) {
137                         try {
138                             element.addAttribute(attribute);
139                             break;
140                         }
141                         catch (NamespaceConflictException ex) {
142                             // According to section 7.1.3 of XSLT spec we
143
// need to remap the prefix here; ideally the
144
// XSLT processor should do this but many don't
145
// for instance, see
146
// http://nagoya.apache.org/bugzilla/show_bug.cgi?id=5389
147
attribute.setNamespace(
148                               "p"+attribute.getNamespacePrefix(),
149                               attribute.getNamespaceURI()
150                             );
151                         }
152                     }
153                 }
154                 else {
155                     element.appendChild(node);
156                 }
157             }
158         }
159         
160         // Attach any additional namespaces
161
for (int i = 0; i < attributes.getLength(); i++) {
162             String JavaDoc qName = attributes.getQName(i);
163             if (qName.startsWith("xmlns:")) {
164                 String JavaDoc namespaceName = attributes.getValue(i);
165                 String JavaDoc namespacePrefix = qName.substring(6);
166                 String JavaDoc currentValue
167                    = element.getNamespaceURI(namespacePrefix);
168                 if (!namespaceName.equals(currentValue)) {
169                     try {
170                         element.addNamespaceDeclaration(
171                           namespacePrefix, namespaceName);
172                     }
173                     catch (NamespaceConflictException ex) {
174                         // skip it; see attribset40 test case;
175
// This should only happen if an attribute's
176
// namespace conflicts with the element's
177
// namespace; in which case we already remapped
178
// the prefix when adding the attribute
179
}
180                 }
181             }
182             else if (qName.equals("xmlns")) {
183                 String JavaDoc namespaceName = attributes.getValue(i);
184                 if (namespaceName == null) { // Work around a Xalan bug
185
namespaceName = "";
186                 }
187                 String JavaDoc namespacePrefix = "";
188                 String JavaDoc currentValue
189                   = element.getNamespaceURI(namespacePrefix);
190                 if (!namespaceName.equals(currentValue)) {
191                     try {
192                         element.addNamespaceDeclaration(namespacePrefix,
193                          namespaceName);
194                     }
195                     catch (NamespaceConflictException ex) {
196                        // work around Bug 27937 in Xalan
197
// http://nagoya.apache.org/bugzilla/show_bug.cgi?id=27937
198
// Xalan sometimes use the XML namespace
199
// http://www.w3.org/XML/1998/namespace where it
200
// should use the empty string
201
if ("http://www.w3.org/XML/1998/namespace".equals(namespaceName)
202                          && "".equals(namespacePrefix)) {
203                             element.addNamespaceDeclaration("", "");
204                        }
205                     }
206                 }
207             }
208         }
209         
210         // reset namespaceDeclarations
211
namespaceDeclarations = new AttributesImpl JavaDoc();
212         
213     }
214   
215     
216     public void endElement(String JavaDoc namespaceURI, String JavaDoc localName,
217       String JavaDoc qualifiedName) {
218         
219         flushText();
220         Element element = (Element) parents.pop();
221         if (parents.isEmpty()) {
222             Nodes nodes = factory.finishMakingElement(current);
223             for (int i = 0; i < nodes.size(); i++) {
224                 result.append(nodes.get(i));
225             }
226             current = null;
227         }
228         else {
229             Nodes nodes = factory.finishMakingElement(element);
230             ParentNode parent = element.getParent();
231             element.detach();
232             for (int i = 0; i < nodes.size(); i++) {
233                 Node node = nodes.get(i);
234                 if (node instanceof Attribute) {
235                     ((Element) parent).addAttribute((Attribute) node);
236                 }
237                 else {
238                     parent.appendChild(node);
239                 }
240             }
241         }
242
243     }
244   
245     
246     public void characters(char[] text, int start, int length) {
247         buffer.append(text, start, length);
248     }
249  
250     
251     // accumulate all text that's in the buffer into a text node
252
private void flushText() {
253         if (buffer.length() > 0) {
254             Nodes text = factory.makeText(buffer.toString());
255             addToResultTree(text);
256             buffer = new StringBuffer JavaDoc();
257         }
258     }
259   
260     
261     public void ignorableWhitespace(char[] text, int start, int length) {
262         characters(text, start, length);
263     }
264   
265     
266     public void processingInstruction(String JavaDoc target, String JavaDoc data)
267       throws SAXException JavaDoc {
268
269         // See http://saxon.sourceforge.net/saxon6.5.2/extensibility.html#Writing-output-filters
270
// to understand why we need to work around Saxon here
271
if ("saxon:warning".equals(target)) {
272             throw new SAXException JavaDoc("continue");
273         }
274         else if ("javax.xml.transform.disable-output-escaping".equals(target)
275           || "javax.xml.transform.enable-output-escaping".equals(target)) {
276             // Xalan workaround
277
return;
278         }
279         
280         flushText();
281         // Xalan fails to split the ?> before passing such data to
282
// this method, so we have to do it
283
int position = data.indexOf("?>");
284         while (position != -1) {
285             data = data.substring(0, position) + "? >" + data.substring(position+2);
286             position = data.indexOf("?>");
287         }
288         Nodes nodes = factory.makeProcessingInstruction(target, data);
289         addToResultTree(nodes);
290
291     }
292
293     
294     private void addToResultTree(Nodes nodes) {
295         
296         if (parents.isEmpty()) {
297             for (int i = 0; i < nodes.size(); i++) {
298                 result.append(nodes.get(i));
299             }
300         }
301         else {
302             ParentNode parent = (ParentNode) parents.peek();
303             for (int i = 0; i < nodes.size(); i++) {
304                 Node node = nodes.get(i);
305                 if (node instanceof Attribute) {
306                     ((Element) parent).addAttribute((Attribute) node);
307                 }
308                 else {
309                     parent.appendChild(node);
310                 }
311             }
312         }
313         
314     }
315
316     
317     public void endPrefixMapping(String JavaDoc prefix) {}
318     
319     
320     private AttributesImpl JavaDoc namespaceDeclarations = new AttributesImpl JavaDoc();
321     
322     public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri) {
323         
324        if ("".equals(prefix)) {
325            namespaceDeclarations.addAttribute("", "xmlns", "xmlns", "CDATA", uri);
326        }
327        else {
328            namespaceDeclarations.addAttribute("", "xmlns:" + prefix, "xmlns:" + prefix, "CDATA", uri);
329        }
330         
331     }
332
333     
334     public void skippedEntity(String JavaDoc name) {
335         flushText();
336         throw new XMLException("Could not resolve entity " + name);
337     }
338     
339     
340     // LexicalHandler events
341
public void startCDATA() {}
342     public void endCDATA() {}
343     public void startDTD(String JavaDoc name, String JavaDoc publicID, String JavaDoc systemID) {}
344     public void endDTD() {}
345     public void startEntity(String JavaDoc name) {}
346     public void endEntity(String JavaDoc name) {}
347
348     
349     public void comment(char[] text, int start, int length) {
350         
351         flushText();
352         
353         String JavaDoc data = new String JavaDoc(text, start, length);
354         // Xalan should add spaces as necessary to split up double hyphens
355
// in comments but it doesn't
356
int position = data.indexOf("--");
357         while (position != -1) {
358             data = data.substring(0, position) + "- -" + data.substring(position+2);
359             position = data.indexOf("--");
360         }
361         if (data.endsWith("-")) data += ' ';
362         
363         addToResultTree(factory.makeComment(data));
364         
365     }
366      
367     
368 }
Popular Tags