    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/graphics/sld/SLDFactory.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    package org.deegree.graphics.sld;
    037
    038    import static java.lang.Float.parseFloat;
    039    import static java.lang.StrictMath.sqrt;
    040    import static org.deegree.framework.xml.XMLTools.getElement;
    041    import static org.deegree.framework.xml.XMLTools.getNodeAsDouble;
    042    import static org.deegree.ogcbase.CommonNamespaces.SLDNS;
    043    import static org.deegree.ogcbase.CommonNamespaces.SLD_PREFIX;
    044
    045    import java.io.IOException;
    046    import java.io.StringReader;
    047    import java.net.MalformedURLException;
    048    import java.net.URI;
    049    import java.net.URL;
    050    import java.util.ArrayList;
    051    import java.util.HashMap;
    052    import java.util.Iterator;
    053    import java.util.LinkedList;
    054    import java.util.List;
    055
    056    import org.deegree.datatypes.QualifiedName;
    057    import org.deegree.framework.log.ILogger;
    058    import org.deegree.framework.log.LoggerFactory;
    059    import org.deegree.framework.xml.ElementList;
    060    import org.deegree.framework.xml.NamespaceContext;
    061    import org.deegree.framework.xml.XMLFragment;
    062    import org.deegree.framework.xml.XMLParsingException;
    063    import org.deegree.framework.xml.XMLTools;
    064    import org.deegree.model.filterencoding.AbstractFilter;
    065    import org.deegree.model.filterencoding.ComplexFilter;
    066    import org.deegree.model.filterencoding.Expression;
    067    import org.deegree.model.filterencoding.FalseFilter;
    068    import org.deegree.model.filterencoding.Filter;
    069    import org.deegree.model.filterencoding.FilterEvaluationException;
    070    import org.deegree.model.filterencoding.LogicalOperation;
    071    import org.deegree.model.filterencoding.Operation;
    072    import org.deegree.model.filterencoding.OperationDefines;
    073    import org.deegree.ogcbase.CommonNamespaces;
    074    import org.deegree.ogcbase.OGCDocument;
    075    import org.deegree.ogcbase.PropertyPath;
    076    import org.w3c.dom.Element;
    077    import org.w3c.dom.Node;
    078    import org.w3c.dom.NodeList;
    079    import org.w3c.dom.Text;
    080    import org.xml.sax.SAXException;
    081
    082    /**
    083     * Factory class for all mapped SLD-elements.
    084     * <p>
    085     * TODO: Default values for omitted elements (such as fill color) should better not be used in the construction of the
    086     * corresponding objects (Fill), but marked as left out (to make it possible to differentiate between explicitly given
    087     * values and default values).
    088     * <p>
    089     *
    090     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
    091     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a>
    092     * @version $Revision: 25265 $ $Date: 2010-07-13 08:45:57 +0200 (Di, 13. Jul 2010) $
    093     */
    094    public class SLDFactory {
    095
    096        private static final ILogger LOG = LoggerFactory.getLogger( SLDFactory.class );
    097
    098        private static URI ogcNS = CommonNamespaces.OGCNS;
    099
    100        private static URI xlnNS = CommonNamespaces.XLNNS;
    101
    102        private static final String PSE = CommonNamespaces.SE_PREFIX + ":";
    103
    104        private static NamespaceContext nsContext = CommonNamespaces.getNamespaceContext();
    105
    106        private static XMLFragment sldDoc = null;
    107
    108        /**
    109         * Creates a <tt>StyledLayerDescriptor</tt>-instance from the given XML-representation.
    110         * <p>
    111         *
    112         * @param s
    113         *            contains the XML document
    114         * @throws XMLParsingException
    115         *             if a syntactic or semantic error in the XML document is encountered
    116         * @return the constructed <tt>StyledLayerDescriptor</tt>-instance
    117         */
    118        public static synchronized StyledLayerDescriptor createSLD( String s )
    119                                throws XMLParsingException {
    120            StyledLayerDescriptor sld = null;
    121            try {
    122                sldDoc = new XMLFragment();
    123                sldDoc.load( new StringReader( s.trim() ), XMLFragment.DEFAULT_URL );
    124                sld = createSLD( sldDoc );
    125            } catch ( IOException e ) {
    126                LOG.logDebug( e.getMessage(), e );
    127                throw new XMLParsingException( "IOException encountered while parsing SLD-Document" );
    128            } catch ( SAXException e ) {
    129                LOG.logDebug( e.getMessage(), e );
    130                throw new XMLParsingException( "SAXException encountered while parsing SLD-Document" );
    131            }
    132
    133            return sld;
    134        }
    135
    136        /**
    137         * Creates a <tt>StyledLayerDescriptor</tt>-instance from a SLD document read from the passed URL
    138         *
    139         * @param url
    140         * @return the SLD bean
    141         * @throws XMLParsingException
    142         */
    143        public static synchronized StyledLayerDescriptor createSLD( URL url )
    144                                throws XMLParsingException {
    145            StyledLayerDescriptor sld = null;
    146
    147            try {
    148                sldDoc = new XMLFragment();
    149                sldDoc.load( url );
    150                sld = createSLD( sldDoc );
    151            } catch ( IOException e ) {
    152                LOG.logError( e.getMessage(), e );
    153                throw new XMLParsingException( "IOException encountered while parsing SLD-Document" );
    154            } catch ( SAXException e ) {
    155                LOG.logError( e.getMessage(), e );
    156                throw new XMLParsingException( "SAXException encountered while parsing SLD-Document" );
    157            }
    158
    159            return sld;
    160        }
    161
    162        /**
    163         * Creates a <tt>StyledLayerDescriptor</tt>-instance according to the contents of the DOM-subtree starting at the
    164         * given 'StyledLayerDescriptor'-<tt>Element</tt>.
    165         * <p>
    166         *
    167         * @param sldDoc
    168         *
    169         * @throws XMLParsingException
    170         *             if a syntactic or semantic error in the DOM-subtree is encountered
    171         * @return the constructed <tt>StyledLayerDescriptor</tt>-instance
    172         */
    173        public static StyledLayerDescriptor createSLD( XMLFragment sldDoc )
    174                                throws XMLParsingException {
    175
    176            Element element = sldDoc.getRootElement();
    177
    178            // optional: <Name>
    179            String name = XMLTools.getStringValue( "Name", CommonNamespaces.SLDNS, element, null );
    180
    181            // optional: <Title>
    182            String title = XMLTools.getStringValue( "Title", CommonNamespaces.SLDNS, element, null );
    183            // optional: <Abstract>
    184            String abstract_ = XMLTools.getStringValue( "Abstract", CommonNamespaces.SLDNS, element, null );
    185            // required: version-Attribute
    186            String version = XMLTools.getRequiredAttrValue( "version", null, element );
    187
    188            // optional: <NamedLayer>(s) / <UserLayer>(s)
    189            NodeList nodelist = element.getChildNodes();
    190            ArrayList<AbstractLayer> layerList = new ArrayList<AbstractLayer>( 100 );
    191
    192            for ( int i = 0; i < nodelist.getLength(); i++ ) {
    193                if ( nodelist.item( i ) instanceof Element ) {
    194                    Element child = (Element) nodelist.item( i );
    195                    String namespace = child.getNamespaceURI();
    196                    if ( !CommonNamespaces.SLDNS.toASCIIString().equals( namespace ) ) {
    197                        continue;
    198                    }
    199
    200                    String childName = child.getLocalName();
    201                    if ( childName.equals( "NamedLayer" ) ) {
    202                        layerList.add( createNamedLayer( child ) );
    203                    } else if ( childName.equals( "UserLayer" ) ) {
    204                        layerList.add( createUserLayer( child ) );
    205                    }
    206                }
    207            }
    208
    209            AbstractLayer[] al = new AbstractLayer[layerList.size()];
    210            AbstractLayer[] layers = layerList.toArray( al );
    211
    212            return new StyledLayerDescriptor( name, title, version, abstract_, layers );
    213        }
    214
    215        private static Categorize createCategorize( Element root )
    216                                throws XMLParsingException {
    217            // ignore fallback value, we really implement it
    218            // String fallbackValue = root.getAttribute( "fallbackValue" );
    219
    220            Categorize categorize = new Categorize();
    221
    222            // ignore lookup value element, should be set to "Rasterdata"
    223            // Element lv = XMLTools.getElement( root, PSE + "LookupValue", nsContext );
    224            // ParameterValueType lookupValue = lv == null ? null : createParameterValueType( lv );
    225            //
    226            // if ( lookupValue != null ) {
    227            // categorize.setLookupValue( lookupValue );
    228            // }
    229
    230            List<Element> valueElements = XMLTools.getElements( root, PSE + "Value", nsContext );
    231            List<Element> thresholdElements = XMLTools.getElements( root, PSE + "Threshold", nsContext );
    232
    233            LinkedList<ParameterValueType> values = new LinkedList<ParameterValueType>();
    234            LinkedList<ParameterValueType> thresholds = new LinkedList<ParameterValueType>();
    235
    236            for ( Element e : valueElements ) {
    237                values.add( createParameterValueType( e ) );
    238            }
    239
    240            for ( Element e : thresholdElements ) {
    241                thresholds.add( createParameterValueType( e ) );
    242            }
    243
    244            categorize.setValues( values );
    245            categorize.setThresholds( thresholds );
    246
    247            String tbt = root.getAttribute( "threshholdsBelongTo" );
    248            if ( tbt == null ) {
    249                tbt = root.getAttribute( "thresholdsBelongTo" );
    250            }
    251
    252            ThresholdsBelongTo thresholdsBelongTo = null;
    253
    254            if ( tbt != null ) {
    255                if ( tbt.equalsIgnoreCase( "succeeding" ) ) {
    256                    thresholdsBelongTo = ThresholdsBelongTo.SUCCEEDING;
    257                }
    258                if ( tbt.equalsIgnoreCase( "preceding" ) ) {
    259                    thresholdsBelongTo = ThresholdsBelongTo.PRECEDING;
    260                }
    261            }
    262
    263            if ( thresholdsBelongTo != null ) {
    264                categorize.setThresholdsBelongTo( thresholdsBelongTo );
    265            }
    266
    267            return categorize;
    268        }
    269
    270        /**
    271         *
    272         * @param root
    273         * @param min
    274         * @param max
    275         * @return a raster symbolizer
    276         * @throws XMLParsingException
    277         */
    278        private static RasterSymbolizer createRasterSymbolizer( Element root, double min, double max )
    279                                throws XMLParsingException {
    280            RasterSymbolizer symbolizer = new RasterSymbolizer( min, max );
    281
    282            if ( root.getAttribute( "shaded" ) != null && root.getAttribute( "shaded" ).equals( "true" ) ) {
    283                symbolizer.setShaded( true );
    284
    285                String kernelAtt = root.getAttribute( "kernel" );
    286                if ( kernelAtt == null || kernelAtt.trim().length() == 0 ) {
    287                    kernelAtt = "0, 1, 2, -1, 1, 1, -2, -1, 0";
    288                }
    289                String[] vals = kernelAtt.split( "[,]" );
    290                float[] kernel = new float[vals.length];
    291                for ( int i = 0; i < vals.length; ++i ) {
    292                    kernel[i] = parseFloat( vals[i] );
    293                }
    294
    295                symbolizer.setShadeKernel( (int) sqrt( kernel.length ), kernel );
    296            }
    297
    298            Element opacity = XMLTools.getElement( root, PSE + "Opacity", nsContext );
    299            if ( opacity != null ) {
    300                symbolizer.setOpacity( createParameterValueType( opacity ) );
    301            }
    302
    303            Element colorMap = XMLTools.getElement( root, PSE + "ColorMap", nsContext );
    304            if ( colorMap != null ) {
    305                Element categorize = XMLTools.getElement( colorMap, PSE + "Categorize", nsContext );
    306
    307                if ( categorize != null ) {
    308                    symbolizer.setCategorize( createCategorize( categorize ) );
    309                }
    310
    311                Element interpolate = XMLTools.getElement( colorMap, PSE + "Interpolate", nsContext );
    312
    313                if ( interpolate != null ) {
    314                    symbolizer.setInterpolate( createInterpolate( interpolate ) );
    315                }
    316            }
    317
    318            Element contrastEnhancement = getElement( root, PSE + "ContrastEnhancement", nsContext );
    319            if ( contrastEnhancement != null ) {
    320                symbolizer.setGamma( getNodeAsDouble( contrastEnhancement, PSE + "GammaValue", nsContext, 0d ) );
    321            }
    322
    323            return symbolizer;
    324        }
    325
    326        /**
    327         * @param root
    328         * @return an Interpolate object
    329         * @throws XMLParsingException
    330         */
    331        private static Interpolate createInterpolate( Element root )
    332                                throws XMLParsingException {
    333            String fallbackValue = root.getAttribute( "fallbackValue" );
    334
    335            Interpolate interpolate = new Interpolate( fallbackValue );
    336
    337            Element elem = XMLTools.getElement( root, PSE + "lookupValue", nsContext );
    338            if ( elem != null ) {
    339                interpolate.setLookupValue( createParameterValueType( elem ) );
    340            }
    341
    342            String mode = root.getAttribute( "mode" );
    343            if ( mode != null ) {
    344                if ( mode.equalsIgnoreCase( "linear" ) ) {
    345                    interpolate.setMode( Mode.LINEAR );
    346                }
    347                if ( mode.equalsIgnoreCase( "cosine" ) ) {
    348                    LOG.logWarning( "Cosine interpolation is not supported." );
    349                    interpolate.setMode( Mode.COSINE );
    350                }
    351                if ( mode.equalsIgnoreCase( "cubic" ) ) {
    352                    LOG.logWarning( "Cubic interpolation is not supported." );
    353                    interpolate.setMode( Mode.CUBIC );
    354                }
    355            }
    356
    357            String method = root.getAttribute( "method" );
    358            if ( method != null ) {
    359                if ( method.equalsIgnoreCase( "numeric" ) ) {
    360                    LOG.logWarning( "Numeric method is not supported, using color method anyway." );
    361                    interpolate.setMethod( Method.NUMERIC );
    362                }
    363                if ( method.equalsIgnoreCase( "color" ) ) {
    364                    interpolate.setMethod( Method.COLOR );
    365                }
    366            }
    367
    368            List<Element> ips = XMLTools.getElements( root, PSE + "InterpolationPoint", nsContext );
    369
    370            interpolate.setInterpolationPoints( createInterpolationPoints( ips ) );
    371
    372            return interpolate;
    373        }
    374
    375        private static List<InterpolationPoint> createInterpolationPoints( List<Element> ips )
    376                                throws XMLParsingException {
    377            List<InterpolationPoint> ps = new ArrayList<InterpolationPoint>( ips.size() );
    378
    379            for ( Element elem : ips ) {
    380                double data = XMLTools.getRequiredNodeAsDouble( elem, PSE + "Data", nsContext );
    381                Element e = XMLTools.getRequiredElement( elem, PSE + "Value", nsContext );
    382                try {
    383                    String val = createParameterValueType( e ).evaluate( null ).substring( 1 );
    384                    ps.add( new InterpolationPoint( data, val ) );
    385                } catch ( NumberFormatException e1 ) {
    386                    LOG.logError( "A 'Value' in an 'InterpolationPoint' could not be parsed.", e1 );
    387                } catch ( FilterEvaluationException e1 ) {
    388                    LOG.logError( "A 'Value' in an 'InterpolationPoint' could not be parsed.", e1 );
    389                }
    390            }
    391
    392            return ps;
    393        }
    394
    395        /**
    396         * Creates a <tt>TextSymbolizer</tt>-instance according to the contents of the DOM-subtree starting at the given
    397         * 'TextSymbolizer'-<tt>Element</tt>.
    398         * <p>
    399         *
    400         * @param element
    401         *            the 'TextSymbolizer'-<tt>Element</tt>
    402         * @param min
    403         *            scale-constraint to be used
    404         * @param max
    405         *            scale-constraint to be used
    406         * @throws XMLParsingException
    407         *             if a syntactic or semantic error in the DOM-subtree is encountered
    408         * @return the constructed <tt>TextSymbolizer</tt>-instance
    409         */
    410        private static TextSymbolizer createTextSymbolizer( Element element, double min, double max )
    411                                throws XMLParsingException {
    412
    413            // optional: <Geometry>
    414            Geometry geometry = null;
    415            Element geometryElement = XMLTools.getChildElement( "Geometry", CommonNamespaces.SLDNS, element );
    416
    417            if ( geometryElement != null ) {
    418                geometry = createGeometry( geometryElement );
    419            }
    420
    421            // optional: <Label>
    422            ParameterValueType label = null;
    423            Element labelElement = XMLTools.getChildElement( "Label", CommonNamespaces.SLDNS, element );
    424
    425            if ( labelElement != null ) {
    426                label = createParameterValueType( labelElement );
    427            }
    428
    429            // optional: <Font>
    430            Font font = null;
    431            Element fontElement = XMLTools.getChildElement( "Font", CommonNamespaces.SLDNS, element );
    432
    433            if ( fontElement != null ) {
    434                font = createFont( fontElement );
    435            }
    436
    437            // optional: <LabelPlacement>
    438            LabelPlacement labelPlacement = null;
    439            Element lpElement = XMLTools.getChildElement( "LabelPlacement", CommonNamespaces.SLDNS, element );
    440
    441            if ( lpElement != null ) {
    442                labelPlacement = createLabelPlacement( lpElement );
    443            } else {
    444                PointPlacement pp = StyleFactory.createPointPlacement();
    445                labelPlacement = StyleFactory.createLabelPlacement( pp );
    446            }
    447
    448            // optional: <Halo>
    449            Halo halo = null;
    450            Element haloElement = XMLTools.getChildElement( "Halo", SLDNS, element );
    451
    452            if ( haloElement != null ) {
    453                halo = createHalo( haloElement );
    454            }
    455
    456            // optional: <Fill>
    457            Fill fill = null;
    458
    459            TextSymbolizer ps = null;
    460
    461            // deegree specific extension:
    462            Element bbox = getElement( element, SLD_PREFIX + ":BoundingBox", nsContext );
    463            if ( bbox != null ) {
    464                try {
    465                    ParameterValueType minx = createParameterValueType( getElement( bbox, SLD_PREFIX + ":Minx", nsContext ) );
    466                    ParameterValueType miny = createParameterValueType( getElement( bbox, SLD_PREFIX + ":Miny", nsContext ) );
    467                    ParameterValueType maxx = createParameterValueType( getElement( bbox, SLD_PREFIX + ":Maxx", nsContext ) );
    468                    ParameterValueType maxy = createParameterValueType( getElement( bbox, SLD_PREFIX + ":Maxy", nsContext ) );
    469
    470                    ps = new TextSymbolizer( geometry, label, font, halo, minx, miny, maxx, maxy );
    471                } catch ( NullPointerException npe ) {
    472                    npe.printStackTrace();
    473                    LOG.logError( "One of Minx, Miny, Maxx, Maxy was missing in a BoundingBox." );
    474                }
    475            } else {
    476
    477                String respClass = XMLTools.getAttrValue( element, null, "responsibleClass", null );
    478                if ( respClass == null ) {
    479                    ps = new TextSymbolizer( geometry, label, font, labelPlacement, halo, fill, min, max );
    480                } else {
    481                    ps = new TextSymbolizer( geometry, respClass, label, font, labelPlacement, halo, fill, min, max );
    482                }
    483            }
    484
    485            return ps;
    486        }
    487
    488        /**
    489         * Creates a <tt>Halo</tt>-instance according to the contents of the DOM-subtree starting at the given 'Halo'-
    490         * <tt>Element</tt>.
    491         * <p>
    492         *
    493         * @param element
    494         *            the 'Halo'-<tt>Element</tt>
    495         * @throws XMLParsingException
    496         *             if a syntactic or semantic error in the DOM-subtree is encountered
    497         * @return the constructed <tt>Halo</tt>-instance
    498         */
    499        private static Halo createHalo( Element element )
    500                                throws XMLParsingException {
    501            // optional: <Radius>
    502            ParameterValueType radius = null;
    503            Element radiusElement = XMLTools.getChildElement( "Radius", CommonNamespaces.SLDNS, element );
    504
    505            if ( radiusElement != null ) {
    506                radius = createParameterValueType( radiusElement );
    507            }
    508
    509            // optional: <Fill>
    510            Fill fill = null;
    511            Element fillElement = XMLTools.getChildElement( "Fill", CommonNamespaces.SLDNS, element );
    512
    513            if ( fillElement != null ) {
    514                fill = createFill( fillElement );
    515            }
    516
    517            // optional: <Stroke>
    518            Stroke stroke = null;
    519            Element strokeElement = XMLTools.getChildElement( "Stroke", CommonNamespaces.SLDNS, element );
    520
    521            if ( strokeElement != null ) {
    522                stroke = createStroke( strokeElement );
    523            }
    524
    525            return new Halo( radius, fill, stroke );
    526        }
    527
    528        /**
    529         * Creates a <tt>LabelPlacement</tt>-instance according to the contents of the DOM-subtree starting at the given
    530         * 'LabelPlacement'-<tt>Element</tt>.
    531         * <p>
    532         *
    533         * @param element
    534         *            the 'LabelPlacement'-<tt>Element</tt>
    535         * @throws XMLParsingException
    536         *             if a syntactic or semantic error in the DOM-subtree is encountered
    537         * @return the constructed <tt>LabelPlacement</tt>-instance
    538         */
    539        private static LabelPlacement createLabelPlacement( Element element )
    540                                throws XMLParsingException {
    541            LabelPlacement labelPlacement = null;
    542
    543            // required: <PointPlacement> / <LinePlacement>
    544            NodeList nodelist = element.getChildNodes();
    545            PointPlacement pPlacement = null;
    546            LinePlacement lPlacement = null;
    547
    548            for ( int i = 0; i < nodelist.getLength(); i++ ) {
    549                if ( nodelist.item( i ) instanceof Element ) {
    550                    Element child = (Element) nodelist.item( i );
    551                    String namespace = child.getNamespaceURI();
    552
    553                    if ( !CommonNamespaces.SLDNS.toASCIIString().equals( namespace ) ) {
    554                        continue;
    555                    }
    556
    557                    String childName = child.getLocalName();
    558
    559                    if ( childName.equals( "PointPlacement" ) ) {
    560                        pPlacement = createPointPlacement( child );
    561                    } else if ( childName.equals( "LinePlacement" ) ) {
    562                        lPlacement = createLinePlacement( child );
    563                    }
    564                }
    565            }
    566
    567            if ( ( pPlacement != null ) && ( lPlacement == null ) ) {
    568                labelPlacement = new LabelPlacement( pPlacement );
    569            } else if ( ( pPlacement == null ) && ( lPlacement != null ) ) {
    570                labelPlacement = new LabelPlacement( lPlacement );
    571            } else {
    572                throw new XMLParsingException( "Element 'LabelPlacement' must contain exactly one "
    573                                               + "'PointPlacement'- or one 'LinePlacement'-element!" );
    574            }
    575
    576            return labelPlacement;
    577        }
    578
    579        /**
    580         * Creates a <tt>PointPlacement</tt>-instance according to the contents of the DOM-subtree starting at the given
    581         * 'PointPlacement'-<tt>Element</tt>.
    582         * <p>
    583         *
    584         * @param element
    585         *            the 'PointPlacement'-<tt>Element</tt>
    586         * @throws XMLParsingException
    587         *             if a syntactic or semantic error in the DOM-subtree is encountered
    588         * @return the constructed <tt>PointPlacement</tt>-instance
    589         */
    590        private static PointPlacement createPointPlacement( Element element )
    591                                throws XMLParsingException {
    592
    593            // optional: auto-Attribute (this is deegree-specific)
    594            boolean auto = false;
    595            String autoStr = XMLTools.getAttrValue( element, null, "auto", null );
    596
    597            if ( autoStr != null && autoStr.equals( "true" ) ) {
    598                auto = true;
    599            }
    600
    601            // optional: <AnchorPoint>
    602            ParameterValueType[] anchorPoint = null;
    603            Element apElement = XMLTools.getChildElement( "AnchorPoint", CommonNamespaces.SLDNS, element );
    604
    605            if ( apElement != null ) {
    606                anchorPoint = new ParameterValueType[2];
    607
    608                Element apXElement = XMLTools.getChildElement( "AnchorPointX", CommonNamespaces.SLDNS, apElement );
    609                Element apYElement = XMLTools.getChildElement( "AnchorPointY", CommonNamespaces.SLDNS, apElement );
    610
    611                if ( ( apXElement == null ) || ( apYElement == null ) ) {
    612                    throw new XMLParsingException( "Element 'AnchorPoint' must contain exactly one "
    613                                                   + "'AnchorPointX'- and one 'AnchorPointY'-element!" );
    614                }
    615
    616                anchorPoint[0] = createParameterValueType( apXElement );
    617                anchorPoint[1] = createParameterValueType( apYElement );
    618            }
    619
    620            // optional: <Displacement>
    621            ParameterValueType[] displacement = null;
    622            Element dElement = XMLTools.getChildElement( "Displacement", CommonNamespaces.SLDNS, element );
    623
    624            if ( dElement != null ) {
    625                displacement = new ParameterValueType[2];
    626
    627                Element dXElement = XMLTools.getChildElement( "DisplacementX", CommonNamespaces.SLDNS, dElement );
    628                Element dYElement = XMLTools.getChildElement( "DisplacementY", CommonNamespaces.SLDNS, dElement );
    629
    630                if ( ( dXElement == null ) || ( dYElement == null ) ) {
    631                    throw new XMLParsingException( "Element 'Displacement' must contain exactly one "
    632                                                   + "'DisplacementX'- and one 'DisplacementY'-element!" );
    633                }
    634
    635                displacement[0] = createParameterValueType( dXElement );
    636                displacement[1] = createParameterValueType( dYElement );
    637            }
    638
    639            // optional: <Rotation>
    640            ParameterValueType rotation = null;
    641            Element rElement = XMLTools.getChildElement( "Rotation", CommonNamespaces.SLDNS, element );
    642
    643            if ( rElement != null ) {
    644                rotation = createParameterValueType( rElement );
    645            }
    646
    647            return new PointPlacement( anchorPoint, displacement, rotation, auto );
    648        }
    649
    650        /**
    651         * Creates a <tt>LinePlacement</tt>-instance according to the contents of the DOM-subtree starting at the given
    652         * 'LinePlacement'-<tt>Element</tt>.
    653         * <p>
    654         *
    655         * @param element
    656         *            the 'LinePlacement'-<tt>Element</tt>
    657         * @throws XMLParsingException
    658         *             if a syntactic or semantic error in the DOM-subtree is encountered
    659         * @return the constructed <tt>LinePlacement</tt>-instance
    660         */
    661        private static LinePlacement createLinePlacement( Element element )
    662                                throws XMLParsingException {
    663
    664            // optional: <PerpendicularOffset>
    665            ParameterValueType pOffset = null;
    666            Element pOffsetElement = XMLTools.getChildElement( "PerpendicularOffset", CommonNamespaces.SLDNS, element );
    667
    668            if ( pOffsetElement != null ) {
    669                pOffset = createParameterValueType( pOffsetElement );
    670            }
    671
    672            // optional: <Gap> (this is deegree-specific)
    673            ParameterValueType gap = null;
    674            Element gapElement = XMLTools.getChildElement( "Gap", CommonNamespaces.SLDNS, element );
    675
    676            if ( gapElement != null ) {
    677                gap = createParameterValueType( gapElement );
    678            }
    679
    680            // optional: <LineWidth> (this is deegree-specific)
    681            ParameterValueType lineWidth = null;
    682            Element lineWidthElement = XMLTools.getChildElement( "LineWidth", CommonNamespaces.SLDNS, element );
    683
    684            if ( lineWidthElement != null ) {
    685                lineWidth = createParameterValueType( lineWidthElement );
    686            }
    687
    688            return new LinePlacement( pOffset, lineWidth, gap );
    689        }
    690
    691        /**
    692         * Creates a <tt>Font</tt>-instance according to the contents of the DOM-subtree starting at the given 'Font'-
    693         * <tt>Element</tt>.
    694         * <p>
    695         *
    696         * @param element
    697         *            the 'Font'-<tt>Element</tt>
    698         * @throws XMLParsingException
    699         *             if a syntactic or semantic error in the DOM-subtree is encountered
    700         * @return the constructed <tt>Font</tt>-instance
    701         */
    702        private static Font createFont( Element element )
    703                                throws XMLParsingException {
    704
    705            // optional: <CssParameter>s
    706            ElementList nl = XMLTools.getChildElements( "CssParameter", CommonNamespaces.SLDNS, element );
    707            HashMap<String, CssParameter> cssParams = new HashMap<String, CssParameter>( nl.getLength() );
    708
    709            for ( int i = 0; i < nl.getLength(); i++ ) {
    710                CssParameter cssParam = createCssParameter( nl.item( i ) );
    711                cssParams.put( cssParam.getName(), cssParam );
    712            }
    713
    714            return new Font( cssParams );
    715        }
    716
    717        /**
    718         * Creates a <tt>ParameterValueType</tt>-instance according to the contents of the DOM-subtree starting at the given
    719         * <tt>Element</tt>.
    720         * <p>
    721         *
    722         * @param element
    723         *            the <tt>Element</tt> (must be of the type sld:ParameterValueType)
    724         * @throws XMLParsingException
    725         *             if a syntactic or semantic error in the DOM-subtree is encountered
    726         * @return the constructed <tt>ParameterValueType</tt>-instance
    727         */
    728        private static ParameterValueType createParameterValueType( Element element )
    729                                throws XMLParsingException {
    730            // mix of text nodes and <wfs:Expression>-elements
    731            ArrayList<Object> componentList = new ArrayList<Object>();
    732            NodeList nl = element.getChildNodes();
    733
    734            for ( int i = 0; i < nl.getLength(); i++ ) {
    735                Node node = nl.item( i );
    736
    737                switch ( node.getNodeType() ) {
    738                case Node.TEXT_NODE: {
    739                    componentList.add( node.getNodeValue() );
    740                    break;
    741                }
    742                case Node.ELEMENT_NODE: {
    743                    Expression expression = Expression.buildFromDOM( (Element) node );
    744                    componentList.add( expression );
    745                    break;
    746                }
    747                default:
    748                    throw new XMLParsingException( "Elements of type 'ParameterValueType' may only "
    749                                                   + "consist of CDATA and 'ogc:Expression'-elements!" );
    750                }
    751            }
    752
    753            Object[] components = componentList.toArray( new Object[componentList.size()] );
    754            return new ParameterValueType( components );
    755        }
    756
    757        /**
    758         * Creates a <tt>NamedStyle</tt>-instance according to the contents of the DOM-subtree starting at the given
    759         * 'NamedStyle'-<tt>Element</tt>.
    760         * <p>
    761         *
    762         * @param element
    763         *            the 'NamedStyle'-<tt>Element</tt>
    764         * @throws XMLParsingException
    765         *             if a syntactic or semantic error in the DOM-subtree is encountered
    766         * @return the constructed <tt>NamedStyle</tt>-instance
    767         */
    768        private static NamedStyle createNamedStyle( Element element )
    769                                throws XMLParsingException {
    770            // required: <Name>
    771            String name = XMLTools.getRequiredStringValue( "Name", CommonNamespaces.SLDNS, element );
    772
    773            return new NamedStyle( name );
    774        }
    775
    776        /**
    777         *
    778         */
    779        public static NamedStyle createNamedStyle( String name ) {
    780            return new NamedStyle( name );
    781        }
    782
    783        /**
    784         * Creates a <tt>RemoteOWS</tt>-instance according to the contents of the DOM-subtree starting at the given
    785         * 'RemoteOWS'-<tt>Element</tt>.
    786         * <p>
    787         *
    788         * @param element
    789         *            the 'RemoteOWS'-<tt>Element</tt>
    790         * @throws XMLParsingException
    791         *             if a syntactic or semantic error in the DOM-subtree is encountered
    792         * @return the constructed <tt>RemoteOWS</tt>-instance
    793         */
    794        private static RemoteOWS createRemoteOWS( Element element )
    795                                throws XMLParsingException {
    796            // required: <Service>
    797            String service = XMLTools.getRequiredStringValue( "Service", CommonNamespaces.SLDNS, element );
    798
    799            if ( !( service.equals( "WFS" ) || service.equals( "WCS" ) ) ) {
    800                throw new XMLParsingException( "Value ('" + service + "') of element 'service' is invalid. "
    801                                               + "Allowed values are: 'WFS' and 'WCS'." );
    802            }
    803
    804            // required: <OnlineResource>
    805            Element onlineResourceElement = XMLTools.getRequiredChildElement( "OnlineResource", CommonNamespaces.SLDNS,
    806                                                                              element );
    807            String href = XMLTools.getRequiredAttrValue( "xlink:href", null, onlineResourceElement );
    808            URL url = null;
    809
    810            try {
    811                url = new URL( href );
    812            } catch ( MalformedURLException e ) {
    813                LOG.logDebug( e.getMessage(), e );
    814                throw new XMLParsingException( "Value ('" + href + "') of attribute 'href' of "
    815                                               + "element 'OnlineResoure' does not denote a valid URL" );
    816            }
    817
    818            return new RemoteOWS( service, url );
    819        }
    820
    821        /**
    822         * Creates a <tt>NamedLayer</tt>-instance according to the contents of the DOM-subtree starting at the given
    823         * 'UserLayer'-<tt>Element</tt>.
    824         * <p>
    825         *
    826         * @param element
    827         *            the 'NamedLayer'-<tt>Element</tt>
    828         * @throws XMLParsingException
    829         *             if a syntactic or semantic error in the DOM-subtree is encountered
    830         * @return the constructed <tt>NamedLayer</tt>-instance
    831         */
    832        private static NamedLayer createNamedLayer( Element element )
    833                                throws XMLParsingException {
    834            // required: <Name>
    835            String name = XMLTools.getRequiredStringValue( "Name", CommonNamespaces.SLDNS, element );
    836
    837            // optional: <LayerFeatureConstraints>
    838            LayerFeatureConstraints lfc = null;
    839            Element lfcElement = XMLTools.getChildElement( "LayerFeatureConstraints", CommonNamespaces.SLDNS, element );
    840
    841            if ( lfcElement != null ) {
    842                lfc = createLayerFeatureConstraints( lfcElement );
    843            }
    844
    845            // optional: <NamedStyle>(s) / <UserStyle>(s)
    846            NodeList nodelist = element.getChildNodes();
    847            ArrayList<AbstractStyle> styleList = new ArrayList<AbstractStyle>();
    848
    849            for ( int i = 0; i < nodelist.getLength(); i++ ) {
    850                if ( nodelist.item( i ) instanceof Element ) {
    851                    Element child = (Element) nodelist.item( i );
    852                    String namespace = child.getNamespaceURI();
    853
    854                    if ( !CommonNamespaces.SLDNS.toASCIIString().equals( namespace ) ) {
    855                        continue;
    856                    }
    857
    858                    String childName = child.getLocalName();
    859
    860                    if ( childName.equals( "NamedStyle" ) ) {
    861                        styleList.add( createNamedStyle( child ) );
    862                    } else if ( childName.equals( "UserStyle" ) ) {
    863                        styleList.add( createUserStyle( child ) );
    864                    }
    865                }
    866            }
    867
    868            AbstractStyle[] styles = styleList.toArray( new AbstractStyle[styleList.size()] );
    869
    870            return new NamedLayer( name, lfc, styles );
    871        }
    872
    873        /**
    874         *
    875         */
    876        public static NamedLayer createNamedLayer( String name, LayerFeatureConstraints layerFeatureConstraints,
    877                                                   AbstractStyle[] styles ) {
    878            return new NamedLayer( name, layerFeatureConstraints, styles );
    879        }
    880
    881        /**
    882         * Creates a <tt>UserLayer</tt>-instance according to the contents of the DOM-subtree starting at the given
    883         * 'UserLayer'-<tt>Element</tt>.
    884         * <p>
    885         *
    886         * @param element
    887         *            the 'UserLayer'-<tt>Element</tt>
    888         * @throws XMLParsingException
    889         *             if a syntactic or semantic error in the DOM-subtree is encountered
    890         * @return the constructed <tt>UserLayer</tt>-instance
    891         */
    892        private static UserLayer createUserLayer( Element element )
    893                                throws XMLParsingException {
    894            // optional: <Name>
    895            String name = XMLTools.getStringValue( "Name", CommonNamespaces.SLDNS, element, null );
    896
    897            // optional: <RemoteOWS>
    898            RemoteOWS remoteOWS = null;
    899            Element remoteOWSElement = XMLTools.getChildElement( "RemoteOWS", CommonNamespaces.SLDNS, element );
    900
    901            if ( remoteOWSElement != null ) {
    902                remoteOWS = createRemoteOWS( remoteOWSElement );
    903            }
    904
    905            // required: <LayerFeatureConstraints>
    906            LayerFeatureConstraints lfc = null;
    907            Element lfcElement = XMLTools.getRequiredChildElement( "LayerFeatureConstraints", CommonNamespaces.SLDNS,
    908                                                                   element );
    909            lfc = createLayerFeatureConstraints( lfcElement );
    910
    911            // optional: <UserStyle>(s)
    912            ElementList nodelist = XMLTools.getChildElements( "UserStyle", CommonNamespaces.SLDNS, element );
    913            UserStyle[] styles = new UserStyle[nodelist.getLength()];
    914            for ( int i = 0; i < nodelist.getLength(); i++ ) {
    915                styles[i] = createUserStyle( nodelist.item( i ) );
    916            }
    917
    918            return new UserLayer( name, lfc, styles, remoteOWS );
    919        }
    920
    921        /**
    922         * Creates a <tt>FeatureTypeConstraint</tt>-instance according to the contents of the DOM-subtree starting at the
    923         * given 'FeatureTypeConstraint'-<tt>Element</tt>.
    924         * <p>
    925         *
    926         * @param element
    927         *            the 'FeatureTypeConstraint'-<tt>Element</tt>
    928         * @throws XMLParsingException
    929         *             if a syntactic or semantic error in the DOM-subtree is encountered
    930         * @return the constructed <tt>FeatureTypeConstraint</tt>-instance
    931         */
    932        private static FeatureTypeConstraint createFeatureTypeConstraint( Element element )
    933                                throws XMLParsingException {
    934            // optional: <Name>
    935            Node node = XMLTools.getNode( element, "sld:FeatureTypeName/text()", CommonNamespaces.getNamespaceContext() );
    936
    937            QualifiedName name;
    938            if ( node == null ) {
    939                name = null;
    940            } else {
    941                name = XMLTools.getQualifiedNameValue( node );
    942            }
    943
    944            // optional: <Filter>
    945            Filter filter = null;
    946            Element filterElement = XMLTools.getChildElement( "Filter", ogcNS, element );
    947
    948            if ( filterElement != null ) {
    949                filter = AbstractFilter.buildFromDOM( filterElement );
    950            }
    951
    952            // optional: <Extent>(s)
    953            ElementList nodelist = XMLTools.getChildElements( "Extent", CommonNamespaces.SLDNS, element );
    954            Extent[] extents = new Extent[nodelist.getLength()];
    955
    956            for ( int i = 0; i < nodelist.getLength(); i++ ) {
    957                extents[i] = createExtent( nodelist.item( i ) );
    958            }
    959
    960            return new FeatureTypeConstraint( name, filter, extents );
    961        }
    962
    963        /**
    964         * Creates an <tt>Extent</tt>-instance according to the contents of the DOM-subtree starting at the given 'Extent'-
    965         * <tt>Element</tt>.
    966         * <p>
    967         *
    968         * @param element
    969         *            the 'Extent'-<tt>Element</tt>
    970         * @throws XMLParsingException
    971         *             if a syntactic or semantic error in the DOM-subtree is encountered
    972         * @return the constructed <tt>Extent</tt>-instance
    973         */
    974        private static Extent createExtent( Element element )
    975                                throws XMLParsingException {
    976            // required: <Name>
    977            String name = XMLTools.getRequiredStringValue( "Name", CommonNamespaces.SLDNS, element );
    978            // required: <Value>
    979            String value = XMLTools.getRequiredStringValue( "Value", CommonNamespaces.SLDNS, element );
    980
    981            return new Extent( name, value );
    982        }
    983
    984        /**
    985         * Creates a <tt>LayerFeatureConstraints</tt>-instance according to the contents of the DOM-subtree starting at the
    986         * given 'LayerFeatureConstraints'-<tt>Element</tt>.
    987         * <p>
    988         *
    989         * @param element
    990         *            the 'LayerFeatureConstraints'-<tt>Element</tt>
    991         * @throws XMLParsingException
    992         *             if a syntactic or semantic error in the DOM-subtree is encountered
    993         * @return the constructed <tt>LayerFeatureConstraints</tt>-instance
    994         */
    995        public static LayerFeatureConstraints createLayerFeatureConstraints( Element element )
    996                                throws XMLParsingException {
    997            // required: <FeatureTypeConstraint>(s)
    998            ElementList nodelist = XMLTools.getChildElements( "FeatureTypeConstraint", CommonNamespaces.SLDNS, element );
    999            FeatureTypeConstraint[] ftcs = new FeatureTypeConstraint[nodelist.getLength()];
    1000
    1001            for ( int i = 0; i < nodelist.getLength(); i++ ) {
    1002                ftcs[i] = createFeatureTypeConstraint( nodelist.item( i ) );
    1003            }
    1004
    1005            return new LayerFeatureConstraints( ftcs );
    1006        }
    1007
    1008        /**
    1009         * Creates a <tt>UserStyle</tt>-instance according to the contents of the DOM-subtree starting at the given
    1010         * 'UserStyle'-<tt>Element</tt>.
    1011         * <p>
    1012         *
    1013         * @param element
    1014         *            the 'UserStyle'-<tt>Element</tt>
    1015         * @throws XMLParsingException
    1016         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1017         * @return the constructed <tt>UserStyle</tt>-instance
    1018         */
    1019        public static UserStyle createUserStyle( Element element )
    1020                                throws XMLParsingException {
    1021            // optional: <Name>
    1022            String name = XMLTools.getStringValue( "Name", CommonNamespaces.SLDNS, element, null );
    1023            // optional: <Title>
    1024            String title = XMLTools.getStringValue( "Title", CommonNamespaces.SLDNS, element, null );
    1025            // optional: <Abstract>
    1026            String abstract_ = XMLTools.getStringValue( "Abstract", CommonNamespaces.SLDNS, element, null );
    1027
    1028            // optional: <IsDefault>
    1029            String defaultString = XMLTools.getStringValue( "IsDefault", CommonNamespaces.SLDNS, element, null );
    1030            boolean isDefault = false;
    1031
    1032            if ( defaultString != null ) {
    1033                if ( defaultString.equals( "1" ) ) {
    1034                    isDefault = true;
    1035                }
    1036            }
    1037
    1038            // required: <FeatureTypeStyle> (s)
    1039            ElementList nl = XMLTools.getChildElements( "FeatureTypeStyle", CommonNamespaces.SLDNS, element );
    1040            FeatureTypeStyle[] styles = new FeatureTypeStyle[nl.getLength()];
    1041
    1042            if ( styles.length == 0 ) {
    1043                throw new XMLParsingException( "Required child-element 'FeatureTypeStyle' of element "
    1044                                               + "'UserStyle' is missing!" );
    1045            }
    1046
    1047            for ( int i = 0; i < nl.getLength(); i++ ) {
    1048                styles[i] = createFeatureTypeStyle( nl.item( i ) );
    1049            }
    1050
    1051            return new UserStyle( name, title, abstract_, isDefault, styles );
    1052        }
    1053
    1054        /**
    1055         * Creates a <tt>FeatureTypeStyle</tt>-instance according to the contents of the DOM-subtree starting at the given
    1056         * 'FeatureTypeStyle'-<tt>Element</tt>.
    1057         * <p>
    1058         * TODO: The ElseFilter currently does not work correctly with FeatureFilters.
    1059         * <p>
    1060         *
    1061         * @param element
    1062         *            the 'FeatureTypeStyle'-<tt>Element</tt>
    1063         * @throws XMLParsingException
    1064         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1065         * @return the constructed <tt>FeatureTypeStyle</tt>-instance
    1066         */
    1067        public static FeatureTypeStyle createFeatureTypeStyle( Element element )
    1068                                throws XMLParsingException {
    1069            // optional: <Name>
    1070            String name = XMLTools.getStringValue( "Name", CommonNamespaces.SLDNS, element, null );
    1071            // optional: <Title>
    1072            String title = XMLTools.getStringValue( "Title", CommonNamespaces.SLDNS, element, null );
    1073            // optional: <Abstract>
    1074            String abstract_ = XMLTools.getStringValue( "Abstract", CommonNamespaces.SLDNS, element, null );
    1075            // optional: <FeatureTypeName>
    1076            String featureTypeName = XMLTools.getStringValue( "FeatureTypeName", CommonNamespaces.SLDNS, element, null );
    1077
    1078            // optional: several <Rule> / <SemanticTypeIdentifier>
    1079            NodeList nodelist = element.getChildNodes();
    1080            ArrayList<Rule> ruleList = new ArrayList<Rule>();
    1081            ArrayList<String> typeIdentifierList = new ArrayList<String>();
    1082
    1083            // collect Filters of all Rules
    1084            ArrayList<Filter> filters = new ArrayList<Filter>();
    1085            // collect all Rules that have an ElseFilter
    1086            ArrayList<Rule> elseRules = new ArrayList<Rule>();
    1087
    1088            for ( int i = 0; i < nodelist.getLength(); i++ ) {
    1089                if ( nodelist.item( i ) instanceof Element ) {
    1090                    Element child = (Element) nodelist.item( i );
    1091                    String namespace = child.getNamespaceURI();
    1092                    if ( !CommonNamespaces.SLDNS.toString().equals( namespace ) ) {
    1093                        continue;
    1094                    }
    1095
    1096                    String childName = child.getLocalName();
    1097
    1098                    if ( childName.equals( "Rule" ) ) {
    1099                        Rule rule = createRule( child );
    1100                        if ( rule.hasElseFilter() ) {
    1101                            elseRules.add( rule );
    1102                        } else if ( rule.getFilter() == null || rule.getFilter() instanceof ComplexFilter ) {
    1103                            filters.add( rule.getFilter() );
    1104                        }
    1105                        ruleList.add( rule );
    1106                    } else if ( childName.equals( "SemanticTypeIdentifier" ) ) {
    1107                        typeIdentifierList.add( XMLTools.getStringValue( child ) );
    1108                    }
    1109                }
    1110            }
    1111
    1112            // compute and set the ElseFilter for all ElseFilter-Rules
    1113            Filter elseFilter = null;
    1114            // a Rule exists with no Filter at all -> elseFilter = false
    1115            if ( filters.contains( null ) ) {
    1116                elseFilter = new FalseFilter();
    1117                // one Rule with a Filter exists -> elseFilter = NOT Filter
    1118            } else if ( filters.size() == 1 ) {
    1119                elseFilter = new ComplexFilter( OperationDefines.NOT );
    1120                List<Operation> arguments = ( (LogicalOperation) ( (ComplexFilter) elseFilter ).getOperation() ).getArguments();
    1121                ComplexFilter complexFilter = (ComplexFilter) filters.get( 0 );
    1122                arguments.add( complexFilter.getOperation() );
    1123                // several Rules with Filters exist -> elseFilter = NOT (Filter1 OR Filter2 OR...)
    1124            } else if ( filters.size() > 1 ) {
    1125                ComplexFilter innerFilter = new ComplexFilter( OperationDefines.OR );
    1126                elseFilter = new ComplexFilter( innerFilter, null, OperationDefines.NOT );
    1127                List<Operation> arguments = ( (LogicalOperation) innerFilter.getOperation() ).getArguments();
    1128                Iterator<Filter> it = filters.iterator();
    1129                while ( it.hasNext() ) {
    1130                    ComplexFilter complexFilter = (ComplexFilter) it.next();
    1131                    arguments.add( complexFilter.getOperation() );
    1132                }
    1133            }
    1134            Iterator<Rule> it = elseRules.iterator();
    1135            while ( it.hasNext() ) {
    1136                it.next().setFilter( elseFilter );
    1137            }
    1138
    1139            Rule[] rules = ruleList.toArray( new Rule[ruleList.size()] );
    1140            String[] typeIdentifiers = typeIdentifierList.toArray( new String[typeIdentifierList.size()] );
    1141
    1142            return new FeatureTypeStyle( name, title, abstract_, featureTypeName, typeIdentifiers, rules );
    1143        }
    1144
    1145        /**
    1146         * Creates a <tt>Rule</tt>-instance according to the contents of the DOM-subtree starting at the given 'Rule'-
    1147         * <tt>Element</tt>.
    1148         * <p>
    1149         *
    1150         * @param element
    1151         *            the 'Rule'-<tt>Element</tt>
    1152         * @throws XMLParsingException
    1153         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1154         * @return the constructed <tt>Rule</tt>-instance
    1155         */
    1156        private static Rule createRule( Element element )
    1157                                throws XMLParsingException {
    1158            // optional: <Name>
    1159            String name = XMLTools.getStringValue( "Name", CommonNamespaces.SLDNS, element, null );
    1160            // optional: <Title>
    1161            String title = XMLTools.getStringValue( "Title", CommonNamespaces.SLDNS, element, null );
    1162            // optional: <Abstract>
    1163            String abstract_ = XMLTools.getStringValue( "Abstract", CommonNamespaces.SLDNS, element, null );
    1164
    1165            // optional: <LegendGraphic>
    1166            LegendGraphic legendGraphic = null;
    1167            Element legendGraphicElement = XMLTools.getChildElement( "LegendGraphic", CommonNamespaces.SLDNS, element );
    1168
    1169            if ( legendGraphicElement != null ) {
    1170                legendGraphic = createLegendGraphic( legendGraphicElement );
    1171            }
    1172
    1173            // optional: <Filter>
    1174            boolean isAnElseFilter = false;
    1175            Filter filter = null;
    1176            Element filterElement = XMLTools.getChildElement( "Filter", ogcNS, element );
    1177            if ( filterElement != null ) {
    1178                filter = AbstractFilter.buildFromDOM( filterElement );
    1179            }
    1180
    1181            // optional: <ElseFilter>
    1182            Element elseFilterElement = XMLTools.getChildElement( "ElseFilter", CommonNamespaces.SLDNS, element );
    1183            if ( elseFilterElement != null ) {
    1184                isAnElseFilter = true;
    1185            }
    1186
    1187            if ( ( filterElement != null ) && ( elseFilterElement != null ) ) {
    1188                throw new XMLParsingException( "Element 'Rule' may contain a 'Filter'- or "
    1189                                               + "an 'ElseFilter'-element, but not both!" );
    1190            }
    1191
    1192            // optional: <MinScaleDenominator>
    1193            double min = XMLTools.getNodeAsDouble( element, "sld:MinScaleDenominator", nsContext, 0.0 );
    1194            // optional: <MaxScaleDenominator>
    1195            double max = XMLTools.getNodeAsDouble( element, "sld:MaxScaleDenominator", nsContext, 9E99 );
    1196
    1197            // optional: different Symbolizer-elements
    1198            NodeList symbolizerNL = element.getChildNodes();
    1199            ArrayList<Symbolizer> symbolizerList = new ArrayList<Symbolizer>( symbolizerNL.getLength() );
    1200
    1201            for ( int i = 0; i < symbolizerNL.getLength(); i++ ) {
    1202                if ( symbolizerNL.item( i ) instanceof Element ) {
    1203                    Element symbolizerElement = (Element) symbolizerNL.item( i );
    1204                    String namespace = symbolizerElement.getNamespaceURI();
    1205
    1206                    if ( !CommonNamespaces.SLDNS.toString().equals( namespace )
    1207                         && !CommonNamespaces.SENS.toString().equals( namespace ) ) {
    1208                        continue;
    1209                    }
    1210
    1211                    String symbolizerName = symbolizerElement.getLocalName();
    1212
    1213                    if ( symbolizerName.equals( "LineSymbolizer" ) ) {
    1214                        symbolizerList.add( createLineSymbolizer( symbolizerElement, min, max ) );
    1215                    } else if ( symbolizerName.equals( "PointSymbolizer" ) ) {
    1216                        symbolizerList.add( createPointSymbolizer( symbolizerElement, min, max ) );
    1217                    } else if ( symbolizerName.equals( "PolygonSymbolizer" ) ) {
    1218                        symbolizerList.add( createPolygonSymbolizer( symbolizerElement, min, max ) );
    1219                    } else if ( symbolizerName.equals( "TextSymbolizer" ) ) {
    1220                        symbolizerList.add( createTextSymbolizer( symbolizerElement, min, max ) );
    1221                    } else if ( symbolizerName.equals( "RasterSymbolizer" ) ) {
    1222                        symbolizerList.add( createRasterSymbolizer( symbolizerElement, min, max ) );
    1223                    }
    1224                }
    1225            }
    1226
    1227            Symbolizer[] symbolizers = symbolizerList.toArray( new Symbolizer[symbolizerList.size()] );
    1228
    1229            return new Rule( symbolizers, name, title, abstract_, legendGraphic, filter, isAnElseFilter, min, max );
    1230        }
    1231
    1232        /**
    1233         *
    1234         * @param symbolizerElement
    1235         * @return {@link Symbolizer}
    1236         * @throws XMLParsingException
    1237         */
    1238        public static Symbolizer createSymbolizer( Element symbolizerElement ) throws XMLParsingException {
    1239            String symbolizerName = symbolizerElement.getLocalName();
    1240
    1241            double min = 0;
    1242            double max = Double.MAX_VALUE;
    1243            Symbolizer symbolizer = null;
    1244            if ( symbolizerName.equals( "LineSymbolizer" ) ) {
    1245                symbolizer = createLineSymbolizer( symbolizerElement, min, max );
    1246            } else if ( symbolizerName.equals( "PointSymbolizer" ) ) {
    1247                symbolizer = createPointSymbolizer( symbolizerElement, min, max );
    1248            } else if ( symbolizerName.equals( "PolygonSymbolizer" ) ) {
    1249                symbolizer = createPolygonSymbolizer( symbolizerElement, min, max );
    1250            } else if ( symbolizerName.equals( "TextSymbolizer" ) ) {
    1251                symbolizer = createTextSymbolizer( symbolizerElement, min, max );
    1252            } else if ( symbolizerName.equals( "RasterSymbolizer" ) ) {
    1253                symbolizer = createRasterSymbolizer( symbolizerElement, min, max );
    1254            }
    1255            return symbolizer;
    1256        }
    1257
    1258        /**
    1259         * Creates a <tt>PointSymbolizer</tt>-instance according to the contents of the DOM-subtree starting at the given
    1260         * 'PointSymbolizer'-<tt>Element</tt>.
    1261         * <p>
    1262         *
    1263         * @param element
    1264         *            the 'PointSymbolizer'-<tt>Element</tt>
    1265         * @param min
    1266         *            scale-constraint to be used
    1267         * @param max
    1268         *            scale-constraint to be used
    1269         * @throws XMLParsingException
    1270         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1271         * @return the constructed <tt>PointSymbolizer</tt>-instance
    1272         */
    1273        private static PointSymbolizer createPointSymbolizer( Element element, double min, double max )
    1274                                throws XMLParsingException {
    1275
    1276            // optional: <Geometry>
    1277            Geometry geometry = null;
    1278            Element geometryElement = XMLTools.getChildElement( "Geometry", CommonNamespaces.SLDNS, element );
    1279
    1280            if ( geometryElement != null ) {
    1281                geometry = createGeometry( geometryElement );
    1282            }
    1283
    1284            // optional: <Graphic>
    1285            Graphic graphic = null;
    1286            Element graphicElement = XMLTools.getChildElement( "Graphic", CommonNamespaces.SLDNS, element );
    1287
    1288            if ( graphicElement != null ) {
    1289                graphic = createGraphic( graphicElement );
    1290            }
    1291
    1292            PointSymbolizer ps = null;
    1293            String respClass = XMLTools.getAttrValue( element, null, "responsibleClass", null );
    1294            if ( respClass == null ) {
    1295                ps = new PointSymbolizer( graphic, geometry, min, max );
    1296            } else {
    1297                ps = new PointSymbolizer( graphic, geometry, respClass, min, max );
    1298            }
    1299
    1300            return ps;
    1301        }
    1302
    1303        /**
    1304         * Creates a <tt>LineSymbolizer</tt>-instance according to the contents of the DOM-subtree starting at the given
    1305         * 'LineSymbolizer'-<tt>Element</tt>.
    1306         * <p>
    1307         *
    1308         * @param element
    1309         *            the 'LineSymbolizer'-<tt>Element</tt>
    1310         * @param min
    1311         *            scale-constraint to be used
    1312         * @param max
    1313         *            scale-constraint to be used
    1314         * @throws XMLParsingException
    1315         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1316         * @return the constructed <tt>LineSymbolizer</tt>-instance
    1317         */
    1318        private static LineSymbolizer createLineSymbolizer( Element element, double min, double max )
    1319                                throws XMLParsingException {
    1320
    1321            // optional: <Geometry>
    1322            Geometry geometry = null;
    1323            Element geometryElement = XMLTools.getChildElement( "Geometry", CommonNamespaces.SLDNS, element );
    1324
    1325            if ( geometryElement != null ) {
    1326                geometry = createGeometry( geometryElement );
    1327            }
    1328
    1329            // optional: <Stroke>
    1330            Stroke stroke = null;
    1331            Element strokeElement = XMLTools.getChildElement( "Stroke", CommonNamespaces.SLDNS, element );
    1332
    1333            if ( strokeElement != null ) {
    1334                stroke = createStroke( strokeElement );
    1335            }
    1336
    1337            LineSymbolizer ls = null;
    1338            String respClass = XMLTools.getAttrValue( element, null, "responsibleClass", null );
    1339            if ( respClass == null ) {
    1340                ls = new LineSymbolizer( stroke, geometry, min, max );
    1341            } else {
    1342                ls = new LineSymbolizer( stroke, geometry, respClass, min, max );
    1343            }
    1344            return ls;
    1345        }
    1346
    1347        /**
    1348         * Creates a <tt>PolygonSymbolizer</tt>-instance according to the contents of the DOM-subtree starting at the given
    1349         * 'PolygonSymbolizer'-<tt>Element</tt>.
    1350         * <p>
    1351         *
    1352         * @param element
    1353         *            the 'PolygonSymbolizer'-<tt>Element</tt>
    1354         * @param min
    1355         *            scale-constraint to be used
    1356         * @param max
    1357         *            scale-constraint to be used
    1358         * @throws XMLParsingException
    1359         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1360         * @return the constructed <tt>PolygonSymbolizer</tt>-instance
    1361         */
    1362        private static PolygonSymbolizer createPolygonSymbolizer( Element element, double min, double max )
    1363                                throws XMLParsingException {
    1364            // optional: <Geometry>
    1365            Geometry geometry = null;
    1366            Element geometryElement = XMLTools.getChildElement( "Geometry", CommonNamespaces.SLDNS, element );
    1367
    1368            if ( geometryElement != null ) {
    1369                geometry = createGeometry( geometryElement );
    1370            }
    1371
    1372            // optional: <Fill>
    1373            Fill fill = null;
    1374            Element fillElement = XMLTools.getChildElement( "Fill", CommonNamespaces.SLDNS, element );
    1375
    1376            if ( fillElement != null ) {
    1377                fill = createFill( fillElement );
    1378            }
    1379
    1380            // optional: <Stroke>
    1381            Stroke stroke = null;
    1382            Element strokeElement = XMLTools.getChildElement( "Stroke", CommonNamespaces.SLDNS, element );
    1383
    1384            if ( strokeElement != null ) {
    1385                stroke = createStroke( strokeElement );
    1386            }
    1387
    1388            PolygonSymbolizer ps = null;
    1389            String respClass = XMLTools.getAttrValue( element, null, "responsibleClass", null );
    1390            if ( respClass == null ) {
    1391                ps = new PolygonSymbolizer( fill, stroke, geometry, min, max );
    1392            } else {
    1393                ps = new PolygonSymbolizer( fill, stroke, geometry, respClass, min, max );
    1394            }
    1395
    1396            return ps;
    1397        }
    1398
    1399        /**
    1400         * Creates a <tt>Geometry</tt>-instance according to the contents of the DOM-subtree starting at the given
    1401         * 'Geometry'-<tt>Element</tt>.
    1402         *
    1403         * @param element
    1404         *            the 'Geometry'-<tt>Element</tt>
    1405         * @throws XMLParsingException
    1406         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1407         * @return the constructed <tt>Geometry</tt>-instance
    1408         */
    1409        private static Geometry createGeometry( Element element )
    1410                                throws XMLParsingException {
    1411            Geometry geometry = null;
    1412
    1413            // required: <PropertyName>
    1414            Element propertyNameElement = XMLTools.getRequiredChildElement( "PropertyName", ogcNS, element );
    1415
    1416            // optional: <Function>
    1417            Element functionElement = XMLTools.getChildElement( "Function", ogcNS, propertyNameElement );
    1418
    1419            // just a property name exists
    1420            if ( functionElement == null ) {
    1421                Node node = XMLTools.getNode( propertyNameElement, "/text()", nsContext );
    1422                PropertyPath pp = OGCDocument.parsePropertyPath( (Text) node );
    1423                geometry = new Geometry( pp, null );
    1424            } else {
    1425                // expressions are not supported
    1426            }
    1427
    1428            return geometry;
    1429        }
    1430
    1431        /**
    1432         * Creates a <tt>Fill</tt>-instance according to the contents of the DOM-subtree starting at the given 'Fill'-
    1433         * <tt>Element</tt>.
    1434         * <p>
    1435         *
    1436         * @param element
    1437         *            the 'Fill'-<tt>Element</tt>
    1438         * @throws XMLParsingException
    1439         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1440         * @return the constructed <tt>Fill</tt>-instance
    1441         */
    1442        private static Fill createFill( Element element )
    1443                                throws XMLParsingException {
    1444            // optional: <GraphicFill>
    1445            GraphicFill graphicFill = null;
    1446            Element graphicFillElement = XMLTools.getChildElement( "GraphicFill", CommonNamespaces.SLDNS, element );
    1447
    1448            if ( graphicFillElement != null ) {
    1449                graphicFill = createGraphicFill( graphicFillElement );
    1450            }
    1451
    1452            // optional: <CssParameter>s
    1453            ElementList nl = XMLTools.getChildElements( "CssParameter", CommonNamespaces.SLDNS, element );
    1454            HashMap<String, Object> cssParams = new HashMap<String, Object>( nl.getLength() );
    1455
    1456            for ( int i = 0; i < nl.getLength(); i++ ) {
    1457                CssParameter cssParam = createCssParameter( nl.item( i ) );
    1458                cssParams.put( cssParam.getName(), cssParam );
    1459            }
    1460
    1461            return new Fill( cssParams, graphicFill );
    1462        }
    1463
    1464        /**
    1465         * Creates a <tt>LegendGraphic</tt>-instance according to the contents of the DOM-subtree starting at the given
    1466         * 'LegendGraphic'-element.
    1467         * <p>
    1468         *
    1469         * @param element
    1470         *            the 'LegendGraphic'-<tt>Element</tt>
    1471         * @throws XMLParsingException
    1472         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1473         * @return the constructed <tt>Graphic</tt>-instance
    1474         */
    1475        private static LegendGraphic createLegendGraphic( Element element )
    1476                                throws XMLParsingException {
    1477            // required: <Graphic>
    1478            Element graphicElement = XMLTools.getRequiredChildElement( "Graphic", CommonNamespaces.SLDNS, element );
    1479            Graphic graphic = createGraphic( graphicElement );
    1480
    1481            return new LegendGraphic( graphic );
    1482        }
    1483
    1484        /**
    1485         * Creates an <tt>ExternalGraphic</tt>-instance according to the contents of the DOM-subtree starting at the given
    1486         * 'ExternalGraphic'-<tt>Element</tt>.
    1487         * <p>
    1488         *
    1489         * @param element
    1490         *            the 'ExternalGraphic'-<tt>Element</tt>
    1491         * @throws XMLParsingException
    1492         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1493         * @return the constructed <tt>ExternalGraphic</tt>-instance
    1494         */
    1495        private static ExternalGraphic createExternalGraphic( Element element )
    1496                                throws XMLParsingException {
    1497            // required: <OnlineResource>
    1498            Element onlineResourceElement = XMLTools.getRequiredChildElement( "OnlineResource", CommonNamespaces.SLDNS,
    1499                                                                              element );
    1500
    1501            // required: href-Attribute (in <OnlineResource>)
    1502            String href = XMLTools.getRequiredAttrValue( "href", xlnNS, onlineResourceElement );
    1503            URL url = null;
    1504            try {
    1505                url = sldDoc.resolve( href );
    1506            } catch ( MalformedURLException e ) {
    1507                LOG.logDebug( e.getMessage(), e );
    1508                throw new XMLParsingException( "Value ('" + href + "') of attribute 'href' of "
    1509                                               + "element 'OnlineResoure' does not denote a valid URL" );
    1510            }
    1511
    1512            // required: <Format> (in <OnlineResource>)
    1513            String format = XMLTools.getRequiredStringValue( "Format", CommonNamespaces.SLDNS, element );
    1514
    1515            return new ExternalGraphic( format, url );
    1516        }
    1517
    1518        /**
    1519         * Creates a <tt>Mark</tt>-instance according to the contents of the DOM-subtree starting at the given 'Mark'-
    1520         * <tt>Element</tt>.
    1521         * <p>
    1522         *
    1523         * @param element
    1524         *            the 'Mark'-<tt>Element</tt>
    1525         * @throws XMLParsingException
    1526         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1527         * @return the constructed <tt>Mark</tt>-instance
    1528         */
    1529        private static Mark createMark( Element element )
    1530                                throws XMLParsingException {
    1531            Stroke stroke = null;
    1532            Fill fill = null;
    1533
    1534            // optional: <WellKnownName>
    1535            String wkn = XMLTools.getStringValue( "WellKnownName", CommonNamespaces.SLDNS, element, null );
    1536
    1537            // optional: <Stroke>
    1538            Element strokeElement = XMLTools.getChildElement( "Stroke", CommonNamespaces.SLDNS, element );
    1539
    1540            if ( strokeElement != null ) {
    1541                stroke = createStroke( strokeElement );
    1542            }
    1543
    1544            // optional: <Fill>
    1545            Element fillElement = XMLTools.getChildElement( "Fill", CommonNamespaces.SLDNS, element );
    1546
    1547            if ( fillElement != null ) {
    1548                fill = createFill( fillElement );
    1549            }
    1550
    1551            return new Mark( wkn, stroke, fill );
    1552        }
    1553
    1554        /**
    1555         * Creates a <tt>Stroke</tt>-instance according to the contents of the DOM-subtree starting at the given 'Stroke'-
    1556         * <tt>Element</tt>.
    1557         * <p>
    1558         *
    1559         * @param element
    1560         *            the 'Stroke'-<tt>Element</tt>
    1561         * @throws XMLParsingException
    1562         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1563         * @return the constructed <tt>Stroke</tt>-instance
    1564         */
    1565        private static Stroke createStroke( Element element )
    1566                                throws XMLParsingException {
    1567            GraphicFill gf = null;
    1568            GraphicStroke gs = null;
    1569
    1570            // optional: <GraphicFill>
    1571            Element gfElement = XMLTools.getChildElement( "GraphicFill", CommonNamespaces.SLDNS, element );
    1572
    1573            if ( gfElement != null ) {
    1574                gf = createGraphicFill( gfElement );
    1575            }
    1576
    1577            // optional: <GraphicStroke>
    1578            Element gsElement = XMLTools.getChildElement( "GraphicStroke", CommonNamespaces.SLDNS, element );
    1579
    1580            if ( gsElement != null ) {
    1581                gs = createGraphicStroke( gsElement );
    1582            }
    1583
    1584            // optional: <CssParameter>s
    1585            ElementList nl = XMLTools.getChildElements( "CssParameter", CommonNamespaces.SLDNS, element );
    1586            HashMap<String, Object> cssParams = new HashMap<String, Object>( nl.getLength() );
    1587
    1588            for ( int i = 0; i < nl.getLength(); i++ ) {
    1589                CssParameter cssParam = createCssParameter( nl.item( i ) );
    1590                cssParams.put( cssParam.getName(), cssParam );
    1591            }
    1592
    1593            return new Stroke( cssParams, gs, gf );
    1594        }
    1595
    1596        /**
    1597         * Creates a <tt>GraphicFill</tt>-instance according to the contents of the DOM-subtree starting at the given
    1598         * 'GraphicFill'-<tt>Element</tt>.
    1599         * <p>
    1600         *
    1601         * @param element
    1602         *            the 'GraphicFill'-<tt>Element</tt>
    1603         * @throws XMLParsingException
    1604         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1605         * @return the constructed <tt>GraphicFill</tt>-instance
    1606         */
    1607        private static GraphicFill createGraphicFill( Element element )
    1608                                throws XMLParsingException {
    1609            // required: <Graphic>
    1610            Element graphicElement = XMLTools.getRequiredChildElement( "Graphic", CommonNamespaces.SLDNS, element );
    1611            Graphic graphic = createGraphic( graphicElement );
    1612
    1613            return new GraphicFill( graphic );
    1614        }
    1615
    1616        /**
    1617         * Creates a <tt>GraphicStroke</tt>-instance according to the contents of the DOM-subtree starting at the given
    1618         * 'GraphicStroke'-<tt>Element</tt>.
    1619         * <p>
    1620         *
    1621         * @param element
    1622         *            the 'GraphicStroke'-<tt>Element</tt>
    1623         * @throws XMLParsingException
    1624         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1625         * @return the constructed <tt>GraphicStroke</tt>-instance
    1626         */
    1627        private static GraphicStroke createGraphicStroke( Element element )
    1628                                throws XMLParsingException {
    1629            // required: <Graphic>
    1630            Element graphicElement = XMLTools.getRequiredChildElement( "Graphic", CommonNamespaces.SLDNS, element );
    1631            Graphic graphic = createGraphic( graphicElement );
    1632
    1633            return new GraphicStroke( graphic );
    1634        }
    1635
    1636        /**
    1637         * Creates a <tt>Graphic</tt>-instance according to the contents of the DOM-subtree starting at the given
    1638         * 'Graphic'-element.
    1639         * <p>
    1640         *
    1641         * @param element
    1642         *            the 'Graphic'-<tt>Element</tt>
    1643         * @throws XMLParsingException
    1644         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1645         * @return the constructed <tt>Graphic</tt>-instance
    1646         */
    1647        private static Graphic createGraphic( Element element )
    1648                                throws XMLParsingException {
    1649
    1650            // optional: <Opacity>
    1651            ParameterValueType opacity = null;
    1652            // optional: <Size>
    1653            ParameterValueType size = null;
    1654            // optional: <Rotation>
    1655            ParameterValueType rotation = null;
    1656
    1657            // optional: <ExternalGraphic>s / <Mark>s
    1658            NodeList nodelist = element.getChildNodes();
    1659            ArrayList<Object> marksAndExtGraphicsList = new ArrayList<Object>();
    1660
    1661            for ( int i = 0; i < nodelist.getLength(); i++ ) {
    1662                if ( nodelist.item( i ) instanceof Element ) {
    1663                    Element child = (Element) nodelist.item( i );
    1664                    String namespace = child.getNamespaceURI();
    1665
    1666                    if ( !CommonNamespaces.SLDNS.toString().equals( namespace ) ) {
    1667                        continue;
    1668                    }
    1669
    1670                    String childName = child.getLocalName();
    1671
    1672                    if ( childName.equals( "ExternalGraphic" ) ) {
    1673                        marksAndExtGraphicsList.add( createExternalGraphic( child ) );
    1674                    } else if ( childName.equals( "Mark" ) ) {
    1675                        marksAndExtGraphicsList.add( createMark( child ) );
    1676                    } else if ( childName.equals( "Opacity" ) ) {
    1677                        opacity = createParameterValueType( child );
    1678                    } else if ( childName.equals( "Size" ) ) {
    1679                        size = createParameterValueType( child );
    1680                    } else if ( childName.equals( "Rotation" ) ) {
    1681                        rotation = createParameterValueType( child );
    1682                    }
    1683                }
    1684            }
    1685
    1686            Object[] marksAndExtGraphics = marksAndExtGraphicsList.toArray( new Object[marksAndExtGraphicsList.size()] );
    1687
    1688            return new Graphic( marksAndExtGraphics, opacity, size, rotation );
    1689        }
    1690
    1691        /**
    1692         * Creates a <tt>CssParameter</tt>-instance according to the contents of the DOM-subtree starting at the given
    1693         * 'CssParameter'-<tt>Element</tt>.
    1694         * <p>
    1695         *
    1696         * @param element
    1697         *            the 'CssParamter'-<tt>Element</tt>
    1698         * @throws XMLParsingException
    1699         *             if a syntactic or semantic error in the DOM-subtree is encountered
    1700         * @return the constructed <tt>CssParameter</tt>-instance
    1701         */
    1702        private static CssParameter createCssParameter( Element element )
    1703                                throws XMLParsingException {
    1704            // required: name-Attribute
    1705            String name = XMLTools.getRequiredAttrValue( "name", null, element );
    1706            ParameterValueType pvt = createParameterValueType( element );
    1707
    1708            return ( new CssParameter( name, pvt ) );
    1709        }
    1710
    1711        /**
    1712         * <code>TresholdsBelongTo</code> enumerates values possibly belonging to <code>ThreshholdsBelongToType</code>.
    1713         *
    1714         * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
    1715         * @author last edited by: $Author: aschmitz $
    1716         *
    1717         * @version $Revision: 25265 $, $Date: 2010-07-13 08:45:57 +0200 (Di, 13. Jul 2010) $
    1718         */
    1719        public enum ThresholdsBelongTo {
    1720            /**
    1721             * <code>"succeeding"</code>
    1722             */
    1723            SUCCEEDING,
    1724            /**
    1725             * <code>"preceding"</code>
    1726             */
    1727            PRECEDING
    1728        }
    1729
    1730        /**
    1731         * <code>Mode</code> is the ModeType from the Symbology Encoding Schema.
    1732         *
    1733         * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
    1734         * @author last edited by: $Author: aschmitz $
    1735         *
    1736         * @version $Revision: 25265 $, $Date: 2010-07-13 08:45:57 +0200 (Di, 13. Jul 2010) $
    1737         */
    1738        public enum Mode {
    1739            /**
    1740             * <code>"linear"</code>
    1741             */
    1742            LINEAR,
    1743            /**
    1744             * <code>"cosine"</code>
    1745             */
    1746            COSINE,
    1747            /**
    1748             * <code>"cubic"</code>
    1749             */
    1750            CUBIC
    1751        }
    1752
    1753        /**
    1754         * <code>Method</code> is the MethodType from the Symbology encoding Schema.
    1755         *
    1756         * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
    1757         * @author last edited by: $Author: aschmitz $
    1758         *
    1759         * @version $Revision: 25265 $, $Date: 2010-07-13 08:45:57 +0200 (Di, 13. Jul 2010) $
    1760         */
    1761        public enum Method {
    1762            /**
    1763             * <code>"numeric"</code>
    1764             */
    1765            NUMERIC,
    1766            /**
    1767             * <code>"color"</code>
    1768             */
    1769            COLOR
    1770        }
    1771
    1772    }