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