    001    // $HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_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    }