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 com.sun.javafx.css; 27 28 import com.sun.javafx.FXUnit; 29 import com.sun.javafx.css.converters.FontConverter; 30 import com.sun.javafx.css.converters.SizeConverter; 31 import com.sun.javafx.tk.Toolkit; 32 import java.util.ArrayList; 33 import java.util.Collections; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 import javafx.collections.FXCollections; 38 import javafx.collections.MapChangeListener; 39 import javafx.css.CssMetaData; 40 import javafx.css.ParsedValue; 41 import javafx.css.StyleOrigin; 42 import javafx.css.StyleableProperty; 43 import javafx.scene.Group; 44 import javafx.scene.Node; 45 import javafx.scene.Scene; 46 import javafx.scene.paint.Color; 47 import javafx.scene.shape.Rectangle; 48 import javafx.scene.text.Font; 49 import javafx.scene.text.Text; 50 import javafx.stage.Stage; 51 import static org.junit.Assert.*; 52 53 import org.junit.Ignore; 54 import org.junit.Test; 55 56 public class Node_cssStyleMap_Test { 57 58 @org.junit.Rule 59 public FXUnit fx = new FXUnit(); 60 61 public Node_cssStyleMap_Test() { 62 } 63 64 boolean disabled = false; 65 int nadds = 0; 66 int nremoves = 0; 67 68 static List<CascadingStyle> createStyleList(List<Declaration> decls) { 69 70 final List<CascadingStyle> styles = new ArrayList<CascadingStyle>(); 71 72 for (Declaration decl : decls) { 73 styles.add( 74 new CascadingStyle( 75 new Style(decl.rule.getUnobservedSelectorList().get(0), decl), 76 new PseudoClassState(), 77 0, 78 0 79 ) 80 ); 81 } 82 83 return styles; 84 } 85 86 static Map<String, List<CascadingStyle>> createStyleMap(List<CascadingStyle> styles) { 87 88 final Map<String, List<CascadingStyle>> smap = 89 new HashMap<String, List<CascadingStyle>>(); 90 91 final int max = styles != null ? styles.size() : 0; 92 for (int i=0; i<max; i++) { 93 final CascadingStyle style = styles.get(i); 94 final String property = style.getProperty(); 95 // This is carefully written to use the minimal amount of hashing. 96 List<CascadingStyle> list = smap.get(property); 97 if (list == null) { 98 list = new ArrayList<CascadingStyle>(5); 99 smap.put(property, list); 100 } 101 list.add(style); 102 } 103 return smap; 104 } 105 106 @Test @Ignore ("Pending RT-34463") 107 public void testStyleMapTracksChanges() { 108 109 final List<Declaration> declsNoState = new ArrayList<Declaration>(); 110 Collections.addAll(declsNoState, 111 new Declaration("-fx-fill", new ParsedValueImpl<Color,Color>(Color.RED, null), false), 112 new Declaration("-fx-stroke", new ParsedValueImpl<Color,Color>(Color.YELLOW, null), false), 113 new Declaration("-fx-stroke-width", new ParsedValueImpl<ParsedValue<?,Size>,Number>( 114 new ParsedValueImpl<Size,Size>(new Size(3d, SizeUnits.PX), null), 115 SizeConverter.getInstance()), false) 116 ); 117 118 119 final List<Selector> selsNoState = new ArrayList<Selector>(); 120 Collections.addAll(selsNoState, 121 Selector.createSelector(".rect") 122 ); 123 124 Rule rule = new Rule(selsNoState, declsNoState); 125 126 Stylesheet stylesheet = new Stylesheet("testStyleMapTracksChanges"); 127 stylesheet.setOrigin(StyleOrigin.USER_AGENT); 128 stylesheet.getRules().add(rule); 129 130 final List<Declaration> declsDisabledState = new ArrayList<Declaration>(); 131 Collections.addAll(declsDisabledState, 132 new Declaration("-fx-fill", new ParsedValueImpl<Color,Color>(Color.GRAY, null), false), 133 new Declaration("-fx-stroke", new ParsedValueImpl<Color,Color>(Color.DARKGRAY, null), false) 134 ); 135 136 final List<Selector> selsDisabledState = new ArrayList<Selector>(); 137 Collections.addAll(selsDisabledState, 138 Selector.createSelector(".rect:disabled") 139 ); 140 141 rule = new Rule(selsDisabledState, declsDisabledState); 142 stylesheet.getRules().add(rule); 143 144 final List<CascadingStyle> stylesNoState = createStyleList(declsNoState); 145 final List<CascadingStyle> stylesDisabledState = createStyleList(declsDisabledState); 146 147 // add to this list on wasAdded, check bean on wasRemoved. 148 final List<StyleableProperty<?>> beans = new ArrayList<StyleableProperty<?>>(); 149 Rectangle rect = new Rectangle(50,50); 150 rect.getStyleClass().add("rect"); 151 rect.impl_setStyleMap(FXCollections.observableMap(new HashMap<StyleableProperty<?>, List<Style>>())); 152 rect.impl_getStyleMap().addListener(new MapChangeListener<StyleableProperty, List<Style>>() { 153 154 public void onChanged(MapChangeListener.Change<? extends StyleableProperty, ? extends List<Style>> change) { 155 156 if (change.wasAdded()) { 157 158 List<Style> styles = change.getValueAdded(); 159 for (Style style : styles) { 160 161 // stroke width comes from ".rect" even for disabled state. 162 if (disabled == false || "-fx-stroke-width".equals(style.getDeclaration().getProperty())) { 163 assertTrue(style.getDeclaration().toString(),declsNoState.contains(style.getDeclaration())); 164 assertTrue(style.getSelector().toString(),selsNoState.contains(style.getSelector())); 165 } else { 166 assertTrue(style.getDeclaration().toString(),declsDisabledState.contains(style.getDeclaration())); 167 assertTrue(style.getSelector().toString(),selsDisabledState.contains(style.getSelector())); 168 } 169 Object value = style.getDeclaration().parsedValue.convert(null); 170 StyleableProperty styleableProperty = change.getKey(); 171 beans.add(styleableProperty); 172 assertEquals(styleableProperty.getValue(), value); 173 nadds += 1; 174 } 175 176 } if (change.wasRemoved()) { 177 StyleableProperty styleableProperty = change.getKey(); 178 assert(beans.contains(styleableProperty)); 179 nremoves += 1; 180 } 181 } 182 }); 183 184 Group root = new Group(); 185 root.getChildren().add(rect); 186 StyleManager.getInstance().setDefaultUserAgentStylesheet(stylesheet); 187 Scene scene = new Scene(root); 188 Stage stage = new Stage(); 189 stage.setScene(scene); 190 stage.show(); 191 192 // The three no state styles should be applied 193 assertEquals(3, nadds); 194 assertEquals(0, nremoves); 195 196 rect.setDisable(true); 197 disabled = true; 198 nadds = 0; 199 nremoves = 0; 200 201 Toolkit.getToolkit().firePulse(); 202 203 // The three no state styles should be removed and the 204 // two disabled state styles plus the stroke width style 205 // should be applied. 206 assertEquals(3, nadds); 207 assertEquals(3, nremoves); 208 209 } 210 211 @Test @Ignore ("Pending RT-34463") 212 public void testRT_21212() { 213 214 final List<Declaration> rootDecls = new ArrayList<Declaration>(); 215 Collections.addAll(rootDecls, 216 new Declaration("-fx-font-size", new ParsedValueImpl<ParsedValue<?,Size>,Number>( 217 new ParsedValueImpl<Size,Size>(new Size(12, SizeUnits.PX), null), 218 SizeConverter.getInstance()), false) 219 ); 220 221 final List<Selector> rootSels = new ArrayList<Selector>(); 222 Collections.addAll(rootSels, 223 Selector.createSelector(".root") 224 ); 225 226 Rule rootRule = new Rule(rootSels, rootDecls); 227 228 Stylesheet stylesheet = new Stylesheet("testRT_21212"); 229 stylesheet.setOrigin(StyleOrigin.USER_AGENT); 230 stylesheet.getRules().add(rootRule); 231 232 final List<CascadingStyle> rootStyles = createStyleList(rootDecls); 233 final Map<String,List<CascadingStyle>> rootStyleMap = createStyleMap(rootStyles); 234 final Map<StyleCache.Key, StyleCache> styleCache = 235 new HashMap<StyleCache.Key, StyleCache>(); 236 237 Group group = new Group(); 238 group.getStyleClass().add("root"); 239 240 241 final ParsedValue[] fontValues = new ParsedValue[] { 242 new ParsedValueImpl<String,String>("system", null), 243 new ParsedValueImpl<ParsedValue<?,Size>,Number>( 244 new ParsedValueImpl<Size,Size>(new Size(1.5, SizeUnits.EM), null), 245 SizeConverter.getInstance() 246 ), 247 null, 248 null 249 }; 250 final List<Declaration> textDecls = new ArrayList<Declaration>(); 251 Collections.addAll(textDecls, 252 new Declaration("-fx-font", new ParsedValueImpl<ParsedValue[], Font>( 253 fontValues, FontConverter.getInstance()), false) 254 ); 255 256 final List<Selector> textSels = new ArrayList<Selector>(); 257 Collections.addAll(textSels, 258 Selector.createSelector(".text") 259 ); 260 261 Rule textRule = new Rule(textSels, textDecls); 262 stylesheet.getRules().add(textRule); 263 264 final List<CascadingStyle> styles = createStyleList(textDecls); 265 final Map<String,List<CascadingStyle>> styleMap = createStyleMap(styles); 266 final Map<String,List<CascadingStyle>> emptyMap = createStyleMap(null); 267 268 Text text = new Text("HelloWorld"); 269 group.getChildren().add(text); 270 271 final List<Declaration> expecteds = new ArrayList<Declaration>(); 272 expecteds.addAll(rootDecls); 273 expecteds.addAll(textDecls); 274 text.getStyleClass().add("text"); 275 text.impl_setStyleMap(FXCollections.observableMap(new HashMap<StyleableProperty<?>, List<Style>>())); 276 text.impl_getStyleMap().addListener(new MapChangeListener<StyleableProperty, List<Style>>() { 277 278 // a little different than the other tests since we should end up 279 // with font and font-size in the map and nothing else. After all 280 // the changes have been handled, the expecteds list should be empty. 281 public void onChanged(MapChangeListener.Change<? extends StyleableProperty, ? extends List<Style>> change) { 282 if (change.wasAdded()) { 283 List<Style> styles = change.getValueAdded(); 284 for (Style style : styles) { 285 assertTrue(expecteds.contains(style.getDeclaration())); 286 expecteds.remove(style.getDeclaration()); 287 } 288 } 289 } 290 }); 291 292 StyleManager.getInstance().setDefaultUserAgentStylesheet(stylesheet); 293 Scene scene = new Scene(group); 294 Stage stage = new Stage(); 295 stage.setScene(scene); 296 stage.show(); 297 298 assertEquals(18, text.getFont().getSize(),0); 299 assertTrue(Integer.toString(expecteds.size()), expecteds.isEmpty()); 300 301 } 302 303 boolean containsProperty(CssMetaData key, Map<String,List<CascadingStyle>> map) { 304 305 if (map.containsKey(key)) return true; 306 List<CssMetaData> subProperties = key.getSubProperties(); 307 if (subProperties != null && !subProperties.isEmpty()) { 308 for (CssMetaData subKey: subProperties) { 309 if (map.containsKey(subKey)) return true; 310 } 311 } 312 return false; 313 } 314 315 @Test 316 public void testRT_34799() { 317 318 Stylesheet stylesheet = new Stylesheet("testRT_34799"); 319 stylesheet.setOrigin(StyleOrigin.USER_AGENT); 320 321 final List<Declaration> txtDecls = new ArrayList<Declaration>(); 322 Collections.addAll(txtDecls, 323 new Declaration("-fx-fill", new ParsedValueImpl<Color,Color>(Color.RED, null), false) 324 ); 325 326 final List<Selector> textSels = new ArrayList<Selector>(); 327 Collections.addAll(textSels, 328 Selector.createSelector(".rt-34799") 329 ); 330 331 Rule txtRules = new Rule(textSels, txtDecls); 332 stylesheet.getRules().add(txtRules); 333 334 final List<Style> expectedStyles = new ArrayList<>(); 335 for (Rule rule : stylesheet.getRules()) { 336 for (Selector selector : rule.getSelectors()) { 337 for (Declaration declaration : rule.getUnobservedDeclarationList()) { 338 expectedStyles.add( 339 new Style(selector, declaration) 340 ); 341 } 342 } 343 } 344 345 Text text = new Text("HelloWorld"); 346 text.getStyleClass().add("rt-34799"); 347 348 Group group = new Group(); 349 group.getStyleClass().add("root"); 350 351 group.getChildren().add(text); 352 353 StyleManager.getInstance().setDefaultUserAgentStylesheet(stylesheet); 354 Scene scene = new Scene(group); 355 356 group.applyCss(); // TODO: force StyleHelper to be created, remove pending RT-34812 357 358 int nExpected = expectedStyles.size(); 359 assert(nExpected > 0); 360 361 for(CssMetaData cssMetaData : text.getCssMetaData()) { 362 List<Style> styles = Node.impl_getMatchingStyles(cssMetaData, text); 363 if (styles != null && !styles.isEmpty()) { 364 assertTrue(expectedStyles.containsAll(styles)); 365 assertTrue(styles.containsAll(expectedStyles)); 366 nExpected -= 1; 367 } 368 } 369 370 assertEquals(nExpected, 0); 371 372 } 373 374 }