001    // $HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/framework/xml/XMLFragment.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005     Department of Geography, University of Bonn
006     and
007     lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035     ----------------------------------------------------------------------------*/
036    
037    package org.deegree.framework.xml;
038    
039    import java.io.File;
040    import java.io.IOException;
041    import java.io.InputStream;
042    import java.io.InputStreamReader;
043    import java.io.OutputStream;
044    import java.io.PushbackInputStream;
045    import java.io.PushbackReader;
046    import java.io.Reader;
047    import java.io.Serializable;
048    import java.io.StringWriter;
049    import java.io.Writer;
050    import java.net.MalformedURLException;
051    import java.net.URI;
052    import java.net.URISyntaxException;
053    import java.net.URL;
054    import java.util.HashMap;
055    import java.util.LinkedList;
056    import java.util.Map;
057    import java.util.NoSuchElementException;
058    import java.util.Properties;
059    import java.util.StringTokenizer;
060    
061    import javax.xml.parsers.DocumentBuilder;
062    import javax.xml.parsers.DocumentBuilderFactory;
063    import javax.xml.parsers.ParserConfigurationException;
064    import javax.xml.transform.OutputKeys;
065    import javax.xml.transform.Source;
066    import javax.xml.transform.Transformer;
067    import javax.xml.transform.TransformerConfigurationException;
068    import javax.xml.transform.TransformerException;
069    import javax.xml.transform.TransformerFactory;
070    import javax.xml.transform.dom.DOMSource;
071    import javax.xml.transform.stream.StreamResult;
072    import javax.xml.transform.stream.StreamSource;
073    
074    import org.apache.commons.httpclient.HttpClient;
075    import org.apache.commons.httpclient.methods.GetMethod;
076    import org.deegree.datatypes.QualifiedName;
077    import org.deegree.datatypes.xlink.SimpleLink;
078    import org.deegree.enterprise.WebUtils;
079    import org.deegree.framework.log.ILogger;
080    import org.deegree.framework.log.LoggerFactory;
081    import org.deegree.framework.util.BootLogger;
082    import org.deegree.framework.util.CharsetUtils;
083    import org.deegree.framework.util.FileUtils;
084    import org.deegree.framework.util.StringTools;
085    import org.deegree.model.feature.Messages;
086    import org.deegree.ogcbase.CommonNamespaces;
087    import org.w3c.dom.Document;
088    import org.w3c.dom.Element;
089    import org.w3c.dom.NamedNodeMap;
090    import org.w3c.dom.Node;
091    import org.xml.sax.InputSource;
092    import org.xml.sax.SAXException;
093    
094    /**
095     * An instance of <code>XMLFragment</code> encapsulates an underlying {@link Element} which acts as the root element of
096     * the document (which may be a fragment or a whole document).
097     * <p>
098     * Basically, <code>XMLFragment</code> provides easy loading and proper saving (automatically generated CDATA-elements
099     * for text nodes that need to be escaped) and acts as base class for all XML parsers in deegree.
100     * 
101     * TODO: automatically generated CDATA-elements are not implemented yet
102     * 
103     * <p>
104     * Additionally, <code>XMLFragment</code> tries to make the handling of relative paths inside the document's content as
105     * painless as possible. This means that after initialization of the <code>XMLFragment</code> with the correct SystemID
106     * (i.e. the URL of the document):
107     * <ul>
108     * <li>external parsed entities (in the DOCTYPE part) can use relative URLs; e.g. &lt;!ENTITY local SYSTEM
109     * "conf/wfs/wfs.cfg"&gt;</li>
110     * <li>application specific documents which extend <code>XMLFragment</code> can resolve relative URLs during parsing by
111     * calling the <code>resolve()</code> method</li>
112     * </ul>
113     * 
114     * @author <a href="mailto:tfr@users.sourceforge.net">Torsten Friebe </a>
115     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
116     * @author last edited by: $Author: jwilden $
117     * 
118     * @version $Revision: 23962 $, $Date: 2010-04-29 09:56:44 +0200 (Do, 29 Apr 2010) $
119     * 
120     * @see org.deegree.framework.xml.XMLTools
121     */
122    
123    public class XMLFragment implements Serializable {
124    
125        private static final long serialVersionUID = 8984447437613709386L;
126    
127        /**
128         * The namespace map containing the prefixes mapped to the namespaces.
129         */
130        protected static NamespaceContext nsContext = CommonNamespaces.getNamespaceContext();
131    
132        /**
133         * The xlink namespace
134         */
135        protected static final URI XLNNS = CommonNamespaces.XLNNS;
136    
137        private static final ILogger LOG = LoggerFactory.getLogger( XMLFragment.class );
138    
139        /**
140         * Use this URL as SystemID only if an <code>XMLFragment</code> cannot be pinpointed to a URL - in this case it may
141         * not use any relative references!
142         */
143        public static final String DEFAULT_URL = "http://www.deegree.org";
144    
145        private URL systemId;
146    
147        private Element rootElement;
148    
149        static {
150            LOG.logDebug( "DOM implementation in use (DocumentBuilderFactory): "
151                          + DocumentBuilderFactory.newInstance().getClass().getName() );
152            try {
153                LOG.logDebug( "DOM implementation in use (DocumentBuilder): "
154                              + DocumentBuilderFactory.newInstance().newDocumentBuilder().getClass().getName() );
155            } catch ( Exception e ) {
156                BootLogger.logError( "Error creating test DocumentBuilder instance.", e );
157            }
158        }
159    
160        /**
161         * Creates a new <code>XMLFragment</code> which is not initialized.
162         */
163        public XMLFragment() {
164            // nothing to do
165        }
166    
167        /**
168         * Creates a new <code>XMLFragment</code> which is loaded from the given <code>URL</code>.
169         * 
170         * @param url
171         * @throws IOException
172         * @throws SAXException
173         */
174        public XMLFragment( URL url ) throws IOException, SAXException {
175            load( url );
176        }
177    
178        /**
179         * Creates a new <code>XMLFragment</code> which is loaded from the given <code>File</code>.
180         * 
181         * @param file
182         *            the file to load from
183         * @throws SAXException
184         *             if the document could not be parsed
185         * @throws IOException
186         *             if the document could not be read
187         * @throws MalformedURLException
188         *             if the file cannot be transposed to a valid url
189         */
190        public XMLFragment( File file ) throws MalformedURLException, IOException, SAXException {
191            if ( file != null ) {
192                load( file.toURI().toURL() );
193            }
194        }
195    
196        /**
197         * Creates a new <code>XMLFragment</code> which is loaded from the given <code>Reader</code>.
198         * 
199         * @param reader
200         * @param systemId
201         *            this string should represent a URL that is related to the passed reader. If this URL is not available
202         *            or unknown, the string should contain the value of XMLFragment.DEFAULT_URL
203         * @throws SAXException
204         * @throws IOException
205         */
206        public XMLFragment( Reader reader, String systemId ) throws SAXException, IOException {
207            load( reader, systemId );
208        }
209    
210        /**
211         * Creates a new <code>XMLFragment</code> instance based on the submitted <code>Document</code>.
212         * 
213         * @param doc
214         * @param systemId
215         *            this string should represent a URL that is the source of the passed doc. If this URL is not available
216         *            or unknown, the string should contain the value of XMLFragment.DEFAULT_URL
217         * @throws MalformedURLException
218         *             if systemId is no valid and absolute <code>URL</code>
219         */
220        public XMLFragment( Document doc, String systemId ) throws MalformedURLException {
221            setRootElement( doc.getDocumentElement() );
222            setSystemId( systemId );
223        }
224    
225        /**
226         * Creates a new <code>XMLFragment</code> instance based on the submitted <code>Element</code>.
227         * 
228         * @param element
229         */
230        public XMLFragment( Element element ) {
231            setRootElement( element );
232        }
233    
234        /**
235         * Constructs an empty document with the given <code>QualifiedName</code> as root node.
236         * 
237         * @param elementName
238         *            if the name's namespace is set, the prefix should be set as well.
239         */
240        public XMLFragment( QualifiedName elementName ) {
241            try {
242                DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
243                if ( elementName.getNamespace() == null ) {
244                    rootElement = db.newDocument().createElement( elementName.getLocalName() );
245                } else {
246                    String pre = elementName.getPrefix();
247                    String ns = elementName.getNamespace().toString();
248                    if ( pre == null || pre.trim().length() == 0 ) {
249                        pre = "dummy";
250                        LOG.logWarning( StringTools.concat( 200, "Incorrect usage of deegree API,",
251                                                            " prefix of a root node was not ", "defined:\nNode name was ",
252                                                            elementName.getLocalName(), ", namespace was ", ns ) );
253                    }
254                    String name = StringTools.concat( 200, pre, ":", elementName.getLocalName() );
255                    rootElement = db.newDocument().createElementNS( ns, name );
256                    rootElement.getOwnerDocument().appendChild( rootElement );
257                }
258            } catch ( ParserConfigurationException e ) {
259                LOG.logError( "The parser seems to be misconfigured. Broken installation?", e );
260            }
261        }
262    
263        /**
264         * Returns the systemId (the URL of the <code>XMLFragment</code>).
265         * 
266         * @return the systemId
267         */
268        public URL getSystemId() {
269            return systemId;
270        }
271    
272        /**
273         * @param systemId
274         *            The systemId (physical location) to set (may be null).
275         * @throws MalformedURLException
276         */
277        public void setSystemId( String systemId )
278                                throws MalformedURLException {
279            if ( systemId != null ) {
280                this.systemId = new URL( systemId );
281            }
282        }
283    
284        /**
285         * @param systemId
286         *            The systemId (physical location) to set.
287         */
288        public void setSystemId( URL systemId ) {
289            this.systemId = systemId;
290        }
291    
292        /**
293         * Returns whether the document has a schema reference.
294         * 
295         * @return true, if the document has a schema reference, false otherwise
296         */
297        public boolean hasSchema() {
298            if ( this.rootElement.getAttribute( "xsi:schemaLocation" ) != null ) {
299                return true;
300            }
301            return false;
302        }
303    
304        /**
305         * Determines the namespace <code>URI</code>s and the bound schema <code>URL</code>s from the 'xsi:schemaLocation'
306         * attribute of the document element.
307         * 
308         * @return keys are URIs (namespaces), values are URLs (schema locations)
309         * @throws XMLParsingException
310         */
311        public Map<URI, URL> getAttachedSchemas()
312                                throws XMLParsingException {
313    
314            Map<URI, URL> schemaMap = new HashMap<URI, URL>();
315    
316            NamedNodeMap attrMap = rootElement.getAttributes();
317            Node schemaLocationAttr = attrMap.getNamedItem( "xsi:schemaLocation" );
318            if ( schemaLocationAttr == null ) {
319                return schemaMap;
320            }
321    
322            String target = schemaLocationAttr.getNodeValue();
323            StringTokenizer tokenizer = new StringTokenizer( target );
324    
325            while ( tokenizer.hasMoreTokens() ) {
326                URI nsURI = null;
327                String token = tokenizer.nextToken();
328                try {
329                    nsURI = new URI( token );
330                } catch ( URISyntaxException e ) {
331                    String msg = "Invalid 'xsi:schemaLocation' attribute: namespace " + token + "' is not a valid URI.";
332                    LOG.logError( msg );
333                    throw new XMLParsingException( msg );
334                }
335    
336                URL schemaURL = null;
337                try {
338                    token = tokenizer.nextToken();
339                    schemaURL = resolve( token );
340                } catch ( NoSuchElementException e ) {
341                    String msg = "Invalid 'xsi:schemaLocation' attribute: namespace '" + nsURI
342                                 + "' is missing a schema URL.";
343                    LOG.logError( msg );
344                    throw new XMLParsingException( msg );
345                } catch ( MalformedURLException ex ) {
346                    String msg = "Invalid 'xsi:schemaLocation' attribute: '" + token + "' for namespace '" + nsURI
347                                 + "' could not be parsed as URL.";
348                    throw new XMLParsingException( msg );
349                }
350                schemaMap.put( nsURI, schemaURL );
351            }
352            return schemaMap;
353        }
354    
355        /**
356         * Initializes the <code>XMLFragment</code> with the content from the given <code>URL</code>. Sets the SystemId,
357         * too.
358         * 
359         * @param url
360         * @throws IOException
361         * @throws SAXException
362         */
363        public void load( URL url )
364                                throws IOException, SAXException {
365            if ( url == null ) {
366                throw new IllegalArgumentException( "The given url may not be null" );
367            }
368    
369            String uri = url.toExternalForm();
370            if ( !uri.startsWith( "http://" ) ) {
371                load( url.openStream(), uri );
372                return;
373            }
374            // else try to use a proxy
375            HttpClient client = new HttpClient();
376            WebUtils.enableProxyUsage( client, url );
377            GetMethod get = new GetMethod( url.toExternalForm() );
378            client.executeMethod( get );
379            load( get.getResponseBodyAsStream(), uri );
380        }
381    
382        /**
383         * Initializes the <code>XMLFragment</code> with the content from the given <code>InputStream</code>. Sets the
384         * SystemId, too.
385         * 
386         * @param istream
387         * @param systemId
388         *            cannot be null. This string should represent a URL that is related to the passed istream. If this URL
389         *            is not available or unknown, the string should contain the value of XMLFragment.DEFAULT_URL
390         * @throws SAXException
391         * @throws IOException
392         * @throws XMLException
393         * @throws NullPointerException
394         */
395        public void load( InputStream istream, String systemId )
396                                throws SAXException, IOException, XMLException {
397    
398            PushbackInputStream pbis = new PushbackInputStream( istream, 1024 );
399            String encoding = readEncoding( pbis );
400    
401            if ( LOG.isDebug() ) {
402                LOG.logDebug( "Reading XMLFragment " + systemId + " with encoding ", encoding );
403            }
404    
405            InputStreamReader isr = new InputStreamReader( pbis, encoding );
406            load( isr, systemId );
407        }
408    
409        /**
410         * reads the encoding of a XML document from its header. If no header available
411         * <code>CharsetUtils.getSystemCharset()</code> will be returned
412         * 
413         * @param pbis
414         * @return encoding of a XML document
415         * @throws IOException
416         */
417        private String readEncoding( PushbackInputStream pbis )
418                                throws IOException {
419            byte[] b = new byte[80];
420            String s = "";
421            int rd = 0;
422    
423            LinkedList<byte[]> bs = new LinkedList<byte[]>();
424            LinkedList<Integer> rds = new LinkedList<Integer>();
425            while ( rd < 80 ) {
426                rds.addFirst( pbis.read( b ) );
427                if ( rds.peek() == -1 ) {
428                    rds.poll();
429                    break;
430                }
431                rd += rds.peek();
432                s += new String( b, 0, rds.peek() ).toLowerCase();
433                bs.addFirst( b );
434                b = new byte[80];
435            }
436    
437            String encoding = CharsetUtils.getSystemCharset();
438            if ( s.indexOf( "?>" ) > -1 ) {
439                int p = s.indexOf( "encoding=" );
440                if ( p > -1 ) {
441                    StringBuffer sb = new StringBuffer();
442                    int k = p + 1 + "encoding=".length();
443                    while ( s.charAt( k ) != '"' && s.charAt( k ) != '\'' ) {
444                        sb.append( s.charAt( k++ ) );
445                    }
446                    encoding = sb.toString();
447                }
448            }
449            while ( !bs.isEmpty() ) {
450                pbis.unread( bs.poll(), 0, rds.poll() );
451            }
452    
453            return encoding;
454        }
455    
456        /**
457         * Initializes the <code>XMLFragment</code> with the content from the given <code>Reader</code>. Sets the SystemId,
458         * too.
459         * 
460         * @param reader
461         * @param systemId
462         *            can not be null. This string should represent a URL that is related to the passed reader. If this URL
463         *            is not available or unknown, the string should contain the value of XMLFragment.DEFAULT_URL
464         * @throws SAXException
465         * @throws IOException
466         * @throws NullPointerException
467         */
468        public void load( Reader reader, String systemId )
469                                throws SAXException, IOException {
470    
471            PushbackReader pbr = new PushbackReader( reader, 1024 );
472            int c = pbr.read();
473            if ( c != 65279 && c != 65534 ) {
474                // no BOM! push char back into reader
475                pbr.unread( c );
476            }
477    
478            InputSource source = new InputSource( pbr );
479            if ( systemId == null ) {
480                throw new NullPointerException( "'systemId' must not be null!" );
481            }
482            setSystemId( systemId );
483            DocumentBuilder builder = XMLTools.getDocumentBuilder();
484            Document doc = builder.parse( source );
485            setRootElement( doc.getDocumentElement() );
486        }
487    
488        /**
489         * @param rootElement
490         */
491        public void setRootElement( Element rootElement ) {
492            this.rootElement = rootElement;
493        }
494    
495        /**
496         * @return the element
497         */
498        public Element getRootElement() {
499            return rootElement;
500        }
501    
502        /**
503         * Resolves the given URL (which may be relative) against the SystemID of the <code>XMLFragment</code> into a
504         * <code>URL</code> (which is always absolute).
505         * 
506         * @param url
507         * @return the resolved URL object
508         * @throws MalformedURLException
509         */
510        public URL resolve( String url )
511                                throws MalformedURLException {
512            LOG.logDebug( StringTools.concat( 200, "Resolving URL '", url, "' against SystemID '", systemId,
513                                              "' of XMLFragment" ) );
514    
515            URL resolvedURL = FileUtils.resolt( systemId, url );
516    
517            LOG.logDebug( StringTools.concat( 100, "-> resolvedURL: '", resolvedURL, "'" ) );
518            return resolvedURL;
519        }
520    
521        /**
522         * Writes the <code>XMLFragment</code> instance to the given <code>Writer</code> using the default system encoding
523         * and adding CDATA-sections in for text-nodes where needed.
524         * 
525         * TODO: Add code for CDATA safety.
526         * 
527         * @param writer
528         */
529        public void write( Writer writer ) {
530            Properties properties = new Properties();
531            properties.setProperty( OutputKeys.ENCODING, CharsetUtils.getSystemCharset() );
532            write( writer, properties );
533        }
534    
535        /**
536         * Writes the <code>XMLFragment</code> instance to the given <code>Writer</code> using the specified
537         * <code>OutputKeys</code>.
538         * 
539         * @param writer
540         *            cannot be null
541         * @param outputProperties
542         *            output properties for the <code>Transformer</code> that is used to serialize the document
543         * 
544         *            see javax.xml.OutputKeys
545         */
546        public void write( Writer writer, Properties outputProperties ) {
547            try {
548                Source source = new DOMSource( rootElement );
549                Transformer transformer = TransformerFactory.newInstance().newTransformer();
550                if ( outputProperties != null ) {
551                    transformer.setOutputProperties( outputProperties );
552                }
553                transformer.transform( source, new StreamResult( writer ) );
554            } catch ( TransformerConfigurationException e ) {
555                LOG.logError( e.getMessage(), e );
556                throw new XMLException( e );
557            } catch ( Exception e ) {
558                LOG.logError( e.getMessage(), e );
559                throw new XMLException( e );
560            }
561        }
562    
563        /**
564         * Writes the <code>XMLFragment</code> instance to the given <code>OutputStream</code> using the default system
565         * encoding and adding CDATA-sections in for text-nodes where needed.
566         * 
567         * TODO: Add code for CDATA safety.
568         * 
569         * @param os
570         */
571        public void write( OutputStream os ) {
572            Properties properties = new Properties();
573            properties.setProperty( OutputKeys.ENCODING, CharsetUtils.getSystemCharset() );
574            write( os, properties );
575        }
576    
577        /**
578         * Writes the <code>XMLFragment</code> instance to the given <code>OutputStream</code> using the specified
579         * <code>OutputKeys</code> which allow complete control of the generated output.
580         * 
581         * @param os
582         *            cannot be null
583         * @param outputProperties
584         *            output properties for the <code>Transformer</code> used to serialize the document
585         * 
586         * @see javax.xml.transform.OutputKeys
587         */
588        public void write( OutputStream os, Properties outputProperties ) {
589            try {
590                Source source = new DOMSource( rootElement );
591                Transformer transformer = TransformerFactory.newInstance().newTransformer();
592                if ( outputProperties != null ) {
593                    transformer.setOutputProperties( outputProperties );
594                }
595                transformer.transform( source, new StreamResult( os ) );
596            } catch ( TransformerConfigurationException e ) {
597                LOG.logError( e.getMessage(), e );
598                throw new XMLException( e );
599            } catch ( Exception e ) {
600                LOG.logError( e.getMessage(), e );
601                throw new XMLException( e );
602            }
603        }
604    
605        /**
606         * Writes the <code>XMLFragment</code> instance to the given <code>OutputStream</code> using indentation so it may
607         * be read easily.
608         * 
609         * @param os
610         * @throws TransformerException
611         */
612        public void prettyPrint( OutputStream os )
613                                throws TransformerException {
614            InputStream xsl = XMLFragment.class.getResourceAsStream( "PrettyPrinter.xsl" );
615            Transformer transformer = TransformerFactory.newInstance().newTransformer( new StreamSource( xsl ) );
616            transformer.transform( new DOMSource( rootElement ), new StreamResult( os ) );
617        }
618    
619        /**
620         * Writes the <code>XMLFragment</code> instance to the given <code>Writer</code> using indentation so it may be read
621         * easily.
622         * 
623         * @param writer
624         * @throws TransformerException
625         */
626        public void prettyPrint( Writer writer )
627                                throws TransformerException {
628            InputStream xsl = XMLFragment.class.getResourceAsStream( "PrettyPrinter.xsl" );
629            Transformer transformer = TransformerFactory.newInstance().newTransformer( new StreamSource( xsl ) );
630            transformer.transform( new DOMSource( rootElement ), new StreamResult( writer ) );
631        }
632    
633        /**
634         * Parses the submitted <code>Element</code> as a <code>SimpleLink</code>.
635         * <p>
636         * Possible escaping of the attributes "xlink:href", "xlink:role" and "xlink:arcrole" is performed automatically.
637         * </p>
638         * 
639         * @param element
640         * @return the object representation of the element
641         * @throws XMLParsingException
642         */
643        protected SimpleLink parseSimpleLink( Element element )
644                                throws XMLParsingException {
645    
646            URI href = null;
647            URI role = null;
648            URI arcrole = null;
649            String title = null;
650            String show = null;
651            String actuate = null;
652    
653            String uriString = null;
654            try {
655                uriString = XMLTools.getNodeAsString( element, "@xlink:href", nsContext, null );
656                if ( uriString != null ) {
657                    href = new URI( null, uriString, null );
658                }
659                uriString = XMLTools.getNodeAsString( element, "@xlink:role", nsContext, null );
660                if ( uriString != null ) {
661                    role = new URI( null, uriString, null );
662                }
663                uriString = XMLTools.getNodeAsString( element, "@xlink:arcrole", nsContext, null );
664                if ( uriString != null ) {
665                    arcrole = new URI( null, uriString, null );
666                }
667            } catch ( URISyntaxException e ) {
668                throw new XMLParsingException( "'" + uriString + "' is not a valid URI." );
669            }
670    
671            return new SimpleLink( href, role, arcrole, title, show, actuate );
672        }
673    
674        /**
675         * Parses the value of the submitted <code>Node</code> as a <code>QualifiedName</code>.
676         * <p>
677         * To parse the text contents of an <code>Element</code> node, the actual text node must be given, not the
678         * <code>Element</code> node itself.
679         * </p>
680         * 
681         * @param node
682         * @return object representation of the element
683         * @throws XMLParsingException
684         */
685        public static QualifiedName parseQualifiedName( Node node )
686                                throws XMLParsingException {
687    
688            String name = node.getNodeValue().trim();
689            QualifiedName qName = null;
690            if ( name.indexOf( ':' ) > -1 ) {
691                String[] tmp = StringTools.toArray( name, ":", false );
692                try {
693                    qName = new QualifiedName( tmp[0], tmp[1], XMLTools.getNamespaceForPrefix( tmp[0], node ) );
694                } catch ( URISyntaxException e ) {
695                    throw new XMLParsingException( e.getMessage(), e );
696                }
697            } else {
698                qName = new QualifiedName( name );
699            }
700            return qName;
701        }
702    
703        /**
704         * Returns the qualified name of the given element.
705         * 
706         * @param element
707         * @return the qualified name of the given element.
708         * @throws XMLParsingException
709         */
710        protected QualifiedName getQualifiedName( Element element )
711                                throws XMLParsingException {
712    
713            // TODO check if we can use element.getNamespaceURI() instead
714            URI nsURI = null;
715            String prefix = element.getPrefix();
716            try {
717                nsURI = XMLTools.getNamespaceForPrefix( prefix, element );
718            } catch ( URISyntaxException e ) {
719                String msg = Messages.format( "ERROR_NSURI_NO_URI", element.getPrefix() );
720                LOG.logError( msg, e );
721                throw new XMLParsingException( msg, e );
722            }
723            QualifiedName ftName = new QualifiedName( prefix, element.getLocalName(), nsURI );
724    
725            return ftName;
726        }
727    
728        /**
729         * Returns a string representation of the XML Document
730         * 
731         * @return the string
732         */
733        public String getAsString() {
734            StringWriter writer = new StringWriter( 50000 );
735            Source source = new DOMSource( rootElement );
736            try {
737                Transformer transformer = TransformerFactory.newInstance().newTransformer();
738                transformer.setOutputProperty( "encoding", CharsetUtils.getSystemCharset() );
739                transformer.transform( source, new StreamResult( writer ) );
740            } catch ( Exception e ) {
741                LOG.logError( "Error serializing XMLFragment!", e );
742            }
743            return writer.toString();
744        }
745    
746        /**
747         * Returns a string representation of the XML Document, pretty printed. Note that pretty printing can mess up XML
748         * documents in some cases (GML, for instance).
749         * 
750         * @return the string
751         */
752        public String getAsPrettyString() {
753            StringWriter writer = new StringWriter( 50000 );
754            try {
755                prettyPrint( writer );
756            } catch ( TransformerException e ) {
757                LOG.logError( "Error pretty printing XMLFragment!", e );
758            }
759            return writer.toString();
760        }
761    
762        /**
763         * Returns a string representation of the object.
764         * 
765         * @return a string representation of the object.
766         */
767        @Override
768        public String toString() {
769            return getAsString();
770        }
771    }