/**
   * Get the java2D Stroke for the given feature.
   *
   * @param candidate : evaluate stroke with the given feature
   * @param coeff : use to adjust stroke size, if in display unit value equals 1
   * @return Java2D Stroke
   */
  public java.awt.Stroke getJ2DStroke(final Object candidate, float coeff) {
    evaluate();

    coeff = Math.abs(coeff);

    java.awt.Stroke j2dStroke = cachedStroke;
    // if stroke is null it means something is dynamic
    if (j2dStroke == null || coeff != 1) {

      float[] candidateDashes = cachedDashes;
      float candidateOffset = cachedDashOffset;
      int candidateCap = cachedCap;
      int candidateJoin = cachedJoin;
      float candidateWidth = cachedWidth;

      if (Float.isNaN(candidateOffset)) {
        final Expression expOffset = styleElement.getDashOffset();
        candidateOffset = GO2Utilities.evaluate(expOffset, candidate, Float.class, 1f);
      }

      if (candidateCap == Integer.MAX_VALUE) {
        final Expression expCap = styleElement.getLineCap();
        final String cap =
            GO2Utilities.evaluate(expCap, null, String.class, STROKE_CAP_BUTT_STRING);
        if (STROKE_CAP_BUTT_STRING.equalsIgnoreCase(cap)) candidateCap = BasicStroke.CAP_BUTT;
        else if (STROKE_CAP_SQUARE_STRING.equalsIgnoreCase(cap))
          candidateCap = BasicStroke.CAP_SQUARE;
        else if (STROKE_CAP_ROUND_STRING.equalsIgnoreCase(cap))
          candidateCap = BasicStroke.CAP_ROUND;
        else candidateCap = BasicStroke.CAP_BUTT;
      }

      if (candidateJoin == Integer.MAX_VALUE) {
        final Expression expJoin = styleElement.getLineJoin();
        final String join =
            GO2Utilities.evaluate(expJoin, null, String.class, STROKE_JOIN_BEVEL_STRING);
        if (STROKE_JOIN_BEVEL_STRING.equalsIgnoreCase(join)) candidateJoin = BasicStroke.JOIN_BEVEL;
        else if (STROKE_JOIN_MITRE_STRING.equalsIgnoreCase(join))
          candidateJoin = BasicStroke.JOIN_MITER;
        else if (STROKE_JOIN_ROUND_STRING.equalsIgnoreCase(join))
          candidateJoin = BasicStroke.JOIN_ROUND;
        else candidateJoin = BasicStroke.JOIN_BEVEL;
      }

      if (Float.isNaN(candidateWidth)) {
        final Expression expWidth = styleElement.getWidth();
        candidateWidth = GO2Utilities.evaluate(expWidth, candidate, Float.class, 1f);
        if (candidateWidth < 0) {
          candidateWidth = 0f;
        }
      }

      if (candidateDashes != null) {
        float[] s = candidateDashes.clone();
        for (int i = 0; i < s.length; i++) {
          s[i] = s[i] * coeff;
        }
        j2dStroke =
            new BasicStroke(
                candidateWidth * coeff, candidateCap, candidateJoin, 1f, s, candidateOffset);
      } else {
        j2dStroke = new BasicStroke(candidateWidth * coeff, candidateCap, candidateJoin, 10f);
      }
    }

    return j2dStroke;
  }
  private boolean evaluateStroke() {
    final float[] dashArray = styleElement.getDashArray();
    final Expression expOffset = styleElement.getDashOffset();
    final Expression expLineCap = styleElement.getLineCap();
    final Expression expLineJoin = styleElement.getLineJoin();
    final Expression expWidth = styleElement.getWidth();
    boolean strokeStatic = true;

    float[] candidateDashes = null;
    float candidateOffset = Float.NaN;
    int candidateCap = -1;
    int candidateJoin = -1;
    float candidateWidth = Float.NaN;

    candidateDashes = GO2Utilities.validDashes(dashArray);

    // offset ----------------------------------------------
    if (GO2Utilities.isStatic(expOffset)) {
      candidateOffset = GO2Utilities.evaluate(expOffset, null, Float.class, 1f);
    } else {
      strokeStatic = false;
      GO2Utilities.getRequieredAttributsName(expOffset, requieredAttributs);
    }

    // line width ------------------------------------------
    if (GO2Utilities.isStatic(expWidth)) {
      candidateWidth = GO2Utilities.evaluate(expWidth, null, Float.class, 1f);

      // we return false, width is 0 no need to cache or draw anything
      if (candidateWidth == 0) {
        isStaticVisible = VisibilityState.UNVISIBLE;
        return false;
      }

      // this style is visible
      if (isStaticVisible == VisibilityState.NOT_DEFINED) isStaticVisible = VisibilityState.VISIBLE;

    } else {
      // this style visibility is dynamic
      if (isStaticVisible != VisibilityState.UNVISIBLE) isStaticVisible = VisibilityState.DYNAMIC;
      strokeStatic = false;
      GO2Utilities.getRequieredAttributsName(expWidth, requieredAttributs);
    }

    // line cap and join---------------------------------------------
    if (cachedWidth <= 2.5f) {
      // line cap and join are invisible under this size
      candidateCap = BasicStroke.CAP_SQUARE;
      candidateJoin = BasicStroke.JOIN_MITER;
    } else {
      if (GO2Utilities.isStatic(expLineCap)) {
        final String cap =
            GO2Utilities.evaluate(expLineCap, null, String.class, STROKE_CAP_BUTT_STRING);
        if (STROKE_CAP_BUTT_STRING.equalsIgnoreCase(cap)) candidateCap = BasicStroke.CAP_BUTT;
        else if (STROKE_CAP_SQUARE_STRING.equalsIgnoreCase(cap))
          candidateCap = BasicStroke.CAP_SQUARE;
        else if (STROKE_CAP_ROUND_STRING.equalsIgnoreCase(cap))
          candidateCap = BasicStroke.CAP_ROUND;
        else candidateCap = BasicStroke.CAP_BUTT;
      } else {
        strokeStatic = false;
        GO2Utilities.getRequieredAttributsName(expLineCap, requieredAttributs);
      }

      if (GO2Utilities.isStatic(expLineJoin)) {
        final String join =
            GO2Utilities.evaluate(expLineJoin, null, String.class, STROKE_JOIN_BEVEL_STRING);
        if (STROKE_JOIN_BEVEL_STRING.equalsIgnoreCase(join)) candidateJoin = BasicStroke.JOIN_BEVEL;
        else if (STROKE_JOIN_MITRE_STRING.equalsIgnoreCase(join))
          candidateJoin = BasicStroke.JOIN_MITER;
        else if (STROKE_JOIN_ROUND_STRING.equalsIgnoreCase(join))
          candidateJoin = BasicStroke.JOIN_ROUND;
        else candidateJoin = BasicStroke.JOIN_BEVEL;
      } else {
        strokeStatic = false;
        GO2Utilities.getRequieredAttributsName(expLineJoin, requieredAttributs);
      }
    }

    // we cache each possible expression ------------------------------
    this.cachedDashes = candidateDashes;
    if (!Float.isNaN(candidateOffset))
      cachedDashOffset = (candidateOffset > 0) ? candidateOffset : 0;
    if (candidateCap != -1) cachedCap = candidateCap;
    if (candidateJoin != -1) cachedJoin = candidateJoin;
    if (!Float.isNaN(candidateWidth)) {
      cachedWidth = candidateWidth;
      if (cachedWidth < 0) cachedWidth = 0f;
    }

    // if static we can can cache the stroke directly----------------------
    if (strokeStatic) {
      // we can never cache the java2d stroke seens it's size depend on the symbolizer unit of
      // mesure
      if (cachedDashes != null) {
        cachedStroke =
            new BasicStroke(
                candidateWidth, candidateCap, candidateJoin, 10f, cachedDashes, cachedDashOffset);
      } else {
        cachedStroke = new BasicStroke(candidateWidth, candidateCap, candidateJoin, 10f);
      }
    } else {
      this.isStatic = false;
    }

    return true;
  }