    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/graphics/sld/Stroke.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.awt.Color.decode;
    039    import static org.deegree.framework.log.LoggerFactory.getLogger;
    040
    041    import java.awt.Color;
    042    import java.util.HashMap;
    043    import java.util.StringTokenizer;
    044
    045    import org.deegree.framework.log.ILogger;
    046    import org.deegree.framework.util.ColorUtils;
    047    import org.deegree.framework.xml.Marshallable;
    048    import org.deegree.model.feature.Feature;
    049    import org.deegree.model.filterencoding.Expression;
    050    import org.deegree.model.filterencoding.FilterEvaluationException;
    051
    052    /**
    053     * A Stroke allows a string of line segments (or any linear geometry) to be rendered. There are three basic types of
    054     * strokes: solid Color, GraphicFill (stipple), and repeated GraphicStroke. A repeated graphic is plotted linearly and
    055     * has its graphic symbol bended around the curves of the line string. The default is a solid black line (Color
    056     * "#000000").
    057     * <p>
    058     * The supported CSS-Parameter names are:
    059     * <ul>
    060     * <li>stroke (color)
    061     * <li>stroke-opacity
    062     * <li>stroke-width
    063     * <li>stroke-linejoin
    064     * <li>stroke-linecap
    065     * <li>stroke-dasharray
    066     * <li>stroke-dashoffset
    067     * <p>
    068     *
    069     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
    070     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a>
    071     * @version $Revision: 24727 $ $Date: 2010-06-07 17:58:11 +0200 (Mo, 07. Jun 2010) $
    072     */
    073
    074    public class Stroke extends Drawing implements Marshallable {
    075
    076        private static final ILogger LOG = getLogger( Stroke.class );
    077
    078        /**
    079         * Wraps the java.awt.BasicStroke.JOIN_MITER constant (why)
    080         */
    081        public static final int LJ_MITRE = java.awt.BasicStroke.JOIN_MITER;
    082
    083        /**
    084         * Wraps the java.awt.BasicStroke.JOIN_ROUND constant (why)
    085         */
    086        public static final int LJ_ROUND = java.awt.BasicStroke.JOIN_ROUND;
    087
    088        /**
    089         * Wraps the java.awt.BasicStroke.JOIN_BEVEL constant (why)
    090         */
    091        public static final int LJ_BEVEL = java.awt.BasicStroke.JOIN_BEVEL;
    092
    093        /**
    094         * Wraps the java.awt.BasicStroke.CAP_BUTT constant (why)
    095         */
    096        public static final int LC_BUTT = java.awt.BasicStroke.CAP_BUTT;
    097
    098        /**
    099         * Wraps the java.awt.BasicStroke.CAP_ROUND constant (why)
    100         */
    101        public static final int LC_ROUND = java.awt.BasicStroke.CAP_ROUND;
    102
    103        /**
    104         * Wraps the java.awt.BasicStroke.CAP_SQUARE constant (why)
    105         */
    106        public static final int LC_SQUARE = java.awt.BasicStroke.CAP_SQUARE;
    107
    108        // default values
    109        /**
    110         * Default color is black.
    111         */
    112        public static final Color COLOR_DEFAULT = Color.BLACK;
    113
    114        /**
    115         * Default opacity is 1
    116         */
    117        public static final double OPACITY_DEFAULT = 1.0;
    118
    119        /**
    120         * Default with is 1
    121         */
    122        public static final double WIDTH_DEFAULT = 1.0;
    123
    124        /**
    125         * Default Line join is mitre
    126         */
    127        public static final int LJ_DEFAULT = LJ_MITRE;
    128
    129        /**
    130         * default line end is butt
    131         */
    132        public static final int LC_DEFAULT = LC_BUTT;
    133
    134        private GraphicStroke graphicStroke = null;
    135
    136        private Color color = null;
    137
    138        private double smplOpacity = -1;
    139
    140        private double smplWidth = -1;
    141
    142        private int smplLineJoin = -1;
    143
    144        private int smplLineCap = -1;
    145
    146        private float[] smplDashArray = null;
    147
    148        private float smplDashOffset = -1;
    149
    150        /**
    151         * Constructs a new <tt>Stroke<tt>.
    152         */
    153        protected Stroke() {
    154            super( new HashMap<String, Object>(), null );
    155        }
    156
    157        /**
    158         * Constructs a new <tt>Stroke<tt>. <p>
    159         *
    160         * @param cssParams
    161         *            keys are <tt>Strings<tt> (see above), values are <tt>CssParameters</tt>
    162         * @param graphicStroke
    163         * @param graphicFill
    164         */
    165        public Stroke( HashMap<String, Object> cssParams, GraphicStroke graphicStroke, GraphicFill graphicFill ) {
    166            super( cssParams, graphicFill );
    167            this.graphicStroke = graphicStroke;
    168            try {
    169                extractSimpleColor();
    170            } catch ( Exception e ) {
    171                LOG.logWarning( "The color could not be parsed as string." );
    172                LOG.logDebug( "Stack trace:", e );
    173            }
    174            try {
    175                extractSimpleOpacity();
    176            } catch ( Exception e ) {
    177                LOG.logWarning( "The opacity could not be parsed as string." );
    178                LOG.logDebug( "Stack trace:", e );
    179            }
    180            try {
    181                extractSimpleWidth();
    182            } catch ( Exception e ) {
    183                LOG.logWarning( "The width could not be parsed as string." );
    184                LOG.logDebug( "Stack trace:", e );
    185            }
    186            try {
    187                extractSimpleLineJoin();
    188            } catch ( Exception e ) {
    189                LOG.logWarning( "The linejoin could not be parsed as string." );
    190                LOG.logDebug( "Stack trace:", e );
    191            }
    192            try {
    193                extractSimpleLineCap();
    194            } catch ( Exception e ) {
    195                LOG.logWarning( "The linecap could not be parsed as string." );
    196                LOG.logDebug( "Stack trace:", e );
    197            }
    198            try {
    199                extractSimpleDasharray();
    200            } catch ( Exception e ) {
    201                LOG.logWarning( "The dasharray could not be parsed as string." );
    202                LOG.logDebug( "Stack trace:", e );
    203            }
    204            try {
    205                extractSimpleDashOffset();
    206            } catch ( Exception e ) {
    207                LOG.logWarning( "The dashoffset could not be parsed as string." );
    208                LOG.logDebug( "Stack trace:", e );
    209            }
    210        }
    211
    212        /**
    213         * extracts the color of the stroke if it is simple (nor Expression) to avoid new calculation for each call of
    214         * getStroke(Feature feature)
    215         */
    216        private void extractSimpleColor()
    217                                throws FilterEvaluationException {
    218            CssParameter cssParam = (CssParameter) cssParams.get( "stroke" );
    219            if ( cssParam != null ) {
    220                Object[] o = cssParam.getValue().getComponents();
    221                for ( int i = 0; i < o.length; i++ ) {
    222                    if ( o[i] instanceof Expression ) {
    223                        color = null;
    224                        break;
    225                    }
    226                    try {
    227                        // trimming the String to avoid parsing errors with newlines
    228                        String s = o[i].toString().trim();
    229                        if ( s.equals( "" ) ) {
    230                            color = decode( s );
    231                        }
    232                    } catch ( NumberFormatException e ) {
    233                        throw new FilterEvaluationException( "Given value ('" + o[i] + "') for CSS-Parameter 'stroke' "
    234                                                             + "does not denote a valid color!" );
    235                    }
    236                }
    237            }
    238        }
    239
    240        /**
    241         * returns true if the passed CssParameter contain a simple value
    242         */
    243        private boolean isSimple( CssParameter cssParam ) {
    244            boolean simple = true;
    245            Object[] o = cssParam.getValue().getComponents();
    246            for ( int i = 0; i < o.length; i++ ) {
    247                if ( o[i] instanceof Expression ) {
    248                    simple = false;
    249                    break;
    250                }
    251            }
    252            return simple;
    253        }
    254
    255        /**
    256         * extracts the opacity of the stroke if it is simple (no Expression) to avoid new calculation for each call of
    257         * getStroke(Feature feature)
    258         */
    259        private void extractSimpleOpacity()
    260                                throws FilterEvaluationException {
    261            CssParameter cssParam = (CssParameter) cssParams.get( "stroke-opacity" );
    262            if ( cssParam != null ) {
    263                if ( isSimple( cssParam ) ) {
    264                    Object[] o = cssParam.getValue().getComponents();
    265                    try {
    266                        smplOpacity = Double.parseDouble( (String) o[0] );
    267                    } catch ( NumberFormatException e ) {
    268                        throw new FilterEvaluationException( "Given value for parameter 'stroke-opacity' ('" + o[0]
    269                                                             + "') has invalid format!" );
    270                    }
    271
    272                    if ( ( smplOpacity < 0.0 ) || ( smplOpacity > 1.0 ) ) {
    273                        throw new FilterEvaluationException( "Value for parameter 'stroke-opacity' (given: '" + o[0]
    274                                                             + "') must be between 0.0 and 1.0!" );
    275                    }
    276                }
    277            }
    278        }
    279
    280        /**
    281         * extracts the width of the stroke if it is simple (no Expression) to avoid new calculation for each call of
    282         * getStroke(Feature feature)
    283         */
    284        private void extractSimpleWidth()
    285                                throws FilterEvaluationException {
    286            CssParameter cssParam = (CssParameter) cssParams.get( "stroke-width" );
    287            if ( cssParam != null ) {
    288                if ( isSimple( cssParam ) ) {
    289                    Object[] o = cssParam.getValue().getComponents();
    290                    try {
    291                        smplWidth = Double.parseDouble( (String) o[0] );
    292                    } catch ( NumberFormatException e ) {
    293                        throw new FilterEvaluationException( "Given value for parameter 'stroke-width' ('" + o[0]
    294                                                             + "') has invalid format!" );
    295                    }
    296                    if ( smplWidth < 0.0 ) {
    297                        throw new FilterEvaluationException( "Value for parameter 'stroke-width' (given: '" + smplWidth
    298                                                             + "') must be > 0.0!" );
    299                    }
    300                }
    301            }
    302        }
    303
    304        /**
    305         * extracts the line join of the stroke if it is simple (no Expression) to avoid new calculation for each call of
    306         * getStroke(Feature feature)
    307         */
    308        private void extractSimpleLineJoin()
    309                                throws FilterEvaluationException {
    310            CssParameter cssParam = (CssParameter) cssParams.get( "stroke-linejoin" );
    311            if ( cssParam != null ) {
    312                if ( isSimple( cssParam ) ) {
    313                    Object[] o = cssParam.getValue().getComponents();
    314                    String value = (String) o[0];
    315                    if ( value.equals( "mitre" ) ) {
    316                        smplLineJoin = Stroke.LJ_MITRE;
    317                    } else if ( value.equals( "round" ) ) {
    318                        smplLineJoin = Stroke.LJ_ROUND;
    319                    } else if ( value.equals( "bevel" ) ) {
    320                        smplLineJoin = Stroke.LJ_BEVEL;
    321                    } else {
    322                        throw new FilterEvaluationException( "Given value for parameter 'stroke-linejoin' ('" + value
    323                                                             + "') is unsupported. Supported values are: "
    324                                                             + "'mitre', 'round' or 'bevel'!" );
    325                    }
    326                }
    327            }
    328        }
    329
    330        /**
    331         * extracts the line cap of the stroke if it is simple (no Expression) to avoid new calculation for each call of
    332         * getStroke(Feature feature)
    333         */
    334        private void extractSimpleLineCap()
    335                                throws FilterEvaluationException {
    336            CssParameter cssParam = (CssParameter) cssParams.get( "stroke-linecap" );
    337            if ( cssParam != null ) {
    338                if ( isSimple( cssParam ) ) {
    339                    Object[] o = cssParam.getValue().getComponents();
    340                    String value = (String) o[0];
    341                    if ( value.equals( "butt" ) ) {
    342                        smplLineCap = Stroke.LC_BUTT;
    343                    } else if ( value.equals( "round" ) ) {
    344                        smplLineCap = Stroke.LC_ROUND;
    345                    } else if ( value.equals( "square" ) ) {
    346                        smplLineCap = Stroke.LC_SQUARE;
    347                    } else {
    348                        throw new FilterEvaluationException( "Given value for parameter 'stroke-linecap' ('" + value
    349                                                             + "') is unsupported. Supported values are: "
    350                                                             + "'butt', 'round' or 'square'!" );
    351                    }
    352                }
    353            }
    354        }
    355
    356        /**
    357         * extracts the dasharray of the stroke if it is simple (no Expression) to avoid new calculation for each call of
    358         * getStroke(Feature feature)
    359         */
    360        private void extractSimpleDasharray()
    361                                throws FilterEvaluationException {
    362            CssParameter cssParam = (CssParameter) cssParams.get( "stroke-dasharray" );
    363            if ( cssParam != null ) {
    364                if ( isSimple( cssParam ) ) {
    365                    Object[] o = cssParam.getValue().getComponents();
    366                    String value = (String) o[0];
    367                    StringTokenizer st = new StringTokenizer( value, ",; " );
    368                    int count = st.countTokens();
    369                    float[] dashArray;
    370
    371                    if ( ( count % 2 ) == 0 ) {
    372                        dashArray = new float[count];
    373                    } else {
    374                        dashArray = new float[count * 2];
    375                    }
    376
    377                    int k = 0;
    378                    while ( st.hasMoreTokens() ) {
    379                        String s = st.nextToken();
    380                        try {
    381                            dashArray[k++] = Float.parseFloat( s );
    382                        } catch ( NumberFormatException e ) {
    383                            throw new FilterEvaluationException( "List of values for parameter 'stroke-dashoffset' "
    384                                                                 + "contains an invalid token: '" + s + "'!" );
    385                        }
    386                    }
    387
    388                    // odd number of values -> the pattern must be repeated twice
    389                    if ( ( count % 2 ) == 1 ) {
    390                        int j = 0;
    391                        while ( k < ( ( count * 2 ) - 1 ) ) {
    392                            dashArray[k++] = dashArray[j++];
    393                        }
    394                    }
    395                    smplDashArray = dashArray;
    396                }
    397            }
    398        }
    399
    400        /**
    401         * extracts the dash offset of the stroke if it is simple (no Expression) to avoid new calculation for each call of
    402         * getStroke(Feature feature)
    403         */
    404        private void extractSimpleDashOffset()
    405                                throws FilterEvaluationException {
    406            CssParameter cssParam = (CssParameter) cssParams.get( "stroke-dashoffset" );
    407            if ( cssParam != null ) {
    408                if ( isSimple( cssParam ) ) {
    409                    Object[] o = cssParam.getValue().getComponents();
    410                    String value = (String) o[0];
    411                    try {
    412                        smplDashOffset = Float.parseFloat( value );
    413                    } catch ( NumberFormatException e ) {
    414                        throw new FilterEvaluationException( "Given value for parameter 'stroke-dashoffset' ('" + value
    415                                                             + "') has invalid format!" );
    416                    }
    417                }
    418            }
    419        }
    420
    421        /**
    422         * The GraphicStroke element both indicates that a repeated-linear-graphic stroke type will be used.
    423         * <p>
    424         *
    425         * @return the underlying <tt>GraphicStroke</tt> instance (may be null)
    426         *
    427         */
    428        public GraphicStroke getGraphicStroke() {
    429            return graphicStroke;
    430        }
    431
    432        /**
    433         * The GraphicStroke element both indicates that a repeated-linear-graphic stroke type will be used.
    434         *
    435         * @param graphicStroke
    436         *            the graphicStroke element
    437         *            <p>
    438         *
    439         */
    440        public void setGraphicStroke( GraphicStroke graphicStroke ) {
    441            this.graphicStroke = graphicStroke;
    442        }
    443
    444        /**
    445         * The stroke CssParameter element gives the solid color that will be used for a stroke. The color value is
    446         * RGB-encoded using two hexadecimal digits per primary-color component, in the order Red, Green, Blue, prefixed
    447         * with a hash (#) sign. The hexadecimal digits between A and F may be in either uppercase or lowercase. For
    448         * example, full red is encoded as #ff0000 (with no quotation marks). The default color is defined to be black
    449         * (#000000) in the context of the LineSymbolizer, if the stroke CssParameter element is absent.
    450         * <p>
    451         *
    452         * @param feature
    453         *            specifies the <tt>Feature</tt> to be used for evaluation of the underlying 'sld:ParameterValueType'
    454         * @return the (evaluated) value of the parameter
    455         * @throws FilterEvaluationException
    456         *             if the evaluation fails
    457         */
    458        public Color getStroke( Feature feature )
    459                                throws FilterEvaluationException {
    460            Color awtColor = COLOR_DEFAULT;
    461
    462            if ( color == null ) {
    463                // evaluate color depending on the passed feature's properties
    464                CssParameter cssParam = (CssParameter) cssParams.get( "stroke" );
    465
    466                if ( cssParam != null ) {
    467                    String s = cssParam.getValue( feature );
    468
    469                    try {
    470                        awtColor = Color.decode( s );
    471                    } catch ( NumberFormatException e ) {
    472                        throw new FilterEvaluationException( "Given value ('" + s + "') for CSS-Parameter 'stroke' "
    473                                                             + "does not denote a valid color!" );
    474                    }
    475                }
    476            } else {
    477                awtColor = color;
    478            }
    479
    480            return awtColor;
    481        }
    482
    483        /**
    484         * @see org.deegree.graphics.sld.Stroke#getStroke(Feature) <p>
    485         * @param stroke
    486         *            the stroke to be set
    487         */
    488        public void setStroke( Color stroke ) {
    489            this.color = stroke;
    490            CssParameter strokeColor = StyleFactory.createCssParameter( "stroke", ColorUtils.toHexCode( "#", stroke ) );
    491            cssParams.put( "stroke", strokeColor );
    492        }
    493
    494        /**
    495         * The stroke-opacity CssParameter element specifies the level of translucency to use when rendering the stroke. The
    496         * value is encoded as a floating-point value (float) between 0.0 and 1.0 with 0.0 representing completely
    497         * transparent and 1.0 representing completely opaque, with a linear scale of translucency for intermediate values.
    498         * For example, 0.65 would represent 65% opacity. The default value is 1.0 (opaque).
    499         * <p>
    500         *
    501         * @param feature
    502         *            specifies the <tt>Feature</tt> to be used for evaluation of the underlying 'sld:ParameterValueType'
    503         * @return the (evaluated) value of the parameter
    504         * @throws FilterEvaluationException
    505         *             if the evaluation fails
    506         */
    507        public double getOpacity( Feature feature )
    508                                throws FilterEvaluationException {
    509            double opacity = OPACITY_DEFAULT;
    510
    511            if ( smplOpacity < 0 ) {
    512                CssParameter cssParam = (CssParameter) cssParams.get( "stroke-opacity" );
    513
    514                if ( cssParam != null ) {
    515                    // evaluate opacity depending on the passed feature's properties
    516                    String value = cssParam.getValue( feature );
    517
    518                    try {
    519                        opacity = Double.parseDouble( value );
    520                    } catch ( NumberFormatException e ) {
    521                        throw new FilterEvaluationException( "Given value for parameter 'stroke-opacity' ('" + value
    522                                                             + "') has invalid format!" );
    523                    }
    524
    525                    if ( ( opacity < 0.0 ) || ( opacity > 1.0 ) ) {
    526                        throw new FilterEvaluationException( "Value for parameter 'stroke-opacity' (given: '" + value
    527                                                             + "') must be between 0.0 and 1.0!" );
    528                    }
    529                }
    530            } else {
    531                opacity = smplOpacity;
    532            }
    533
    534            return opacity;
    535        }
    536
    537        /**
    538         * @see org.deegree.graphics.sld.Stroke#getOpacity(Feature) <p>
    539         * @param opacity
    540         *            the opacity to be set for the stroke
    541         */
    542        public void setOpacity( double opacity ) {
    543            if ( opacity > 1 ) {
    544                opacity = 1;
    545            } else if ( opacity < 0 ) {
    546                opacity = 0;
    547            }
    548            this.smplOpacity = opacity;
    549            CssParameter strokeOp = StyleFactory.createCssParameter( "stroke-opacity", "" + opacity );
    550            cssParams.put( "stroke-opacity", strokeOp );
    551        }
    552
    553        /**
    554         * The stroke-width CssParameter element gives the absolute width (thickness) of a stroke in pixels encoded as a
    555         * float. (Arguably, more units could be provided for encoding sizes, such as millimeters or typesetter's points.)
    556         * The default is 1.0. Fractional numbers are allowed (with a system-dependent interpretation) but negative numbers
    557         * are not.
    558         * <p>
    559         *
    560         * @param feature
    561         *            specifies the <tt>Feature</tt> to be used for evaluation of the underlying 'sld:ParameterValueType'
    562         * @return the (evaluated) value of the parameter
    563         * @throws FilterEvaluationException
    564         *             if the evaluation fails
    565         */
    566        public double getWidth( Feature feature )
    567                                throws FilterEvaluationException {
    568            double width = WIDTH_DEFAULT;
    569
    570            if ( smplWidth < 0 ) {
    571                // evaluate smplWidth depending on the passed feature's properties
    572                CssParameter cssParam = (CssParameter) cssParams.get( "stroke-width" );
    573
    574                if ( cssParam != null && feature != null ) {
    575                    String value = cssParam.getValue( feature );
    576
    577                    try {
    578                        width = Double.parseDouble( value );
    579                    } catch ( NumberFormatException e ) {
    580                        throw new FilterEvaluationException( "Given value for parameter 'stroke-width' ('" + value
    581                                                             + "') has invalid format!" );
    582                    }
    583
    584                    if ( width <= 0.0 ) {
    585                        throw new FilterEvaluationException( "Value for parameter 'stroke-width' (given: '" + value
    586                                                             + "') must be greater than 0!" );
    587                    }
    588                }
    589            } else {
    590                width = smplWidth;
    591            }
    592
    593            return width;
    594        }
    595
    596        /**
    597         * @see org.deegree.graphics.sld.Stroke#getWidth(Feature) <p>
    598         * @param width
    599         *            the width to be set for the stroke
    600         */
    601        public void setWidth( double width ) {
    602            if ( width <= 0 )
    603                width = 1;
    604            this.smplWidth = width;
    605            CssParameter strokeWi = StyleFactory.createCssParameter( "stroke-width", "" + width );
    606            cssParams.put( "stroke-width", strokeWi );
    607        }
    608
    609        /**
    610         * The stroke-linejoin CssParameter element encode enumerated values telling how line strings should be joined
    611         * (between line segments). The values are represented as content strings. The allowed values for line join are
    612         * mitre, round, and bevel.
    613         * <p>
    614         *
    615         * @param feature
    616         *            specifies the <tt>Feature</tt> to be used for evaluation of the underlying 'sld:ParameterValueType'
    617         * @return the (evaluated) value of the parameter
    618         * @throws FilterEvaluationException
    619         *             if the evaluation fails
    620         */
    621        public int getLineJoin( Feature feature )
    622                                throws FilterEvaluationException {
    623            int lineJoin = LJ_DEFAULT;
    624
    625            if ( smplLineJoin < 0 ) {
    626                CssParameter cssParam = (CssParameter) cssParams.get( "stroke-linejoin" );
    627
    628                if ( cssParam != null ) {
    629                    String value = cssParam.getValue( feature );
    630
    631                    if ( value.equals( "mitre" ) ) {
    632                        lineJoin = Stroke.LJ_MITRE;
    633                    } else if ( value.equals( "round" ) ) {
    634                        lineJoin = Stroke.LJ_ROUND;
    635                    } else if ( value.equals( "bevel" ) ) {
    636                        lineJoin = Stroke.LJ_BEVEL;
    637                    } else {
    638                        throw new FilterEvaluationException( "Given value for parameter 'stroke-linejoin' ('" + value
    639                                                             + "') is unsupported. Supported values are: "
    640                                                             + "'mitre', 'round' or 'bevel'!" );
    641                    }
    642                }
    643            } else {
    644                lineJoin = smplLineJoin;
    645            }
    646
    647            return lineJoin;
    648        }
    649
    650        /**
    651         * @see org.deegree.graphics.sld.Stroke#getLineJoin(Feature) <p>
    652         * @param lineJoin
    653         *            the lineJoin to be set for the stroke
    654         */
    655        public void setLineJoin( int lineJoin ) {
    656            String join = null;
    657            if ( lineJoin == Stroke.LJ_MITRE ) {
    658                join = "mitre";
    659            } else if ( lineJoin == Stroke.LJ_ROUND ) {
    660                join = "round";
    661            } else if ( lineJoin == Stroke.LJ_BEVEL ) {
    662                join = "bevel";
    663            } else {
    664                // default
    665                lineJoin = Stroke.LJ_BEVEL;
    666                join = "bevel";
    667            }
    668            smplLineJoin = lineJoin;
    669            CssParameter strokeLJ = StyleFactory.createCssParameter( "stroke-linejoin", join );
    670            cssParams.put( "stroke-linejoin", strokeLJ );
    671        }
    672
    673        /**
    674         * Thestroke-linecap CssParameter element encode enumerated values telling how line strings should be capped (at the
    675         * two ends of the line string). The values are represented as content strings. The allowed values for line cap are
    676         * butt, round, and square. The default values are system-dependent.
    677         * <p>
    678         *
    679         * @param feature
    680         *            specifies the <tt>Feature</tt> to be used for evaluation of the underlying 'sld:ParameterValueType'
    681         * @return the (evaluated) value of the parameter
    682         * @throws FilterEvaluationException
    683         *             if the evaluation fails
    684         */
    685        public int getLineCap( Feature feature )
    686                                throws FilterEvaluationException {
    687            int lineCap = LC_DEFAULT;
    688
    689            if ( smplLineCap < 0 ) {
    690
    691                CssParameter cssParam = (CssParameter) cssParams.get( "stroke-linecap" );
    692
    693                if ( cssParam != null ) {
    694                    String value = cssParam.getValue( feature );
    695
    696                    if ( value.equals( "butt" ) ) {
    697                        lineCap = Stroke.LC_BUTT;
    698                    } else if ( value.equals( "round" ) ) {
    699                        lineCap = Stroke.LC_ROUND;
    700                    } else if ( value.equals( "square" ) ) {
    701                        lineCap = Stroke.LC_SQUARE;
    702                    } else {
    703                        throw new FilterEvaluationException( "Given value for parameter 'stroke-linecap' ('" + value
    704                                                             + "') is unsupported. Supported values are: "
    705                                                             + "'butt', 'round' or 'square'!" );
    706                    }
    707                }
    708            } else {
    709                lineCap = smplLineCap;
    710            }
    711
    712            return lineCap;
    713        }
    714
    715        /**
    716         * @see org.deegree.graphics.sld.Stroke#getLineCap(Feature) <p>
    717         * @param lineCap
    718         *            lineCap to be set for the stroke
    719         */
    720        public void setLineCap( int lineCap ) {
    721            String cap = null;
    722            if ( lineCap == Stroke.LC_BUTT ) {
    723                cap = "butt";
    724            } else if ( lineCap == Stroke.LC_ROUND ) {
    725                cap = "round";
    726            } else if ( lineCap == Stroke.LC_SQUARE ) {
    727                cap = "square";
    728            } else {
    729                // default;
    730                cap = "round";
    731                lineCap = Stroke.LC_SQUARE;
    732            }
    733            smplLineCap = lineCap;
    734            CssParameter strokeCap = StyleFactory.createCssParameter( "stroke-linecap", cap );
    735            cssParams.put( "stroke-linecap", strokeCap );
    736        }
    737
    738        /**
    739         * Evaluates the 'stroke-dasharray' parameter as defined in OGC 02-070. The stroke-dasharray CssParameter element
    740         * encodes a dash pattern as a series of space separated floats. The first number gives the length in pixels of dash
    741         * to draw, the second gives the amount of space to leave, and this pattern repeats. If an odd number of values is
    742         * given, then the pattern is expanded by repeating it twice to give an even number of values. Decimal values have a
    743         * system-dependent interpretation (usually depending on whether antialiasing is being used). The default is to draw
    744         * an unbroken line.
    745         * <p>
    746         *
    747         * @param feature
    748         *            the encoded pattern
    749         * @throws FilterEvaluationException
    750         *             if the eevaluation fails or the encoded pattern is erroneous
    751         * @return the decoded pattern as an array of float-values (null if the parameter was not specified)
    752         */
    753        public float[] getDashArray( Feature feature )
    754                                throws FilterEvaluationException {
    755            CssParameter cssParam = (CssParameter) cssParams.get( "stroke-dasharray" );
    756
    757            float[] dashArray = null;
    758            if ( smplDashArray == null ) {
    759                if ( cssParam == null ) {
    760                    return null;
    761                }
    762
    763                String value = cssParam.getValue( feature );
    764
    765                StringTokenizer st = new StringTokenizer( value, ",; " );
    766                int count = st.countTokens();
    767
    768                if ( ( count % 2 ) == 0 ) {
    769                    dashArray = new float[count];
    770                } else {
    771                    dashArray = new float[count * 2];
    772                }
    773
    774                int i = 0;
    775                while ( st.hasMoreTokens() ) {
    776                    String s = st.nextToken();
    777                    try {
    778                        dashArray[i++] = Float.parseFloat( s );
    779                    } catch ( NumberFormatException e ) {
    780                        throw new FilterEvaluationException( "List of values for parameter 'stroke-dashoffset' "
    781                                                             + "contains an invalid token: '" + s + "'!" );
    782                    }
    783                }
    784
    785                // odd number of values -> the pattern must be repeated twice
    786                if ( ( count % 2 ) == 1 ) {
    787                    int j = 0;
    788                    while ( i < ( ( count * 2 ) - 1 ) ) {
    789                        dashArray[i++] = dashArray[j++];
    790                    }
    791                }
    792            } else {
    793                dashArray = smplDashArray;
    794            }
    795
    796            return dashArray;
    797        }
    798
    799        /**
    800         * @see org.deegree.graphics.sld.Stroke#getDashArray(Feature) <p>
    801         * @param dashArray
    802         *            the dashArray to be set for the Stroke
    803         */
    804        public void setDashArray( float[] dashArray ) {
    805            if ( dashArray != null ) {
    806                String s = "";
    807                for ( int i = 0; i < dashArray.length - 1; i++ ) {
    808                    s = s + dashArray[i] + ",";
    809                }
    810                s = s + dashArray[dashArray.length - 1];
    811                smplDashArray = dashArray;
    812                CssParameter strokeDash = StyleFactory.createCssParameter( "stroke-dasharray", s );
    813                cssParams.put( "stroke-dasharray", strokeDash );
    814            }
    815        }
    816
    817        /**
    818         * The stroke-dashoffset CssParameter element specifies the distance as a float into the stroke-dasharray pattern at
    819         * which to start drawing.
    820         * <p>
    821         *
    822         * @param feature
    823         *            specifies the <tt>Feature</tt> to be used for evaluation of the underlying 'sld:ParameterValueType'
    824         * @return the (evaluated) value of the parameter
    825         * @throws FilterEvaluationException
    826         *             if the evaluation fails
    827         */
    828        public float getDashOffset( Feature feature )
    829                                throws FilterEvaluationException {
    830            float dashOffset = 0;
    831
    832            if ( smplDashOffset < 0 ) {
    833                CssParameter cssParam = (CssParameter) cssParams.get( "stroke-dashoffset" );
    834                if ( cssParam != null ) {
    835                    String value = cssParam.getValue( feature );
    836
    837                    try {
    838                        dashOffset = Float.parseFloat( value );
    839                    } catch ( NumberFormatException e ) {
    840                        throw new FilterEvaluationException( "Given value for parameter 'stroke-dashoffset' ('" + value
    841                                                             + "') has invalid format!" );
    842                    }
    843                }
    844            } else {
    845                dashOffset = smplDashOffset;
    846            }
    847
    848            return dashOffset;
    849        }
    850
    851        /**
    852         * The stroke-dashoffset CssParameter element specifies the distance as a float into the stroke-dasharray pattern at
    853         * which to start drawing.
    854         * <p>
    855         *
    856         * @param dashOffset
    857         *            the dashOffset to be set for the Stroke
    858         */
    859        public void setDashOffset( float dashOffset ) {
    860            if ( dashOffset < 0 )
    861                dashOffset = 0;
    862            smplDashOffset = dashOffset;
    863            CssParameter strokeDashOff = StyleFactory.createCssParameter( "stroke-dashoffset", "" + dashOffset );
    864            cssParams.put( "stroke-dashoffset", strokeDashOff );
    865        }
    866
    867        /**
    868         * exports the content of the Stroke as XML formated String
    869         *
    870         * @return xml representation of the Stroke
    871         */
    872        public String exportAsXML() {
    873
    874            StringBuffer sb = new StringBuffer( 1000 );
    875            sb.append( "<Stroke>" );
    876
    877            if ( graphicFill != null ) {
    878                sb.append( ( (Marshallable) graphicFill ).exportAsXML() );
    879            } else if ( graphicStroke != null ) {
    880                sb.append( ( (Marshallable) graphicStroke ).exportAsXML() );
    881            }
    882            for ( Object o : cssParams.values() ) {
    883                sb.append( ( (Marshallable) o ).exportAsXML() );
    884            }
    885
    886            sb.append( "</Stroke>" );
    887
    888            return sb.toString();
    889        }
    890
    891    }