001    //$HeadURL: http://svn.wald.intevation.org/svn/deegree/base/trunk/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: 30390 $ $Date: 2011-04-07 10:21:25 +0200 (Thu, 07 Apr 2011) $
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 )
1239                                throws XMLParsingException {
1240            String symbolizerName = symbolizerElement.getLocalName();
1241    
1242            double min = 0;
1243            double max = Double.MAX_VALUE;
1244            Symbolizer symbolizer = null;
1245            if ( symbolizerName.equals( "LineSymbolizer" ) ) {
1246                symbolizer = createLineSymbolizer( symbolizerElement, min, max );
1247            } else if ( symbolizerName.equals( "PointSymbolizer" ) ) {
1248                symbolizer = createPointSymbolizer( symbolizerElement, min, max );
1249            } else if ( symbolizerName.equals( "PolygonSymbolizer" ) ) {
1250                symbolizer = createPolygonSymbolizer( symbolizerElement, min, max );
1251            } else if ( symbolizerName.equals( "TextSymbolizer" ) ) {
1252                symbolizer = createTextSymbolizer( symbolizerElement, min, max );
1253            } else if ( symbolizerName.equals( "RasterSymbolizer" ) ) {
1254                symbolizer = createRasterSymbolizer( symbolizerElement, min, max );
1255            }
1256            return symbolizer;
1257        }
1258    
1259        /**
1260         * Creates a <tt>PointSymbolizer</tt>-instance according to the contents of the DOM-subtree starting at the given
1261         * 'PointSymbolizer'-<tt>Element</tt>.
1262         * <p>
1263         * 
1264         * @param element
1265         *            the 'PointSymbolizer'-<tt>Element</tt>
1266         * @param min
1267         *            scale-constraint to be used
1268         * @param max
1269         *            scale-constraint to be used
1270         * @throws XMLParsingException
1271         *             if a syntactic or semantic error in the DOM-subtree is encountered
1272         * @return the constructed <tt>PointSymbolizer</tt>-instance
1273         */
1274        private static PointSymbolizer createPointSymbolizer( Element element, double min, double max )
1275                                throws XMLParsingException {
1276    
1277            // optional: <Geometry>
1278            Geometry geometry = null;
1279            Element geometryElement = XMLTools.getChildElement( "Geometry", CommonNamespaces.SLDNS, element );
1280    
1281            if ( geometryElement != null ) {
1282                geometry = createGeometry( geometryElement );
1283            }
1284    
1285            // optional: <Graphic>
1286            Graphic graphic = null;
1287            Element graphicElement = XMLTools.getChildElement( "Graphic", CommonNamespaces.SLDNS, element );
1288    
1289            if ( graphicElement != null ) {
1290                graphic = createGraphic( graphicElement );
1291            }
1292    
1293            PointSymbolizer ps = null;
1294            String respClass = XMLTools.getAttrValue( element, null, "responsibleClass", null );
1295            if ( respClass == null ) {
1296                ps = new PointSymbolizer( graphic, geometry, min, max );
1297            } else {
1298                ps = new PointSymbolizer( graphic, geometry, respClass, min, max );
1299            }
1300    
1301            return ps;
1302        }
1303    
1304        /**
1305         * Creates a <tt>LineSymbolizer</tt>-instance according to the contents of the DOM-subtree starting at the given
1306         * 'LineSymbolizer'-<tt>Element</tt>.
1307         * <p>
1308         * 
1309         * @param element
1310         *            the 'LineSymbolizer'-<tt>Element</tt>
1311         * @param min
1312         *            scale-constraint to be used
1313         * @param max
1314         *            scale-constraint to be used
1315         * @throws XMLParsingException
1316         *             if a syntactic or semantic error in the DOM-subtree is encountered
1317         * @return the constructed <tt>LineSymbolizer</tt>-instance
1318         */
1319        private static LineSymbolizer createLineSymbolizer( Element element, double min, double max )
1320                                throws XMLParsingException {
1321    
1322            // optional: <Geometry>
1323            Geometry geometry = null;
1324            Element geometryElement = XMLTools.getChildElement( "Geometry", CommonNamespaces.SLDNS, element );
1325    
1326            if ( geometryElement != null ) {
1327                geometry = createGeometry( geometryElement );
1328            }
1329    
1330            // optional: <Stroke>
1331            Stroke stroke = null;
1332            Element strokeElement = XMLTools.getChildElement( "Stroke", CommonNamespaces.SLDNS, element );
1333    
1334            if ( strokeElement != null ) {
1335                stroke = createStroke( strokeElement );
1336            }
1337    
1338            LineSymbolizer ls = null;
1339            String respClass = XMLTools.getAttrValue( element, null, "responsibleClass", null );
1340            if ( respClass == null ) {
1341                ls = new LineSymbolizer( stroke, geometry, min, max );
1342            } else {
1343                ls = new LineSymbolizer( stroke, geometry, respClass, min, max );
1344            }
1345            return ls;
1346        }
1347    
1348        /**
1349         * Creates a <tt>PolygonSymbolizer</tt>-instance according to the contents of the DOM-subtree starting at the given
1350         * 'PolygonSymbolizer'-<tt>Element</tt>.
1351         * <p>
1352         * 
1353         * @param element
1354         *            the 'PolygonSymbolizer'-<tt>Element</tt>
1355         * @param min
1356         *            scale-constraint to be used
1357         * @param max
1358         *            scale-constraint to be used
1359         * @throws XMLParsingException
1360         *             if a syntactic or semantic error in the DOM-subtree is encountered
1361         * @return the constructed <tt>PolygonSymbolizer</tt>-instance
1362         */
1363        private static PolygonSymbolizer createPolygonSymbolizer( Element element, double min, double max )
1364                                throws XMLParsingException {
1365            // optional: <Geometry>
1366            Geometry geometry = null;
1367            Element geometryElement = XMLTools.getChildElement( "Geometry", CommonNamespaces.SLDNS, element );
1368    
1369            if ( geometryElement != null ) {
1370                geometry = createGeometry( geometryElement );
1371            }
1372    
1373            // optional: <Fill>
1374            Fill fill = null;
1375            Element fillElement = XMLTools.getChildElement( "Fill", CommonNamespaces.SLDNS, element );
1376    
1377            if ( fillElement != null ) {
1378                fill = createFill( fillElement );
1379            }
1380    
1381            // optional: <Stroke>
1382            Stroke stroke = null;
1383            Element strokeElement = XMLTools.getChildElement( "Stroke", CommonNamespaces.SLDNS, element );
1384    
1385            if ( strokeElement != null ) {
1386                stroke = createStroke( strokeElement );
1387            }
1388    
1389            PolygonSymbolizer ps = null;
1390            String respClass = XMLTools.getAttrValue( element, null, "responsibleClass", null );
1391            if ( respClass == null ) {
1392                ps = new PolygonSymbolizer( fill, stroke, geometry, min, max );
1393            } else {
1394                ps = new PolygonSymbolizer( fill, stroke, geometry, respClass, min, max );
1395            }
1396    
1397            return ps;
1398        }
1399    
1400        /**
1401         * Creates a <tt>Geometry</tt>-instance according to the contents of the DOM-subtree starting at the given
1402         * 'Geometry'-<tt>Element</tt>.
1403         * 
1404         * @param element
1405         *            the 'Geometry'-<tt>Element</tt>
1406         * @throws XMLParsingException
1407         *             if a syntactic or semantic error in the DOM-subtree is encountered
1408         * @return the constructed <tt>Geometry</tt>-instance
1409         */
1410        private static Geometry createGeometry( Element element )
1411                                throws XMLParsingException {
1412            Geometry geometry = null;
1413    
1414            // required: <PropertyName>
1415            Element propertyNameElement = XMLTools.getRequiredChildElement( "PropertyName", ogcNS, element );
1416    
1417            // optional: <Function>
1418            Element functionElement = XMLTools.getChildElement( "Function", ogcNS, propertyNameElement );
1419    
1420            // just a property name exists
1421            if ( functionElement == null ) {
1422                Node node = XMLTools.getNode( propertyNameElement, "/text()", nsContext );
1423                PropertyPath pp = OGCDocument.parsePropertyPath( (Text) node );
1424                geometry = new Geometry( pp, null );
1425            } else {
1426                // expressions are not supported
1427            }
1428    
1429            return geometry;
1430        }
1431    
1432        /**
1433         * Creates a <tt>Fill</tt>-instance according to the contents of the DOM-subtree starting at the given 'Fill'-
1434         * <tt>Element</tt>.
1435         * <p>
1436         * 
1437         * @param element
1438         *            the 'Fill'-<tt>Element</tt>
1439         * @throws XMLParsingException
1440         *             if a syntactic or semantic error in the DOM-subtree is encountered
1441         * @return the constructed <tt>Fill</tt>-instance
1442         */
1443        private static Fill createFill( Element element )
1444                                throws XMLParsingException {
1445            // optional: <GraphicFill>
1446            GraphicFill graphicFill = null;
1447            Element graphicFillElement = XMLTools.getChildElement( "GraphicFill", CommonNamespaces.SLDNS, element );
1448    
1449            if ( graphicFillElement != null ) {
1450                graphicFill = createGraphicFill( graphicFillElement );
1451            }
1452    
1453            // optional: <CssParameter>s
1454            ElementList nl = XMLTools.getChildElements( "CssParameter", CommonNamespaces.SLDNS, element );
1455            HashMap<String, Object> cssParams = new HashMap<String, Object>( nl.getLength() );
1456    
1457            for ( int i = 0; i < nl.getLength(); i++ ) {
1458                CssParameter cssParam = createCssParameter( nl.item( i ) );
1459                cssParams.put( cssParam.getName(), cssParam );
1460            }
1461    
1462            return new Fill( cssParams, graphicFill );
1463        }
1464    
1465        /**
1466         * Creates a <tt>LegendGraphic</tt>-instance according to the contents of the DOM-subtree starting at the given
1467         * 'LegendGraphic'-element.
1468         * <p>
1469         * 
1470         * @param element
1471         *            the 'LegendGraphic'-<tt>Element</tt>
1472         * @throws XMLParsingException
1473         *             if a syntactic or semantic error in the DOM-subtree is encountered
1474         * @return the constructed <tt>Graphic</tt>-instance
1475         */
1476        private static LegendGraphic createLegendGraphic( Element element )
1477                                throws XMLParsingException {
1478            // required: <Graphic>
1479            Element graphicElement = XMLTools.getRequiredChildElement( "Graphic", CommonNamespaces.SLDNS, element );
1480            Graphic graphic = createGraphic( graphicElement );
1481    
1482            return new LegendGraphic( graphic );
1483        }
1484    
1485        /**
1486         * Creates an <tt>ExternalGraphic</tt>-instance according to the contents of the DOM-subtree starting at the given
1487         * 'ExternalGraphic'-<tt>Element</tt>.
1488         * <p>
1489         * 
1490         * @param element
1491         *            the 'ExternalGraphic'-<tt>Element</tt>
1492         * @throws XMLParsingException
1493         *             if a syntactic or semantic error in the DOM-subtree is encountered
1494         * @return the constructed <tt>ExternalGraphic</tt>-instance
1495         */
1496        private static ExternalGraphic createExternalGraphic( Element element )
1497                                throws XMLParsingException {
1498            // required: <OnlineResource>
1499            Element onlineResourceElement = XMLTools.getRequiredChildElement( "OnlineResource", CommonNamespaces.SLDNS,
1500                                                                              element );
1501    
1502            // required: href-Attribute (in <OnlineResource>)
1503            String href = XMLTools.getRequiredAttrValue( "href", xlnNS, onlineResourceElement );
1504            URL url = null;
1505            try {
1506                url = sldDoc.resolve( href );
1507            } catch ( MalformedURLException e ) {
1508                LOG.logDebug( e.getMessage(), e );
1509                throw new XMLParsingException( "Value ('" + href + "') of attribute 'href' of "
1510                                               + "element 'OnlineResoure' does not denote a valid URL" );
1511            }
1512    
1513            // required: <Format> (in <OnlineResource>)
1514            String format = XMLTools.getRequiredStringValue( "Format", CommonNamespaces.SLDNS, element );
1515    
1516            return new ExternalGraphic( format, url );
1517        }
1518    
1519        /**
1520         * Creates a <tt>Mark</tt>-instance according to the contents of the DOM-subtree starting at the given 'Mark'-
1521         * <tt>Element</tt>.
1522         * <p>
1523         * 
1524         * @param element
1525         *            the 'Mark'-<tt>Element</tt>
1526         * @throws XMLParsingException
1527         *             if a syntactic or semantic error in the DOM-subtree is encountered
1528         * @return the constructed <tt>Mark</tt>-instance
1529         */
1530        private static Mark createMark( Element element )
1531                                throws XMLParsingException {
1532            Stroke stroke = null;
1533            Fill fill = null;
1534    
1535            // optional: <WellKnownName>
1536            String wkn = XMLTools.getStringValue( "WellKnownName", CommonNamespaces.SLDNS, element, null );
1537    
1538            // optional: <Stroke>
1539            Element strokeElement = XMLTools.getChildElement( "Stroke", CommonNamespaces.SLDNS, element );
1540    
1541            if ( strokeElement != null ) {
1542                stroke = createStroke( strokeElement );
1543            }
1544    
1545            // optional: <Fill>
1546            Element fillElement = XMLTools.getChildElement( "Fill", CommonNamespaces.SLDNS, element );
1547    
1548            if ( fillElement != null ) {
1549                fill = createFill( fillElement );
1550            }
1551    
1552            return new Mark( wkn, stroke, fill );
1553        }
1554    
1555        /**
1556         * Creates a <tt>Stroke</tt>-instance according to the contents of the DOM-subtree starting at the given 'Stroke'-
1557         * <tt>Element</tt>.
1558         * <p>
1559         * 
1560         * @param element
1561         *            the 'Stroke'-<tt>Element</tt>
1562         * @throws XMLParsingException
1563         *             if a syntactic or semantic error in the DOM-subtree is encountered
1564         * @return the constructed <tt>Stroke</tt>-instance
1565         */
1566        private static Stroke createStroke( Element element )
1567                                throws XMLParsingException {
1568            GraphicFill gf = null;
1569            GraphicStroke gs = null;
1570    
1571            // optional: <GraphicFill>
1572            Element gfElement = XMLTools.getChildElement( "GraphicFill", CommonNamespaces.SLDNS, element );
1573    
1574            if ( gfElement != null ) {
1575                gf = createGraphicFill( gfElement );
1576            }
1577    
1578            // optional: <GraphicStroke>
1579            Element gsElement = XMLTools.getChildElement( "GraphicStroke", CommonNamespaces.SLDNS, element );
1580    
1581            if ( gsElement != null ) {
1582                gs = createGraphicStroke( gsElement );
1583            }
1584    
1585            // optional: <CssParameter>s
1586            ElementList nl = XMLTools.getChildElements( "CssParameter", CommonNamespaces.SLDNS, element );
1587            HashMap<String, Object> cssParams = new HashMap<String, Object>( nl.getLength() );
1588    
1589            for ( int i = 0; i < nl.getLength(); i++ ) {
1590                CssParameter cssParam = createCssParameter( nl.item( i ) );
1591                cssParams.put( cssParam.getName(), cssParam );
1592            }
1593    
1594            return new Stroke( cssParams, gs, gf );
1595        }
1596    
1597        /**
1598         * Creates a <tt>GraphicFill</tt>-instance according to the contents of the DOM-subtree starting at the given
1599         * 'GraphicFill'-<tt>Element</tt>.
1600         * <p>
1601         * 
1602         * @param element
1603         *            the 'GraphicFill'-<tt>Element</tt>
1604         * @throws XMLParsingException
1605         *             if a syntactic or semantic error in the DOM-subtree is encountered
1606         * @return the constructed <tt>GraphicFill</tt>-instance
1607         */
1608        private static GraphicFill createGraphicFill( Element element )
1609                                throws XMLParsingException {
1610            // required: <Graphic>
1611            Element graphicElement = XMLTools.getRequiredChildElement( "Graphic", CommonNamespaces.SLDNS, element );
1612            Graphic graphic = createGraphic( graphicElement );
1613    
1614            return new GraphicFill( graphic );
1615        }
1616    
1617        /**
1618         * Creates a <tt>GraphicStroke</tt>-instance according to the contents of the DOM-subtree starting at the given
1619         * 'GraphicStroke'-<tt>Element</tt>.
1620         * <p>
1621         * 
1622         * @param element
1623         *            the 'GraphicStroke'-<tt>Element</tt>
1624         * @throws XMLParsingException
1625         *             if a syntactic or semantic error in the DOM-subtree is encountered
1626         * @return the constructed <tt>GraphicStroke</tt>-instance
1627         */
1628        private static GraphicStroke createGraphicStroke( Element element )
1629                                throws XMLParsingException {
1630            // required: <Graphic>
1631            Element graphicElement = XMLTools.getRequiredChildElement( "Graphic", CommonNamespaces.SLDNS, element );
1632            Graphic graphic = createGraphic( graphicElement );
1633    
1634            return new GraphicStroke( graphic );
1635        }
1636    
1637        /**
1638         * Creates a <tt>Graphic</tt>-instance according to the contents of the DOM-subtree starting at the given
1639         * 'Graphic'-element.
1640         * <p>
1641         * 
1642         * @param element
1643         *            the 'Graphic'-<tt>Element</tt>
1644         * @throws XMLParsingException
1645         *             if a syntactic or semantic error in the DOM-subtree is encountered
1646         * @return the constructed <tt>Graphic</tt>-instance
1647         */
1648        private static Graphic createGraphic( Element element )
1649                                throws XMLParsingException {
1650    
1651            // optional: <Opacity>
1652            ParameterValueType opacity = null;
1653            // optional: <Size>
1654            ParameterValueType size = null;
1655            // optional: <Rotation>
1656            ParameterValueType rotation = null;
1657            // optional: <Displacement>
1658            ParameterValueType[] displacement = null;
1659    
1660            // optional: <ExternalGraphic>s / <Mark>s
1661            NodeList nodelist = element.getChildNodes();
1662            ArrayList<Object> marksAndExtGraphicsList = new ArrayList<Object>();
1663    
1664            for ( int i = 0; i < nodelist.getLength(); i++ ) {
1665                if ( nodelist.item( i ) instanceof Element ) {
1666                    Element child = (Element) nodelist.item( i );
1667                    String namespace = child.getNamespaceURI();
1668    
1669                    if ( !CommonNamespaces.SLDNS.toString().equals( namespace ) ) {
1670                        continue;
1671                    }
1672    
1673                    String childName = child.getLocalName();
1674    
1675                    if ( childName.equals( "ExternalGraphic" ) ) {
1676                        marksAndExtGraphicsList.add( createExternalGraphic( child ) );
1677                    } else if ( childName.equals( "Mark" ) ) {
1678                        marksAndExtGraphicsList.add( createMark( child ) );
1679                    } else if ( childName.equals( "Opacity" ) ) {
1680                        opacity = createParameterValueType( child );
1681                    } else if ( childName.equals( "Size" ) ) {
1682                        size = createParameterValueType( child );
1683                    } else if ( childName.equals( "Rotation" ) ) {
1684                        rotation = createParameterValueType( child );
1685                    } else if ( childName.equals( "Displacement" ) ) {
1686                        displacement = new ParameterValueType[2];
1687                        Element dXElement = XMLTools.getRequiredElement( child, "sld:DisplacementX",
1688                                                                         CommonNamespaces.getNamespaceContext() );
1689                        Element dYElement = XMLTools.getRequiredElement( child, "sld:DisplacementY",
1690                                                                         CommonNamespaces.getNamespaceContext() );
1691                        if ( ( dXElement == null ) || ( dYElement == null ) ) {
1692                            throw new XMLParsingException( "Element 'Displacement' must contain exactly one "
1693                                                           + "'DisplacementX'- and one 'DisplacementY'-element!" );
1694                        }
1695                        displacement[0] = createParameterValueType( dXElement );
1696                        displacement[1] = createParameterValueType( dYElement );
1697                    }
1698                }
1699            }
1700    
1701            Object[] marksAndExtGraphics = marksAndExtGraphicsList.toArray( new Object[marksAndExtGraphicsList.size()] );
1702            return new Graphic( marksAndExtGraphics, opacity, size, rotation, displacement );
1703        }
1704    
1705        /**
1706         * Creates a <tt>CssParameter</tt>-instance according to the contents of the DOM-subtree starting at the given
1707         * 'CssParameter'-<tt>Element</tt>.
1708         * <p>
1709         * 
1710         * @param element
1711         *            the 'CssParamter'-<tt>Element</tt>
1712         * @throws XMLParsingException
1713         *             if a syntactic or semantic error in the DOM-subtree is encountered
1714         * @return the constructed <tt>CssParameter</tt>-instance
1715         */
1716        private static CssParameter createCssParameter( Element element )
1717                                throws XMLParsingException {
1718            // required: name-Attribute
1719            String name = XMLTools.getRequiredAttrValue( "name", null, element );
1720            ParameterValueType pvt = createParameterValueType( element );
1721    
1722            return ( new CssParameter( name, pvt ) );
1723        }
1724    
1725        /**
1726         * <code>TresholdsBelongTo</code> enumerates values possibly belonging to <code>ThreshholdsBelongToType</code>.
1727         * 
1728         * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
1729         * @author last edited by: $Author: apoth $
1730         * 
1731         * @version $Revision: 30390 $, $Date: 2011-04-07 10:21:25 +0200 (Thu, 07 Apr 2011) $
1732         */
1733        public enum ThresholdsBelongTo {
1734            /**
1735             * <code>"succeeding"</code>
1736             */
1737            SUCCEEDING,
1738            /**
1739             * <code>"preceding"</code>
1740             */
1741            PRECEDING
1742        }
1743    
1744        /**
1745         * <code>Mode</code> is the ModeType from the Symbology Encoding Schema.
1746         * 
1747         * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
1748         * @author last edited by: $Author: apoth $
1749         * 
1750         * @version $Revision: 30390 $, $Date: 2011-04-07 10:21:25 +0200 (Thu, 07 Apr 2011) $
1751         */
1752        public enum Mode {
1753            /**
1754             * <code>"linear"</code>
1755             */
1756            LINEAR,
1757            /**
1758             * <code>"cosine"</code>
1759             */
1760            COSINE,
1761            /**
1762             * <code>"cubic"</code>
1763             */
1764            CUBIC
1765        }
1766    
1767        /**
1768         * <code>Method</code> is the MethodType from the Symbology encoding Schema.
1769         * 
1770         * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
1771         * @author last edited by: $Author: apoth $
1772         * 
1773         * @version $Revision: 30390 $, $Date: 2011-04-07 10:21:25 +0200 (Thu, 07 Apr 2011) $
1774         */
1775        public enum Method {
1776            /**
1777             * <code>"numeric"</code>
1778             */
1779            NUMERIC,
1780            /**
1781             * <code>"color"</code>
1782             */
1783            COLOR
1784        }
1785    
1786    }