001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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: 18195 $ $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
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, 1d ) );
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         * <p>
1403         * FIXME: Add support for 'Function'-Elements.
1404         * <p>
1405         *
1406         * @param element
1407         *            the 'Geometry'-<tt>Element</tt>
1408         * @throws XMLParsingException
1409         *             if a syntactic or semantic error in the DOM-subtree is encountered
1410         * @return the constructed <tt>Geometry</tt>-instance
1411         */
1412        private static Geometry createGeometry( Element element )
1413                                throws XMLParsingException {
1414            Geometry geometry = null;
1415    
1416            // required: <PropertyName>
1417            Element propertyNameElement = XMLTools.getRequiredChildElement( "PropertyName", ogcNS, element );
1418    
1419            // optional: <Function>
1420            Element functionElement = XMLTools.getChildElement( "Function", ogcNS, propertyNameElement );
1421    
1422            // just a property name exists
1423            if ( functionElement == null ) {
1424                Node node = XMLTools.getNode( propertyNameElement, "/text()", nsContext );
1425                PropertyPath pp = OGCDocument.parsePropertyPath( (Text) node );
1426                geometry = new Geometry( pp, null );
1427            } else {
1428                // FIXME:
1429                // the property probably contains a wfs:Function expression
1430            }
1431    
1432            return geometry;
1433        }
1434    
1435        /**
1436         * Creates a <tt>Fill</tt>-instance according to the contents of the DOM-subtree starting at the given 'Fill'-
1437         * <tt>Element</tt>.
1438         * <p>
1439         *
1440         * @param element
1441         *            the 'Fill'-<tt>Element</tt>
1442         * @throws XMLParsingException
1443         *             if a syntactic or semantic error in the DOM-subtree is encountered
1444         * @return the constructed <tt>Fill</tt>-instance
1445         */
1446        private static Fill createFill( Element element )
1447                                throws XMLParsingException {
1448            // optional: <GraphicFill>
1449            GraphicFill graphicFill = null;
1450            Element graphicFillElement = XMLTools.getChildElement( "GraphicFill", CommonNamespaces.SLDNS, element );
1451    
1452            if ( graphicFillElement != null ) {
1453                graphicFill = createGraphicFill( graphicFillElement );
1454            }
1455    
1456            // optional: <CssParameter>s
1457            ElementList nl = XMLTools.getChildElements( "CssParameter", CommonNamespaces.SLDNS, element );
1458            HashMap<String, Object> cssParams = new HashMap<String, Object>( nl.getLength() );
1459    
1460            for ( int i = 0; i < nl.getLength(); i++ ) {
1461                CssParameter cssParam = createCssParameter( nl.item( i ) );
1462                cssParams.put( cssParam.getName(), cssParam );
1463            }
1464    
1465            return new Fill( cssParams, graphicFill );
1466        }
1467    
1468        /**
1469         * Creates a <tt>LegendGraphic</tt>-instance according to the contents of the DOM-subtree starting at the given
1470         * 'LegendGraphic'-element.
1471         * <p>
1472         *
1473         * @param element
1474         *            the 'LegendGraphic'-<tt>Element</tt>
1475         * @throws XMLParsingException
1476         *             if a syntactic or semantic error in the DOM-subtree is encountered
1477         * @return the constructed <tt>Graphic</tt>-instance
1478         */
1479        private static LegendGraphic createLegendGraphic( Element element )
1480                                throws XMLParsingException {
1481            // required: <Graphic>
1482            Element graphicElement = XMLTools.getRequiredChildElement( "Graphic", CommonNamespaces.SLDNS, element );
1483            Graphic graphic = createGraphic( graphicElement );
1484    
1485            return new LegendGraphic( graphic );
1486        }
1487    
1488        /**
1489         * Creates an <tt>ExternalGraphic</tt>-instance according to the contents of the DOM-subtree starting at the given
1490         * 'ExternalGraphic'-<tt>Element</tt>.
1491         * <p>
1492         *
1493         * @param element
1494         *            the 'ExternalGraphic'-<tt>Element</tt>
1495         * @throws XMLParsingException
1496         *             if a syntactic or semantic error in the DOM-subtree is encountered
1497         * @return the constructed <tt>ExternalGraphic</tt>-instance
1498         */
1499        private static ExternalGraphic createExternalGraphic( Element element )
1500                                throws XMLParsingException {
1501            // required: <OnlineResource>
1502            Element onlineResourceElement = XMLTools.getRequiredChildElement( "OnlineResource", CommonNamespaces.SLDNS,
1503                                                                              element );
1504    
1505            // required: href-Attribute (in <OnlineResource>)
1506            String href = XMLTools.getRequiredAttrValue( "href", xlnNS, onlineResourceElement );
1507            URL url = null;
1508            try {
1509                url = sldDoc.resolve( href );
1510            } catch ( MalformedURLException e ) {
1511                LOG.logDebug( e.getMessage(), e );
1512                throw new XMLParsingException( "Value ('" + href + "') of attribute 'href' of "
1513                                               + "element 'OnlineResoure' does not denote a valid URL" );
1514            }
1515    
1516            // required: <Format> (in <OnlineResource>)
1517            String format = XMLTools.getRequiredStringValue( "Format", CommonNamespaces.SLDNS, element );
1518    
1519            return new ExternalGraphic( format, url );
1520        }
1521    
1522        /**
1523         * Creates a <tt>Mark</tt>-instance according to the contents of the DOM-subtree starting at the given 'Mark'-
1524         * <tt>Element</tt>.
1525         * <p>
1526         *
1527         * @param element
1528         *            the 'Mark'-<tt>Element</tt>
1529         * @throws XMLParsingException
1530         *             if a syntactic or semantic error in the DOM-subtree is encountered
1531         * @return the constructed <tt>Mark</tt>-instance
1532         */
1533        private static Mark createMark( Element element )
1534                                throws XMLParsingException {
1535            Stroke stroke = null;
1536            Fill fill = null;
1537    
1538            // optional: <WellKnownName>
1539            String wkn = XMLTools.getStringValue( "WellKnownName", CommonNamespaces.SLDNS, element, null );
1540    
1541            // optional: <Stroke>
1542            Element strokeElement = XMLTools.getChildElement( "Stroke", CommonNamespaces.SLDNS, element );
1543    
1544            if ( strokeElement != null ) {
1545                stroke = createStroke( strokeElement );
1546            }
1547    
1548            // optional: <Fill>
1549            Element fillElement = XMLTools.getChildElement( "Fill", CommonNamespaces.SLDNS, element );
1550    
1551            if ( fillElement != null ) {
1552                fill = createFill( fillElement );
1553            }
1554    
1555            return new Mark( wkn, stroke, fill );
1556        }
1557    
1558        /**
1559         * Creates a <tt>Stroke</tt>-instance according to the contents of the DOM-subtree starting at the given 'Stroke'-
1560         * <tt>Element</tt>.
1561         * <p>
1562         *
1563         * @param element
1564         *            the 'Stroke'-<tt>Element</tt>
1565         * @throws XMLParsingException
1566         *             if a syntactic or semantic error in the DOM-subtree is encountered
1567         * @return the constructed <tt>Stroke</tt>-instance
1568         */
1569        private static Stroke createStroke( Element element )
1570                                throws XMLParsingException {
1571            GraphicFill gf = null;
1572            GraphicStroke gs = null;
1573    
1574            // optional: <GraphicFill>
1575            Element gfElement = XMLTools.getChildElement( "GraphicFill", CommonNamespaces.SLDNS, element );
1576    
1577            if ( gfElement != null ) {
1578                gf = createGraphicFill( gfElement );
1579            }
1580    
1581            // optional: <GraphicStroke>
1582            Element gsElement = XMLTools.getChildElement( "GraphicStroke", CommonNamespaces.SLDNS, element );
1583    
1584            if ( gsElement != null ) {
1585                gs = createGraphicStroke( gsElement );
1586            }
1587    
1588            // optional: <CssParameter>s
1589            ElementList nl = XMLTools.getChildElements( "CssParameter", CommonNamespaces.SLDNS, element );
1590            HashMap<String, Object> cssParams = new HashMap<String, Object>( nl.getLength() );
1591    
1592            for ( int i = 0; i < nl.getLength(); i++ ) {
1593                CssParameter cssParam = createCssParameter( nl.item( i ) );
1594                cssParams.put( cssParam.getName(), cssParam );
1595            }
1596    
1597            return new Stroke( cssParams, gs, gf );
1598        }
1599    
1600        /**
1601         * Creates a <tt>GraphicFill</tt>-instance according to the contents of the DOM-subtree starting at the given
1602         * 'GraphicFill'-<tt>Element</tt>.
1603         * <p>
1604         *
1605         * @param element
1606         *            the 'GraphicFill'-<tt>Element</tt>
1607         * @throws XMLParsingException
1608         *             if a syntactic or semantic error in the DOM-subtree is encountered
1609         * @return the constructed <tt>GraphicFill</tt>-instance
1610         */
1611        private static GraphicFill createGraphicFill( Element element )
1612                                throws XMLParsingException {
1613            // required: <Graphic>
1614            Element graphicElement = XMLTools.getRequiredChildElement( "Graphic", CommonNamespaces.SLDNS, element );
1615            Graphic graphic = createGraphic( graphicElement );
1616    
1617            return new GraphicFill( graphic );
1618        }
1619    
1620        /**
1621         * Creates a <tt>GraphicStroke</tt>-instance according to the contents of the DOM-subtree starting at the given
1622         * 'GraphicStroke'-<tt>Element</tt>.
1623         * <p>
1624         *
1625         * @param element
1626         *            the 'GraphicStroke'-<tt>Element</tt>
1627         * @throws XMLParsingException
1628         *             if a syntactic or semantic error in the DOM-subtree is encountered
1629         * @return the constructed <tt>GraphicStroke</tt>-instance
1630         */
1631        private static GraphicStroke createGraphicStroke( Element element )
1632                                throws XMLParsingException {
1633            // required: <Graphic>
1634            Element graphicElement = XMLTools.getRequiredChildElement( "Graphic", CommonNamespaces.SLDNS, element );
1635            Graphic graphic = createGraphic( graphicElement );
1636    
1637            return new GraphicStroke( graphic );
1638        }
1639    
1640        /**
1641         * Creates a <tt>Graphic</tt>-instance according to the contents of the DOM-subtree starting at the given
1642         * 'Graphic'-element.
1643         * <p>
1644         *
1645         * @param element
1646         *            the 'Graphic'-<tt>Element</tt>
1647         * @throws XMLParsingException
1648         *             if a syntactic or semantic error in the DOM-subtree is encountered
1649         * @return the constructed <tt>Graphic</tt>-instance
1650         */
1651        private static Graphic createGraphic( Element element )
1652                                throws XMLParsingException {
1653    
1654            // optional: <Opacity>
1655            ParameterValueType opacity = null;
1656            // optional: <Size>
1657            ParameterValueType size = null;
1658            // optional: <Rotation>
1659            ParameterValueType rotation = null;
1660    
1661            // optional: <ExternalGraphic>s / <Mark>s
1662            NodeList nodelist = element.getChildNodes();
1663            ArrayList<Object> marksAndExtGraphicsList = new ArrayList<Object>();
1664    
1665            for ( int i = 0; i < nodelist.getLength(); i++ ) {
1666                if ( nodelist.item( i ) instanceof Element ) {
1667                    Element child = (Element) nodelist.item( i );
1668                    String namespace = child.getNamespaceURI();
1669    
1670                    if ( !CommonNamespaces.SLDNS.toString().equals( namespace ) ) {
1671                        continue;
1672                    }
1673    
1674                    String childName = child.getLocalName();
1675    
1676                    if ( childName.equals( "ExternalGraphic" ) ) {
1677                        marksAndExtGraphicsList.add( createExternalGraphic( child ) );
1678                    } else if ( childName.equals( "Mark" ) ) {
1679                        marksAndExtGraphicsList.add( createMark( child ) );
1680                    } else if ( childName.equals( "Opacity" ) ) {
1681                        opacity = createParameterValueType( child );
1682                    } else if ( childName.equals( "Size" ) ) {
1683                        size = createParameterValueType( child );
1684                    } else if ( childName.equals( "Rotation" ) ) {
1685                        rotation = createParameterValueType( child );
1686                    }
1687                }
1688            }
1689    
1690            Object[] marksAndExtGraphics = marksAndExtGraphicsList.toArray( new Object[marksAndExtGraphicsList.size()] );
1691    
1692            return new Graphic( marksAndExtGraphics, opacity, size, rotation );
1693        }
1694    
1695        /**
1696         * Creates a <tt>CssParameter</tt>-instance according to the contents of the DOM-subtree starting at the given
1697         * 'CssParameter'-<tt>Element</tt>.
1698         * <p>
1699         *
1700         * @param element
1701         *            the 'CssParamter'-<tt>Element</tt>
1702         * @throws XMLParsingException
1703         *             if a syntactic or semantic error in the DOM-subtree is encountered
1704         * @return the constructed <tt>CssParameter</tt>-instance
1705         */
1706        private static CssParameter createCssParameter( Element element )
1707                                throws XMLParsingException {
1708            // required: name-Attribute
1709            String name = XMLTools.getRequiredAttrValue( "name", null, element );
1710            ParameterValueType pvt = createParameterValueType( element );
1711    
1712            return ( new CssParameter( name, pvt ) );
1713        }
1714    
1715        /**
1716         * <code>TresholdsBelongTo</code> enumerates values possibly belonging to <code>ThreshholdsBelongToType</code>.
1717         *
1718         * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
1719         * @author last edited by: $Author: mschneider $
1720         *
1721         * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
1722         */
1723        public enum ThresholdsBelongTo {
1724            /**
1725             * <code>"succeeding"</code>
1726             */
1727            SUCCEEDING,
1728            /**
1729             * <code>"preceding"</code>
1730             */
1731            PRECEDING
1732        }
1733    
1734        /**
1735         * <code>Mode</code> is the ModeType from the Symbology Encoding Schema.
1736         *
1737         * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
1738         * @author last edited by: $Author: mschneider $
1739         *
1740         * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
1741         */
1742        public enum Mode {
1743            /**
1744             * <code>"linear"</code>
1745             */
1746            LINEAR,
1747            /**
1748             * <code>"cosine"</code>
1749             */
1750            COSINE,
1751            /**
1752             * <code>"cubic"</code>
1753             */
1754            CUBIC
1755        }
1756    
1757        /**
1758         * <code>Method</code> is the MethodType from the Symbology encoding Schema.
1759         *
1760         * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
1761         * @author last edited by: $Author: mschneider $
1762         *
1763         * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
1764         */
1765        public enum Method {
1766            /**
1767             * <code>"numeric"</code>
1768             */
1769            NUMERIC,
1770            /**
1771             * <code>"color"</code>
1772             */
1773            COLOR
1774        }
1775    
1776    }