1 /*
   2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.layout;
  27 
  28 import com.sun.javafx.css.StyleManager;
  29 import com.sun.javafx.scene.layout.region.BorderImageSlices;
  30 import com.sun.javafx.scene.layout.region.Margins;
  31 import com.sun.javafx.scene.layout.region.RepeatStruct;
  32 import java.util.Map;
  33 import javafx.css.CssMetaData;
  34 import javafx.css.ParsedValue;
  35 import javafx.css.Styleable;
  36 import javafx.css.StyleConverter;
  37 import javafx.geometry.Insets;
  38 import javafx.scene.image.Image;
  39 import javafx.scene.paint.Color;
  40 import javafx.scene.paint.Paint;
  41 
  42 /**
  43  */
  44 class BorderConverter extends StyleConverter<ParsedValue[], Border> {
  45 
  46     private static final BorderConverter BORDER_IMAGE_CONVERTER =
  47             new BorderConverter();
  48 
  49     public static BorderConverter getInstance() {
  50         return BORDER_IMAGE_CONVERTER;
  51     }
  52 
  53     // Disallow instantiation
  54     private BorderConverter() { }
  55 
  56     @Override
  57     public Border convert(Map<CssMetaData<? extends Styleable, ?>, Object> convertedValues) {
  58         final Paint[][] strokeFills = (Paint[][])convertedValues.get(Border.BORDER_COLOR);
  59         final BorderStrokeStyle[][] strokeStyles = (BorderStrokeStyle[][]) convertedValues.get(Border.BORDER_STYLE);
  60         final String[] imageUrls = (String[]) convertedValues.get(Border.BORDER_IMAGE_SOURCE);
  61         //
  62         // In W3C CSS, border colors and border images are not layered. In javafx, they are. We've taken the position
  63         // that there is one layer per -fx-border-color or -fx-border-image-source. This is consistent with
  64         // background-image (see http://www.w3.org/TR/css3-background/#layering). But, in a browser, you can have a
  65         // border-style with no corresponding border-color - the border-color just defaults to 'currentColor' (which
  66         // we don't have so we'll call it 'black' for the time being). So the number of stroke-border layers is now
  67         // determined by the max of strokeFills.length and strokeStyles.length. If there are more styles than fills,
  68         // the remaining styles will use the last fill value (this is consistent with handling of the other stroke
  69         // border properties). If there aren't any fills at all, then the fill is 'currentColor' (i.e., black) just
  70         // as the default stroke is solid.
  71         //
  72         final boolean hasStrokes = (strokeFills != null && strokeFills.length > 0) || (strokeStyles != null && strokeStyles.length > 0);
  73         final boolean hasImages = imageUrls != null && imageUrls.length > 0;
  74 
  75         // If there are neither background fills nor images, then there is nothing for us to construct.
  76         if (!hasStrokes && !hasImages) return null;
  77 
  78         BorderStroke[] borderStrokes = null;
  79         if (hasStrokes) {
  80 
  81             final int lastStrokeFill = strokeFills != null ? strokeFills.length - 1 : -1;
  82             final int lastStrokeStyle = strokeStyles != null ? strokeStyles.length - 1 : -1;
  83             final int nLayers = (lastStrokeFill >= lastStrokeStyle ? lastStrokeFill : lastStrokeStyle) + 1;
  84 
  85             Object tmp = convertedValues.get(Border.BORDER_WIDTH);
  86             final Margins[] borderWidths = tmp == null ? new Margins[0] : (Margins[]) tmp;
  87             final int lastMarginIndex = borderWidths.length - 1;
  88 
  89             tmp = convertedValues.get(Border.BORDER_RADIUS);
  90             final CornerRadii[] borderRadii = tmp == null ? new CornerRadii[0] : (CornerRadii[]) tmp;
  91             final int lastRadiusIndex = borderRadii.length - 1;
  92 
  93             tmp = convertedValues.get(Border.BORDER_INSETS);
  94             final Insets[] borderInsets = tmp == null ? new Insets[0] : (Insets[]) tmp;
  95             final int lastInsetsIndex = borderInsets.length - 1;
  96 
  97             for (int i=0; i<nLayers; i++) {
  98 
  99                 BorderStrokeStyle[] styles;
 100                 // if there are no strokeStyles, then lastStrokeStyle will be < 0
 101                 if (lastStrokeStyle < 0) {
 102                     styles = new BorderStrokeStyle[4];
 103                     styles[0] = styles[1] = styles[2] = styles[3] = BorderStrokeStyle.SOLID;
 104                 } else {
 105                     styles = strokeStyles[i <= lastStrokeStyle ? i : lastStrokeStyle];
 106                 }
 107 
 108                 if (styles[0] == BorderStrokeStyle.NONE &&
 109                         styles[1] == BorderStrokeStyle.NONE &&
 110                         styles[2] == BorderStrokeStyle.NONE &&
 111                         styles[3] == BorderStrokeStyle.NONE) continue;
 112 
 113                 Paint[] strokes;
 114                 // if there are no strokeFills, then lastStrokeFill will be < 0
 115                 if (lastStrokeFill < 0) {
 116                     strokes = new Paint[4];
 117                     // TODO: should be 'currentColor'
 118                     strokes[0] = strokes[1] = strokes[2] = strokes[3] = Color.BLACK;
 119                 }  else {
 120                     strokes = strokeFills[i <= lastStrokeFill ? i : lastStrokeFill];
 121                 }
 122 
 123                 if (borderStrokes == null) borderStrokes = new BorderStroke[nLayers];
 124 
 125                 final Margins margins = borderWidths.length == 0 ?
 126                         null :
 127                         borderWidths[i <= lastMarginIndex ? i : lastMarginIndex];
 128                 final CornerRadii radii = borderRadii.length == 0 ?
 129                         CornerRadii.EMPTY :
 130                         borderRadii[i <= lastRadiusIndex ? i : lastRadiusIndex];
 131                 final Insets insets = borderInsets.length == 0 ?
 132                         null :
 133                         borderInsets[i <= lastInsetsIndex ? i : lastInsetsIndex];
 134 
 135                 borderStrokes[i] = new BorderStroke(
 136                         strokes[0], strokes[1], strokes[2], strokes[3],
 137                         styles[0], styles[1], styles[2], styles[3],
 138                         radii,
 139                         margins == null ?
 140                                 BorderStroke.DEFAULT_WIDTHS :
 141                                 new BorderWidths(margins.getTop(), margins.getRight(), margins.getBottom(), margins.getLeft()),
 142                         insets);
 143             }
 144         }
 145 
 146         BorderImage[] borderImages = null;
 147         if (hasImages) {
 148             borderImages = new BorderImage[imageUrls.length];
 149             Object tmp = convertedValues.get(Border.BORDER_IMAGE_REPEAT);
 150             final RepeatStruct[] repeats = tmp == null ? new RepeatStruct[0] : (RepeatStruct[]) tmp;
 151             final int lastRepeatIndex = repeats.length - 1;
 152 
 153             tmp = convertedValues.get(Border.BORDER_IMAGE_SLICE);
 154             final BorderImageSlices[] slices = tmp == null ? new BorderImageSlices[0] : (BorderImageSlices[]) tmp;
 155             final int lastSlicesIndex = slices.length - 1;
 156 
 157             tmp = convertedValues.get(Border.BORDER_IMAGE_WIDTH);
 158             final BorderWidths[] widths = tmp == null ? new BorderWidths[0] : (BorderWidths[]) tmp;
 159             final int lastWidthsIndex = widths.length - 1;
 160 
 161             tmp = convertedValues.get(Border.BORDER_IMAGE_INSETS);
 162             final Insets[] insets = tmp == null ? new Insets[0] : (Insets[]) tmp;
 163             final int lastInsetsIndex = insets.length - 1;
 164 
 165             for (int i=0; i<imageUrls.length; i++) {
 166                 if (imageUrls[i] == null) continue;
 167                 BorderRepeat repeatX = BorderRepeat.STRETCH, repeatY = BorderRepeat.STRETCH;
 168                 if (repeats.length > 0) {
 169                     final RepeatStruct repeat = repeats[i <= lastRepeatIndex ? i : lastRepeatIndex];
 170                     switch (repeat.repeatX) {
 171                         case SPACE: repeatX = BorderRepeat.SPACE; break;
 172                         case ROUND: repeatX = BorderRepeat.ROUND; break;
 173                         case REPEAT: repeatX = BorderRepeat.REPEAT; break;
 174                         case NO_REPEAT: repeatX = BorderRepeat.STRETCH; break;
 175                     }
 176                     switch (repeat.repeatY) {
 177                         case SPACE: repeatY = BorderRepeat.SPACE; break;
 178                         case ROUND: repeatY = BorderRepeat.ROUND; break;
 179                         case REPEAT: repeatY = BorderRepeat.REPEAT; break;
 180                         case NO_REPEAT: repeatY = BorderRepeat.STRETCH; break;
 181                     }
 182                 }
 183 
 184                 final BorderImageSlices slice = slices.length > 0 ? slices[i <= lastSlicesIndex ? i : lastSlicesIndex] : BorderImageSlices.DEFAULT;
 185                 final Insets inset = insets.length > 0 ? insets[i <= lastInsetsIndex ? i : lastInsetsIndex] : Insets.EMPTY;
 186                 final BorderWidths width = widths.length > 0 ? widths[i <= lastWidthsIndex ? i : lastWidthsIndex] : BorderWidths.DEFAULT;
 187                 final Image img = StyleManager.getInstance().getCachedImage(imageUrls[i]);
 188                 borderImages[i] = new BorderImage(img, width, inset, slice.widths, slice.filled, repeatX, repeatY);
 189             }
 190         }
 191 
 192         return borderStrokes == null && borderImages == null ? null : new Border(borderStrokes, borderImages);
 193     }
 194 
 195     /**
 196      * @inheritDoc
 197      */
 198     @Override public String toString() {
 199         return "BorderConverter";
 200     }
 201 }