OSDN Git Service

2006-08-14 Mark Wielaard <mark@klomp.org>
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / xml / transform / TransformerImpl.java
1 /* TransformerImpl.java -- 
2    Copyright (C) 2004,2005,2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38 package gnu.xml.transform;
39
40 import java.io.BufferedOutputStream;
41 import java.io.FileOutputStream;
42 import java.io.IOException;
43 import java.io.OutputStream;
44 import java.io.UnsupportedEncodingException;
45 import java.io.Writer;
46 import java.net.MalformedURLException;
47 import java.net.UnknownServiceException;
48 import java.net.URL;
49 import java.net.URLConnection;
50 import java.util.Collection;
51 import java.util.Iterator;
52 import java.util.LinkedList;
53 import java.util.List;
54 import java.util.Properties;
55 import java.util.StringTokenizer;
56 import javax.xml.namespace.QName;
57 import javax.xml.transform.ErrorListener;
58 import javax.xml.transform.OutputKeys;
59 import javax.xml.transform.Result;
60 import javax.xml.transform.Source;
61 import javax.xml.transform.Transformer;
62 import javax.xml.transform.TransformerConfigurationException;
63 import javax.xml.transform.TransformerException;
64 import javax.xml.transform.URIResolver;
65 import javax.xml.transform.dom.DOMSource;
66 import javax.xml.transform.dom.DOMResult;
67 import javax.xml.transform.sax.SAXResult;
68 import javax.xml.transform.stream.StreamResult;
69 import org.w3c.dom.Document;
70 import org.w3c.dom.DocumentType;
71 import org.w3c.dom.DOMImplementation;
72 import org.w3c.dom.Node;
73 import org.w3c.dom.Text;
74 import org.xml.sax.ContentHandler;
75 import org.xml.sax.SAXException;
76 import org.xml.sax.ext.LexicalHandler;
77 import gnu.xml.dom.DomDoctype;
78 import gnu.xml.dom.DomDocument;
79 import gnu.xml.dom.ls.WriterOutputStream;
80
81 /**
82  * The transformation process for a given stylesheet.
83  *
84  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
85  */
86 class TransformerImpl
87   extends Transformer
88 {
89
90   final TransformerFactoryImpl factory;
91   final Stylesheet stylesheet;
92   URIResolver uriResolver;
93   ErrorListener errorListener;
94   Properties outputProperties;
95
96   TransformerImpl(TransformerFactoryImpl factory,
97                   Stylesheet stylesheet,
98                   Properties outputProperties)
99     throws TransformerConfigurationException
100   {
101     this.factory = factory;
102     uriResolver = factory.userResolver;
103     errorListener = factory.userListener;
104     this.stylesheet = stylesheet;
105     this.outputProperties = outputProperties;
106     if (stylesheet != null)
107       {
108         // Set up parameter context for this transformer
109         stylesheet.bindings.push(Bindings.PARAM);
110       }
111   }
112
113   public void transform(Source xmlSource, Result outputTarget)
114     throws TransformerException
115   {
116     // Get the source tree
117     DOMSource source;
118     synchronized (factory.resolver)
119       {
120         factory.resolver.setUserResolver(uriResolver);
121         factory.resolver.setUserListener(errorListener);
122         source = factory.resolver.resolveDOM(xmlSource, null, null);
123       }
124     Node context = source.getNode();
125     Document doc = (context instanceof Document) ? (Document) context :
126       context.getOwnerDocument();
127     if (doc instanceof DomDocument)
128       {
129         // Suppress mutation events
130         ((DomDocument) doc).setBuilding(true);
131       }
132     // Get the result tree
133     Node parent = null, nextSibling = null;
134     if (outputTarget instanceof DOMResult)
135       {
136         DOMResult dr = (DOMResult) outputTarget;
137         parent = dr.getNode();
138         nextSibling = dr.getNextSibling();
139
140         Document rdoc = (parent instanceof Document) ? (Document) parent :
141           parent.getOwnerDocument();
142         if (rdoc instanceof DomDocument)
143           {
144             // Suppress mutation events and allow multiple root elements
145             DomDocument drdoc = (DomDocument) rdoc;
146             drdoc.setBuilding(true);
147             drdoc.setCheckWellformedness(false);
148           }
149       }
150     boolean created = false;
151     // Transformation
152     if (stylesheet != null)
153       {
154         if (parent == null)
155           {
156             // Create a new document to hold the result
157             DomDocument resultDoc = new DomDocument();
158             resultDoc.setBuilding(true);
159             resultDoc.setCheckWellformedness(false);
160             parent = resultDoc;
161             created = true;
162           }
163         // Make a copy of the source node, and strip it
164         context = context.cloneNode(true);
165         strip(stylesheet, context);
166         // XSLT transformation
167         try
168           {
169             // Set output properties in the underlying stylesheet
170             ((TransformerOutputProperties) outputProperties).apply();
171             stylesheet.initTopLevelVariables(context);
172             TemplateNode t = stylesheet.getTemplate(null, context, false);
173             if (t != null)
174               {
175                 stylesheet.current = context;
176                 t.apply(stylesheet, null, context, 1, 1, parent, nextSibling);
177               }
178           }
179         catch (TransformerException e)
180           {
181             // Done transforming, reset document
182             if (doc instanceof DomDocument)
183               ((DomDocument) doc).setBuilding(false);
184             throw e;
185           }
186       }
187     else
188       {
189         // Identity transform
190         Node clone = context.cloneNode(true);
191         if (context.getNodeType() != Node.DOCUMENT_NODE)
192           {
193             Document resultDoc;
194             if (parent == null)
195               {
196                 // Create a new document to hold the result
197                 DomDocument rd = new DomDocument();
198                 rd.setBuilding(true);
199                 rd.setCheckWellformedness(false);
200                 parent = resultDoc = rd;
201                 created = true;
202               }
203             else
204               {
205                 resultDoc = (parent instanceof Document) ?
206                   (Document) parent :
207                   parent.getOwnerDocument();
208               }
209             Document sourceDoc = context.getOwnerDocument();
210             if (sourceDoc != resultDoc)
211               clone = resultDoc.adoptNode(clone);
212             if (nextSibling != null)
213               parent.insertBefore(clone, nextSibling);
214             else
215               parent.appendChild(clone);
216           }
217         else
218           {
219             // Cannot append document to another tree
220             parent = clone;
221             created = true;
222           }
223       }
224     String method = outputProperties.getProperty(OutputKeys.METHOD);
225     int outputMethod = "html".equals(method) ? Stylesheet.OUTPUT_HTML :
226       "text".equals(method) ? Stylesheet.OUTPUT_TEXT :
227       Stylesheet.OUTPUT_XML;
228     String encoding = outputProperties.getProperty(OutputKeys.ENCODING);
229     String publicId = outputProperties.getProperty(OutputKeys.DOCTYPE_PUBLIC);
230     String systemId = outputProperties.getProperty(OutputKeys.DOCTYPE_SYSTEM);
231     String version = outputProperties.getProperty(OutputKeys.VERSION);
232     boolean omitXmlDeclaration = 
233       "yes".equals(outputProperties.getProperty(OutputKeys.OMIT_XML_DECLARATION));
234     boolean standalone = 
235       "yes".equals(outputProperties.getProperty(OutputKeys.STANDALONE));
236     String mediaType = outputProperties.getProperty(OutputKeys.MEDIA_TYPE);
237     String cdataSectionElements =
238       outputProperties.getProperty(OutputKeys.CDATA_SECTION_ELEMENTS);
239     boolean indent =
240       "yes".equals(outputProperties.getProperty(OutputKeys.INDENT));
241     if (created && parent instanceof DomDocument)
242       {
243         // Discover document element
244         DomDocument resultDoc = (DomDocument) parent;
245         Node root = resultDoc.getDocumentElement();
246         // Add doctype if specified
247         if (publicId != null || systemId != null)
248           {
249             if (root != null)
250               {
251                 // We must know the name of the root element to
252                 // create the document type
253                 DocumentType doctype = new DomDoctype(resultDoc,
254                                                       root.getNodeName(),
255                                                       publicId,
256                                                       systemId);
257                 resultDoc.insertBefore(doctype, root);
258               }
259           }
260         resultDoc.setBuilding(false);
261         resultDoc.setCheckWellformedness(true);
262       }
263     else if (publicId != null || systemId != null)
264       {
265         switch (parent.getNodeType())
266           {
267           case Node.DOCUMENT_NODE:
268           case Node.DOCUMENT_FRAGMENT_NODE:
269             Document resultDoc = (parent instanceof Document) ?
270               (Document) parent :
271               parent.getOwnerDocument();
272             DOMImplementation impl = resultDoc.getImplementation();
273             Node root = resultDoc.getDocumentElement();
274             if (root != null)
275               {
276                 DocumentType doctype =
277                   impl.createDocumentType(root.getNodeName(),
278                                           publicId,
279                                           systemId);
280                 resultDoc.insertBefore(doctype, root);
281               }
282           }
283       }
284     if (version != null)
285       parent.setUserData("version", version, stylesheet);
286     if (omitXmlDeclaration)
287       parent.setUserData("omit-xml-declaration", "yes", stylesheet);
288     if (standalone)
289       parent.setUserData("standalone", "yes", stylesheet);
290     if (mediaType != null)
291       parent.setUserData("media-type", mediaType, stylesheet);
292     if (cdataSectionElements != null)
293       {
294         List list = new LinkedList();
295         StringTokenizer st = new StringTokenizer(cdataSectionElements);
296         while (st.hasMoreTokens())
297           {
298             String name = st.nextToken();
299             String localName = name;
300             String uri = null;
301             String prefix = null;
302             int ci = name.indexOf(':');
303             if (ci != -1)
304               {
305                 // Use namespaces defined on xsl:output node to resolve
306                 // namespaces for QName
307                 prefix = name.substring(0, ci);
308                 localName = name.substring(ci + 1);
309                 uri = stylesheet.output.lookupNamespaceURI(prefix);
310               }
311             list.add(new QName(uri, localName, prefix));
312           }
313         if (!list.isEmpty())
314           {
315             Document resultDoc = (parent instanceof Document) ?
316               (Document) parent :
317               parent.getOwnerDocument();
318             convertCdataSectionElements(resultDoc, parent, list);
319           }
320       }
321     if (indent)
322       {
323         if (created && parent instanceof DomDocument)
324           {
325             DomDocument domDoc = (DomDocument) parent;
326             domDoc.setBuilding(true);
327             domDoc.setCheckWellformedness(false);
328           }
329         parent.normalize();
330         strip(stylesheet, parent);
331         Document resultDoc = (parent instanceof Document) ?
332           (Document) parent :
333           parent.getOwnerDocument();
334         reindent(resultDoc, parent, 0);
335         if (created && parent instanceof DomDocument)
336           {
337             DomDocument domDoc = (DomDocument) parent;
338             domDoc.setBuilding(false);
339             domDoc.setCheckWellformedness(true);
340           }
341       }
342     // Render result to the target device
343     if (outputTarget instanceof DOMResult)
344       {
345         if (created)
346           {
347             DOMResult dr = (DOMResult) outputTarget;
348             dr.setNode(parent);
349             dr.setNextSibling(null);
350           }
351       }
352     else if (outputTarget instanceof StreamResult)
353       {
354         StreamResult sr = (StreamResult) outputTarget;
355         IOException ex = null;
356         try
357           {
358             writeStreamResult(parent, sr, outputMethod, encoding);
359           }
360         catch (UnsupportedEncodingException e)
361           {
362             try
363               {
364                 writeStreamResult(parent, sr, outputMethod, "UTF-8");
365               }
366             catch (IOException e2)
367               {
368                 ex = e2;
369               }
370           }
371         catch (IOException e)
372           {
373             ex = e;
374           }
375         if (ex != null)
376           {
377             if (errorListener != null)
378               errorListener.error(new TransformerException(ex));
379             else
380               ex.printStackTrace(System.err);
381           }
382       }
383     else if (outputTarget instanceof SAXResult)
384       {
385         SAXResult sr = (SAXResult) outputTarget;
386         try
387           {
388             ContentHandler ch = sr.getHandler();
389             LexicalHandler lh = sr.getLexicalHandler();
390             if (lh == null && ch instanceof LexicalHandler)
391               lh = (LexicalHandler) ch;
392             SAXSerializer serializer = new SAXSerializer();
393             serializer.serialize(parent, ch, lh);
394           }
395         catch (SAXException e)
396           {
397             if (errorListener != null)
398               errorListener.error(new TransformerException(e));
399             else
400               e.printStackTrace(System.err);
401           }
402       }
403   }
404
405   /**
406    * Strip whitespace from the source tree.
407    */
408   static boolean strip(Stylesheet stylesheet, Node node)
409     throws TransformerConfigurationException
410   {
411     short nt = node.getNodeType();
412     if (nt == Node.ENTITY_REFERENCE_NODE)
413       {
414         // Replace entity reference with its content
415         Node parent = node.getParentNode();
416         Node nextSibling = node.getNextSibling();
417         Node child = node.getFirstChild();
418         while (child != null)
419           {
420             Node next = child.getNextSibling();
421             node.removeChild(child);
422             if (nextSibling != null)
423               parent.insertBefore(child, nextSibling);
424             else
425               parent.appendChild(child);
426             child = next;
427           }
428         return true;
429       }
430     if (nt == Node.TEXT_NODE || nt == Node.CDATA_SECTION_NODE)
431       {
432         // Denormalize text into whitespace and non-whitespace nodes
433         String text = node.getNodeValue();
434         String[] tokens = tokenizeWhitespace(text);
435         if (tokens.length > 1)
436           {
437             node.setNodeValue(tokens[0]);
438             Node parent = node.getParentNode();
439             Node nextSibling = node.getNextSibling();
440             Document doc = node.getOwnerDocument();
441             for (int i = 1; i < tokens.length; i++)
442               {
443                 Node newChild = (nt == Node.CDATA_SECTION_NODE) ?
444                   doc.createCDATASection(tokens[i]) :
445                   doc.createTextNode(tokens[i]);
446                 if (nextSibling != null)
447                   parent.insertBefore(newChild, nextSibling);
448                 else
449                   parent.appendChild(newChild);
450               }
451           }
452         return !stylesheet.isPreserved((Text) node, true);
453       }
454     else
455       {
456         Node child = node.getFirstChild();
457         while (child != null)
458           {
459             boolean remove = strip(stylesheet, child);
460             Node next = child.getNextSibling();
461             if (remove)
462               node.removeChild(child);
463             child = next;
464           }
465       }
466     return false;
467   }
468
469   /**
470    * Tokenize the specified text into contiguous whitespace-only and
471    * non-whitespace chunks.
472    */
473   private static String[] tokenizeWhitespace(String text)
474   {
475     int len = text.length();
476     int start = 0, end = len - 1;
477     // Find index of text start
478     for (int i = 0; i < len; i++)
479       {
480         char c = text.charAt(i);
481         boolean whitespace = (c == ' ' || c == '\n' || c == '\t' || c == '\r');
482         if (whitespace)
483           start++;
484         else
485           break;
486       }
487     if (start == end) // all whitespace
488       return new String[] { text };
489     // Find index of text end
490     for (int i = end; i > start; i--)
491       {
492         char c = text.charAt(i);
493         boolean whitespace = (c == ' ' || c == '\n' || c == '\t' || c == '\r');
494         if (whitespace)
495           end--;
496         else
497           break;
498       }
499     if (start == 0 && end == len - 1) // all non-whitespace
500       return new String[] { text };
501     // whitespace, then text, then whitespace
502     String[] ret = (start > 0 && end < len - 1) ?
503       new String[3] : new String[2];
504     int i = 0;
505     if (start > 0)
506       ret[i++] = text.substring(0, start);
507     ret[i++] = text.substring(start, end + 1);
508     if (end < len - 1)
509       ret[i++] = text.substring(end + 1);
510     return ret;
511   }
512
513   /**
514    * Obtain a suitable output stream for writing the result to,
515    * and use the StreamSerializer to write the result tree to the stream.
516    */
517   void writeStreamResult(Node node, StreamResult sr, int outputMethod,
518                          String encoding)
519     throws IOException
520   {
521     OutputStream out = null;
522     boolean created = false;
523     try
524       {
525         out = sr.getOutputStream();
526         if (out == null)
527           {
528             Writer writer = sr.getWriter();
529             if (writer != null)
530               out = new WriterOutputStream(writer);
531           }
532         if (out == null)
533           {
534             String systemId = sr.getSystemId();
535             try
536               {
537                 URL url = new URL(systemId);
538                 URLConnection connection = url.openConnection();
539                 // We need to call setDoInput(false), because our
540                 // implementation of the file protocol allows writing
541                 // (unlike Sun), but it will fail with a FileNotFoundException
542                 // if we also open the connection for input and the output
543                 // file doesn't yet exist.
544                 connection.setDoInput(false);
545                 connection.setDoOutput(true);
546                 out = connection.getOutputStream();
547               }
548             catch (MalformedURLException e)
549               {
550                 out = new FileOutputStream(systemId);
551               }
552             catch (UnknownServiceException e)
553               {
554                 URL url = new URL(systemId);
555                 out = new FileOutputStream(url.getPath());
556               }
557             created = true;
558           }
559         out = new BufferedOutputStream(out);
560         StreamSerializer serializer =
561           new StreamSerializer(outputMethod, encoding, null);
562         if (stylesheet != null)
563           {
564             Collection celem = stylesheet.outputCdataSectionElements;
565             serializer.setCdataSectionElements(celem);
566           }
567         serializer.serialize(node, out);
568         out.flush();
569       }
570     finally
571       {
572         try
573           {
574             if (out != null && created)
575               out.close();
576           }
577         catch (IOException e)
578           {
579             if (errorListener != null)
580               {
581                 try
582                   {
583                     errorListener.error(new TransformerException(e));
584                   }
585                 catch (TransformerException e2)
586                   {
587                     e2.printStackTrace(System.err);
588                   }
589               }
590             else
591               e.printStackTrace(System.err);
592           }
593       }
594   }
595
596   void copyChildren(Document dstDoc, Node src, Node dst)
597   {
598     Node srcChild = src.getFirstChild();
599     while (srcChild != null)
600       {
601         Node dstChild = dstDoc.adoptNode(srcChild);
602         dst.appendChild(dstChild);
603         srcChild = srcChild.getNextSibling();
604       }
605   }
606
607   public void setParameter(String name, Object value)
608   {
609     if (stylesheet != null)
610       stylesheet.bindings.set(new QName(null, name), value, Bindings.PARAM);
611   }
612
613   public Object getParameter(String name)
614   {
615     if (stylesheet != null)
616       return stylesheet.bindings.get(new QName(null, name), null, 1, 1);
617     return null;
618   }
619
620   public void clearParameters()
621   {
622     if (stylesheet != null)
623       {
624         stylesheet.bindings.pop(Bindings.PARAM);
625         stylesheet.bindings.push(Bindings.PARAM);
626       }
627   }
628
629   public void setURIResolver(URIResolver resolver)
630   {
631     uriResolver = resolver;
632   }
633
634   public URIResolver getURIResolver()
635   {
636     return uriResolver;
637   }
638
639   public void setOutputProperties(Properties oformat)
640     throws IllegalArgumentException
641   {
642     if (oformat == null)
643       outputProperties.clear();
644     else
645       outputProperties.putAll(oformat);
646   }
647
648   public Properties getOutputProperties()
649   {
650     return (Properties) outputProperties.clone();
651   }
652
653   public void setOutputProperty(String name, String value)
654     throws IllegalArgumentException
655   {
656     outputProperties.put(name, value);
657   }
658
659   public String getOutputProperty(String name)
660     throws IllegalArgumentException
661   {
662     return outputProperties.getProperty(name);
663   }
664
665   public void setErrorListener(ErrorListener listener)
666   {
667     errorListener = listener;
668   }
669
670   public ErrorListener getErrorListener()
671   {
672     return errorListener;
673   }
674
675   static final String INDENT_WHITESPACE = "  ";
676
677   /*
678    * Apply indent formatting to the given tree.
679    */
680   void reindent(Document doc, Node node, int offset)
681   {
682     if (node.hasChildNodes())
683       {
684         boolean markupContent = false;
685         boolean textContent = false;
686         List children = new LinkedList();
687         Node ctx = node.getFirstChild();
688         while (ctx != null)
689           {
690             switch (ctx.getNodeType())
691               {
692               case Node.ELEMENT_NODE:
693               case Node.PROCESSING_INSTRUCTION_NODE:
694               case Node.DOCUMENT_TYPE_NODE:
695                 markupContent = true;
696                 break;
697               case Node.TEXT_NODE:
698               case Node.CDATA_SECTION_NODE:
699               case Node.ENTITY_REFERENCE_NODE:
700               case Node.COMMENT_NODE:
701                 textContent = true;
702                 break;
703               }
704             children.add(ctx);
705             ctx = ctx.getNextSibling();
706           }
707         if (markupContent)
708           {
709             if (textContent)
710               {
711                 // XXX handle mixed content differently?
712               }
713             int nodeType = node.getNodeType();
714             if (nodeType == Node.DOCUMENT_NODE)
715               {
716                 for (Iterator i = children.iterator(); i.hasNext(); )
717                   {
718                     ctx = (Node) i.next();
719                     reindent(doc, ctx, offset);
720                   }
721               }
722             else
723               {
724                 StringBuffer buf = new StringBuffer();
725                 buf.append('\n');
726                 for (int i = 0; i < offset + 1; i++)
727                   buf.append(INDENT_WHITESPACE);
728                 String ws = buf.toString();
729                 for (Iterator i = children.iterator(); i.hasNext(); )
730                   {
731                     ctx = (Node) i.next();
732                     node.insertBefore(doc.createTextNode(ws), ctx);
733                     reindent(doc, ctx, offset + 1);
734                   }
735                 buf = new StringBuffer();
736                 buf.append('\n');
737                 for (int i = 0; i < offset; i++)
738                   buf.append(INDENT_WHITESPACE);
739                 ws = buf.toString();
740                 node.appendChild(doc.createTextNode(ws));
741               }
742           }
743       }
744   }
745
746   /**
747    * Converts the text node children of any cdata-section-elements in the
748    * tree to CDATA section nodes.
749    */
750   void convertCdataSectionElements(Document doc, Node node, List list)
751   {
752     if (node.getNodeType() == Node.ELEMENT_NODE)
753       {
754         boolean match = false;
755         for (Iterator i = list.iterator(); i.hasNext(); )
756           {
757             QName qname = (QName) i.next();
758             if (match(qname, node))
759               {
760                 match = true;
761                 break;
762               }
763           }
764         if (match)
765           {
766             Node ctx = node.getFirstChild();
767             while (ctx != null)
768               {
769                 if (ctx.getNodeType() == Node.TEXT_NODE)
770                   {
771                     Node cdata = doc.createCDATASection(ctx.getNodeValue());
772                     node.replaceChild(cdata, ctx);
773                     ctx = cdata;
774                   }
775                 ctx = ctx.getNextSibling();
776               }
777           }
778       }
779     Node ctx = node.getFirstChild();
780     while (ctx != null)
781       {
782         if (ctx.hasChildNodes())
783           convertCdataSectionElements(doc, ctx, list);
784         ctx = ctx.getNextSibling();
785       }
786   }
787
788   boolean match(QName qname, Node node)
789   {
790     String ln1 = qname.getLocalPart();
791     String ln2 = node.getLocalName();
792     if (ln2 == null)
793       return ln1.equals(node.getNodeName());
794     else
795       {
796         String uri1 = qname.getNamespaceURI();
797         String uri2 = node.getNamespaceURI();
798         return (uri1.equals(uri2) && ln1.equals(ln2));
799       }
800   }
801
802 }