1 /*
   2  * Copyright (c) 2010, 2014, 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.shape;
  27 
  28 import com.sun.javafx.scene.DirtyBits;
  29 import com.sun.javafx.sg.prism.NGNode;
  30 import com.sun.javafx.sg.prism.NGShape;
  31 import javafx.beans.value.WritableValue;
  32 import javafx.collections.FXCollections;
  33 import javafx.collections.ListChangeListener;
  34 import javafx.collections.ObservableList;
  35 import javafx.css.CssMetaData;
  36 import javafx.css.Styleable;
  37 import javafx.css.StyleableProperty;
  38 import javafx.scene.Group;
  39 import javafx.scene.NodeTest;
  40 import javafx.scene.Scene;
  41 import javafx.scene.paint.Color;
  42 import org.junit.Assert;
  43 import org.junit.Test;
  44 
  45 import java.util.ArrayList;
  46 import java.util.Collections;
  47 import java.util.List;
  48 
  49 import static org.junit.Assert.*;
  50 
  51 public class ShapeTest {
  52 
  53     @Test public void testBoundPropertySync_StrokeType() throws Exception {
  54         NodeTest.assertObjectProperty_AsStringSynced(
  55                 new StubShape(),
  56                 "strokeType", "strokeType", StrokeType.CENTERED);
  57     }
  58 
  59     @Test public void testBoundPropertySync_StrokeLineCap() throws Exception {
  60         NodeTest.assertObjectProperty_AsStringSynced(
  61                 new StubShape(),
  62                 "strokeLineCap", "strokeLineCap", StrokeLineCap.SQUARE);
  63     }
  64 
  65     @Test public void testBoundPropertySync_StrokeLineJoin() throws Exception {
  66         NodeTest.assertObjectProperty_AsStringSynced(
  67                 new StubShape(),
  68                 "strokeLineJoin", "strokeLineJoin", StrokeLineJoin.MITER);
  69     }
  70 
  71     @Test public void testBoundPropertySync_StrokeWidth() throws Exception {
  72         NodeTest.assertDoublePropertySynced(
  73                 new StubShape(),
  74                 "strokeWidth", "strokeWidth", 2.0);
  75     }
  76 
  77     @Test public void testBoundPropertySync_StrokeMiterLimit() throws Exception {
  78         NodeTest.assertDoublePropertySynced(
  79                 new StubShape(),
  80                 "strokeMiterLimit", "strokeMiterLimit", 3.0);
  81     }
  82 
  83     @Test public void testBoundPropertySync_DashOffset() throws Exception {
  84         NodeTest.assertDoublePropertySynced(
  85                 new StubShape(),
  86                 "strokeDashOffset", "strokeDashOffset", 15.0);
  87     }
  88 
  89     @Test public void testBoundPropertySync_Stroke() throws Exception {
  90         final Shape shape = new StubShape();
  91         shape.setStroke(Color.RED);
  92         NodeTest.assertObjectProperty_AsStringSynced(
  93                 shape, "stroke", "stroke", Color.GREEN);
  94     }
  95 
  96     @Test public void testBoundPropertySync_Fill() throws Exception {
  97         final Shape shape = new StubShape();
  98         shape.setFill(Color.BLUE);
  99         NodeTest.assertObjectProperty_AsStringSynced(
 100                 shape, "fill", "fill", Color.RED);
 101     }
 102 
 103     @Test public void testBoundPropertySync_Smooth() throws Exception {
 104         final Shape shape = new StubShape();
 105         shape.setSmooth(true);
 106         NodeTest.assertBooleanPropertySynced(
 107                 shape, "smooth", "smooth", false);
 108     }
 109     
 110     boolean listChangeCalled = false;
 111     @Test public void testStrokeDashArray() {
 112         // there is no strokeDashArrayProperty or this test
 113         // would be in Shape_properties_Test
 114         final ObservableList<Double> expected = 
 115                 FXCollections.observableArrayList(Double.valueOf(1),
 116                                                   Double.valueOf(2),
 117                                                   Double.valueOf(3));
 118         final Shape shape = new StubShape();
 119         ObservableList<Double> actual = shape.getStrokeDashArray();
 120         assertNotNull(actual);
 121         assertTrue(actual.isEmpty());
 122 
 123         actual.addListener((ListChangeListener<Double>) c -> {
 124             listChangeCalled = true;
 125             assertTrue(c.next());
 126             Assert.assertEquals(expected, c.getAddedSubList());
 127         });
 128         
 129         shape.getStrokeDashArray().addAll(expected);
 130         actual = shape.getStrokeDashArray();
 131         assertEquals(expected, actual);
 132         
 133         assertTrue(listChangeCalled);
 134     }
 135     
 136     @Test public void testGetStrokeDashArrayViaCSSPropertyIsNotNull() {
 137         final Shape shape = new StubShape();
 138         Double[] actual = null;
 139         List<CssMetaData<? extends Styleable, ?>> styleables = shape.getCssMetaData();
 140         for (CssMetaData styleable : styleables) {
 141             if ("-fx-stroke-dash-array".equals(styleable.getProperty())) {
 142                 WritableValue writable = styleable.getStyleableProperty(shape);
 143                 actual = (Double[])writable.getValue();
 144                 break;
 145             }
 146         }
 147         assertNotNull(actual);
 148     }
 149     
 150     @Test public void testGetStrokeDashArrayViaCSSPropertyIsSame() {
 151         final Shape shape = new StubShape();
 152         shape.getStrokeDashArray().addAll(5d, 7d, 1d, 3d);
 153         Double[] actuals = null;
 154         List<CssMetaData<? extends Styleable, ?>> styleables = shape.getCssMetaData();
 155         
 156         for (CssMetaData styleable : styleables) {
 157             if ("-fx-stroke-dash-array".equals(styleable.getProperty())) {
 158                 WritableValue writable = styleable.getStyleableProperty(shape);
 159                 actuals = (Double[])writable.getValue();
 160             }
 161         }
 162         
 163         final Double[] expecteds = new Double[] {5d, 7d, 1d, 3d};
 164         Assert.assertArrayEquals(expecteds, actuals);
 165     }
 166 
 167     @Test public void testSetStrokeDashArrayViaCSSPropertyIsSame() {
 168         final Shape shape = new StubShape();
 169         List<Double> actual = null;
 170         List<CssMetaData<? extends Styleable, ?>> styleables = shape.getCssMetaData();
 171         
 172         for (CssMetaData styleable : styleables) {
 173             if ("-fx-stroke-dash-array".equals(styleable.getProperty())) {
 174                 StyleableProperty styleableProperty = styleable.getStyleableProperty(shape);
 175                 styleableProperty.applyStyle(null, new Double[] {5d, 7d, 1d, 3d});
 176                 actual = shape.getStrokeDashArray();
 177             }
 178         }
 179         
 180         final List<Double> expected = new ArrayList();
 181         Collections.addAll(expected, 5d, 7d, 1d, 3d);
 182         assertEquals(expected, actual);
 183     }
 184     
 185     // RT-18647: ClassCastException: [Ljava.lang.Double; cannot be cast to javafx.collections.ObservableList
 186     @Test public void testRT_18647() {
 187         final Scene scene = new Scene(new Group(), 500, 500);
 188         
 189         final Shape shape = new StubShape();
 190         shape.setStyle("-fx-stroke-dash-array: 5 7 1 3;");
 191 
 192         ((Group)scene.getRoot()).getChildren().add(shape);
 193         shape.applyCss();
 194 
 195         final List<Double> expected = new ArrayList();
 196         Collections.addAll(expected, 5d, 7d, 1d, 3d);
 197 
 198         List<Double> actual = shape.getStrokeDashArray();
 199         assertEquals(expected, actual);        
 200         
 201     }
 202 
 203     boolean listenerCalled = false;
 204     // make sure shapeChangeListener doesn't hold reference to runnable.
 205     @Test public void testShapeChangeListenerLeakTest() {
 206 
 207         Shape shape = new StubShape();
 208 
 209         Runnable listener = () -> {
 210             listenerCalled = true;
 211         };
 212 
 213         shape.impl_setShapeChangeListener(listener);
 214 
 215         // sync peer to clear out dirty bits
 216         shape.impl_syncPeer();
 217 
 218         // should trigger listener
 219         shape.setFill(Color.GREEN);
 220 
 221         assert(listenerCalled);
 222 
 223         listener = null;
 224         System.gc();
 225 
 226         // sync peer to clear out dirty bits
 227         shape.impl_syncPeer();
 228 
 229         // this flag should remain false (listener should not be called)
 230         listenerCalled = false;
 231 
 232         shape.setFill(Color.RED);
 233 
 234         assert(!listenerCalled);
 235     }
 236 
 237     public class StubShape extends Shape {
 238 
 239         public StubShape() {
 240             setStroke(Color.BLACK);
 241         }
 242 
 243         @Override
 244         protected NGNode impl_createPeer() {
 245             return new StubNGShape();
 246         }
 247 
 248         @Override public com.sun.javafx.geom.Shape impl_configShape() {
 249             return new com.sun.javafx.geom.RoundRectangle2D(0, 0, 10, 10, 4, 4);
 250         }
 251     }
 252 
 253     public class StubNGShape extends NGShape {
 254         private StrokeType pgStrokeType;
 255         private StrokeLineCap pgStrokeLineCap;
 256         private StrokeLineJoin pgStrokeLineJoin;
 257         private float strokeWidth;
 258         private float strokeMiterLimit;
 259         private float[] strokeDashArray;
 260         private float strokeDashOffset;
 261         private Object stroke;
 262         private Mode mode;
 263         private boolean smooth;
 264         private Object fill;
 265 
 266         public Object getFill() { return fill; }
 267         public boolean isSmooth() { return smooth; }
 268         public Mode getMode() { return mode; }
 269         public Object getStroke() { return stroke; }
 270         public float getStrokeDashOffset() { return strokeDashOffset; }
 271         public float getStrokeMiterLimit() { return strokeMiterLimit; }
 272         public float getStrokeWidth() { return strokeWidth; }
 273         public StrokeType getStrokeType() { return pgStrokeType; }
 274         public StrokeLineCap getStrokeLineCap() { return pgStrokeLineCap; }
 275         public StrokeLineJoin getStrokeLineJoin() { return pgStrokeLineJoin; }
 276         public void setMode(Mode mode) { this.mode = mode; }
 277         @Override public void setSmooth(boolean smooth) { this.smooth = smooth; }
 278         @Override public void setFillPaint(Object fillPaint) { this.fill = fillPaint; }
 279         @Override public void setDrawPaint(Object drawPaint) { this.stroke = drawPaint; }
 280         @Override public void setDrawStroke(float strokeWidth,
 281                                   StrokeType type,
 282                                   StrokeLineCap lineCap,
 283                                   StrokeLineJoin lineJoin,
 284                                   float strokeMiterLimit,
 285                                   float[] strokeDashArray,
 286                                   float strokeDashOffset) {
 287             this.pgStrokeType = type;
 288             this.pgStrokeLineCap = lineCap;
 289             this.pgStrokeLineJoin = lineJoin;
 290             this.strokeWidth = strokeWidth;
 291             this.strokeMiterLimit = strokeMiterLimit;
 292             this.strokeDashOffset = strokeDashOffset;
 293             this.strokeDashArray = new float[strokeDashArray == null ? 0 : strokeDashArray.length];
 294             System.arraycopy(strokeDashArray, 0, this.strokeDashArray, 0, this.strokeDashArray.length);
 295         }
 296 
 297         @Override
 298         public com.sun.javafx.geom.Shape getShape() {
 299             return null;
 300         }
 301     }
 302 }