1 /* 2 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package com.oracle.javafx.scenebuilder.kit.editor.panel.css; 33 34 import com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors.EditorUtils; 35 import com.oracle.javafx.scenebuilder.kit.metadata.util.ColorEncoder; 36 import com.oracle.javafx.scenebuilder.kit.util.Deprecation; 37 import com.oracle.javafx.scenebuilder.kit.util.MathUtils; 38 import javafx.css.Declaration; 39 import javafx.css.Rule; 40 import javafx.css.Size; 41 import javafx.css.converter.PaintConverter; 42 import javafx.css.converter.PaintConverter.LinearGradientConverter; 43 import javafx.css.converter.DeriveColorConverter; 44 import javafx.css.converter.DeriveSizeConverter; 45 import javafx.css.converter.LadderConverter; 46 import java.lang.reflect.Array; 47 import java.util.ArrayList; 48 import java.util.Collection; 49 import java.util.List; 50 import java.util.Locale; 51 import java.util.Map; 52 import javafx.css.ParsedValue; 53 import javafx.geometry.Insets; 54 import javafx.geometry.Side; 55 import javafx.scene.effect.Effect; 56 import javafx.scene.image.Image; 57 import javafx.scene.layout.Background; 58 import javafx.scene.layout.BackgroundFill; 59 import javafx.scene.layout.BackgroundImage; 60 import javafx.scene.layout.BackgroundSize; 61 import javafx.scene.layout.Border; 62 import javafx.scene.layout.BorderImage; 63 import javafx.scene.layout.BorderStroke; 64 import javafx.scene.layout.BorderWidths; 65 import javafx.scene.layout.CornerRadii; 66 import javafx.scene.paint.Color; 67 import javafx.scene.paint.Paint; 68 import javafx.scene.text.Font; 69 70 /** 71 * 72 * @treatAsPrivate 73 */ 74 public class CssValueConverter { 75 76 private static final List<String> STRING_VALUE = new ArrayList<>(); 77 private static final List<String> SINGLE_WHEN_EQUALITY = new ArrayList<>(); 78 79 private CssValueConverter() { 80 assert false; 81 } 82 83 @SuppressWarnings("rawtypes") 84 public static Object convert(ParsedValue pv) { 85 Object value = null; 86 if (pv == null) { 87 return null; 88 } 89 90 if (pv.getConverter() != null) { 91 try { 92 @SuppressWarnings("unchecked") 93 Object converted = pv.getConverter().convert(pv, null); 94 value = converted; 95 } catch (RuntimeException ex) { 96 // OK, can't be resolved, a lookup possibly 97 } 98 } else { 99 value = pv.getValue(); 100 } 101 if (value instanceof ParsedValue) { 102 value = convert((ParsedValue) value); 103 } 104 return value; 105 } 106 107 // Retrieve a CSS String from a value set thanks to CSS 108 public static String toCssString(String property, Rule rule, Object fxValue) { 109 try { 110 return getValue(property, rule, fxValue); 111 } catch (IllegalArgumentException ex) { 112 return getValue(property, null, fxValue); 113 } 114 } 115 116 public static String toCssString(String property, Object fxValue) { 117 return getValue(property, null, fxValue); 118 } 119 120 public static String toCssString(Object fxValue) { 121 return getValue(null, null, fxValue); 122 } 123 124 // Retrieve the value for a sub property. 125 public static Object getSubPropertyValue(String property, Object value) { 126 if (value instanceof Collection) { 127 Collection<?> values = (Collection<?>) value; 128 List<Object> subValues = new ArrayList<>(); 129 for (Object bf : values) { 130 subValues.add(getSubPropertyValue(property, bf)); 131 } 132 return subValues; 133 } else if (value != null && value.getClass().isArray()) { 134 Object newArray = Array.newInstance(value.getClass().getComponentType(), Array.getLength(value)); 135 for (int i = 0; i < Array.getLength(value); i++) { 136 Array.set(newArray, i, getSubPropertyValue(property, Array.get(value, i))); 137 } 138 return newArray; 139 140 // 141 // Background 142 // 143 } else if (value instanceof Background) { 144 Background background = (Background) value; 145 if (background.getFills() != null) { 146 return getSubPropertyValue(property, background.getFills()); 147 } else if (background.getImages() != null) { 148 return getSubPropertyValue(property, background.getImages()); 149 } 150 } else if (value instanceof BackgroundFill) { 151 return subBackgroundFill(property, (BackgroundFill) value); 152 } else if (value instanceof BackgroundImage) { 153 return subBackgroundImage(property, (BackgroundImage) value); 154 155 // 156 // Border 157 // 158 } else if (value instanceof Border) { 159 Border border = (Border) value; 160 if (border.getStrokes() != null) { 161 return getSubPropertyValue(property, border.getStrokes()); 162 } else if (border.getImages() != null) { 163 return getSubPropertyValue(property, border.getImages()); 164 } 165 } else if (value instanceof BorderStroke) { 166 return subBorderStroke(property, (BorderStroke) value); 167 } else if (value instanceof BorderImage) { 168 return subBorderImage(property, (BorderImage) value); 169 170 // 171 // Font 172 // 173 } else if (value instanceof Font) { 174 return subFont(property, (Font) value); 175 } 176 return getValue(property, null, value); 177 } 178 179 static { 180 STRING_VALUE.add("-fx-skin");//NOI18N 181 STRING_VALUE.add("-fx-shape");//NOI18N 182 } 183 184 private static String format(String property, String value) { 185 if (STRING_VALUE.contains(property)) { 186 return "\"" + value + "\"";//NOI18N 187 } else { 188 return value; 189 } 190 } 191 192 // FX to String value transformation entry point. 193 private static String getValue(String property, Rule r, Object eventValue) throws IllegalArgumentException { 194 if (r == null) { 195 return format(property, retrieveValue(property, eventValue)); 196 } 197 198 for (Declaration d : r.getDeclarations()) { 199 if (d.getProperty().equals(property)) { 200 if (property.equals("-fx-background-radius") || property.equals("-fx-border-radius")) { //NOI18N 201 return format(property, getRadiusCssString(property, d.getParsedValue())); 202 } else { 203 return format(property, getCssString(property, d.getParsedValue())); 204 } 205 } 206 } 207 throw new IllegalArgumentException("Can't compute a value");//NOI18N 208 } 209 210 static { 211 SINGLE_WHEN_EQUALITY.add("-fx-padding"); //NOI18N 212 SINGLE_WHEN_EQUALITY.add("-fx-background-radius"); //NOI18N 213 SINGLE_WHEN_EQUALITY.add("-fx-background-insets"); //NOI18N 214 SINGLE_WHEN_EQUALITY.add("-fx-border-color"); //NOI18N 215 SINGLE_WHEN_EQUALITY.add("-fx-border-radius"); //NOI18N 216 SINGLE_WHEN_EQUALITY.add("-fx-border-insets"); //NOI18N 217 SINGLE_WHEN_EQUALITY.add("-fx-border-image-insets"); //NOI18N 218 SINGLE_WHEN_EQUALITY.add("-fx-border-image-slice"); //NOI18N 219 SINGLE_WHEN_EQUALITY.add("-fx-border-image-width"); //NOI18N 220 221 } 222 223 private static boolean singleForEquality(String prop) { 224 return SINGLE_WHEN_EQUALITY.contains(prop); 225 } 226 227 // The difference between retrieveValue and getCssStringValue 228 // is that the ParsedValue is converted in the retrieveValue case, and is not converted in the 229 // getCssStringValue 230 // When converting, we loose the CSS textual format present in the CSS source, 231 // for instance the lookup information, or 'em' unit. 232 @SuppressWarnings("rawtypes") 233 private static String getCssString(String property, ParsedValue value) { 234 235 // TODO : this method should be rewritten in a cleaner way... 236 if (value == null) { 237 return "null"; //NOI18N 238 } 239 240 // I don't like that but this is needed to only have a conversion for gradient. 241 // The gradient.toString is much better than the ParsedValue.toString. 242 if (value.getConverter() instanceof LinearGradientConverter 243 || value.getConverter() instanceof PaintConverter.RadialGradientConverter) { 244 245 try { 246 @SuppressWarnings("unchecked")//NOI18N 247 Object converted = value.getConverter().convert(value, null); 248 249 return toCssString(converted); 250 } catch (RuntimeException ex) { 251 } 252 } 253 254 Object obj = value.getValue(); 255 if (obj instanceof ParsedValue) { 256 return getCssString(property, (ParsedValue) obj); 257 } 258 StringBuilder builder = new StringBuilder(); 259 boolean isDerive = value.getConverter() instanceof DeriveColorConverter || value.getConverter() instanceof DeriveSizeConverter; 260 boolean isLadder = value.getConverter() instanceof LadderConverter; 261 if (isDerive) { 262 builder.append("derive("); //NOI18N 263 } 264 if (isLadder) { 265 builder.append("ladder("); //NOI18N 266 } 267 if (obj instanceof ParsedValue[]) { 268 ParsedValue[] array = (ParsedValue[]) obj; 269 boolean isArrayValue = false; 270 if (array.length >= 1) { 271 ParsedValue pval = array[0]; 272 Object val = null; 273 if (pval != null) { 274 if (pval.getConverter() instanceof LinearGradientConverter 275 || pval.getConverter() instanceof PaintConverter.RadialGradientConverter) { 276 val = null; 277 } else { 278 val = pval.getValue(); 279 } 280 } 281 isArrayValue = val != null && val.getClass().isArray(); 282 } 283 boolean singleForEquality = singleForEquality(property) && !isArrayValue; 284 StringBuilder b = new StringBuilder(); 285 if (singleForEquality) { 286 String latest = null; 287 boolean areEquals = true; 288 List<String> values = new ArrayList<>(array.length); 289 for (ParsedValue v : array) { 290 String current = getCssString(property, v); 291 values.add(current); 292 areEquals &= (latest == null || current.equals(latest)); 293 latest = current; 294 } 295 if (areEquals) { 296 String val = values.get(0); 297 val = removeDotZeroPxPercent(val); 298 b.append(val); 299 } else { 300 for (int i = 0; i < values.size(); i++) { 301 b.append(values.get(i)); 302 if (i < array.length - 1) { 303 b.append(" "); //NOI18N 304 } 305 } 306 } 307 } else { 308 for (int i = 0; i < array.length; i++) { 309 ParsedValue v = array[i]; 310 String val = getCssString(property, v); 311 val = removeDotZeroPxPercent(val); 312 b.append(val); 313 if ((i < array.length - 1) && val.length() > 0) { 314 b.append(", "); //NOI18N 315 } 316 } 317 } 318 319 builder.append(b.toString()); 320 } else { 321 if (obj instanceof ParsedValue[][]) { 322 ParsedValue[][] arr = (ParsedValue[][]) obj; 323 for (int i = 0; i < arr.length; i++) { 324 String val = retrieveValue(property, arr[i]); 325 builder.append(val); 326 if ((i < arr.length - 1) && val.length() > 0) { 327 builder.append(", "); //NOI18N 328 } 329 } 330 } else { 331 builder.append(retrieveValue(property, obj)); 332 } 333 } 334 if (isDerive || isLadder) { 335 builder.append(")"); //NOI18N 336 } 337 return builder.toString(); 338 } 339 340 @SuppressWarnings("rawtypes") 341 private static String getRadiusCssString(String property, ParsedValue value) { 342 // TODO : Ideally should be included in the generic getCssString() method 343 344 // See http://www.w3.org/TR/css3-background/#the-border-radius 345 346 assert property.equals("-fx-background-radius") || property.equals("-fx-border-radius"); //NOI18N 347 StringBuilder sbAll = new StringBuilder(); 348 Object obj = value.getValue(); 349 if (!(obj instanceof ParsedValue[])) { 350 return null; 351 } 352 ParsedValue[] pvArray = (ParsedValue[]) obj; 353 int index = 0; 354 for (ParsedValue pvItem : pvArray) { 355 // We have a CornerRadii representation here: 356 // double dimension array: 357 // 1- horizontal/vertical radii 358 // 2- value per corner 359 // If all the values are identical, only a single value is used. 360 obj = pvItem.getValue(); 361 if (!(obj instanceof ParsedValue[][])) { 362 return null; 363 } 364 ParsedValue[][] pvArray2 = (ParsedValue[][]) obj; 365 StringBuilder sbCornerRadii = new StringBuilder(); 366 Size initSize = null; 367 boolean areEquals = true; 368 int index2 = 0; 369 for (ParsedValue[] pvArray1 : pvArray2) { 370 // horizontal or vertical list 371 for (ParsedValue pvItem2 : pvArray1) { 372 obj = pvItem2.getValue(); 373 if (!(obj instanceof Size)) { 374 return null; 375 } 376 Size size = (Size) obj; 377 sbCornerRadii.append(size).append(" "); //NOI18N 378 if (initSize == null) { 379 initSize = size; 380 } else if (!initSize.equals(size)) { 381 areEquals = false; 382 } 383 } 384 if (index2 != pvArray2.length - 1) { 385 // Separator between the horizontal / vertical lists 386 sbCornerRadii.append(" / "); //NOI18N 387 } 388 index2++; 389 } 390 if (areEquals) { 391 sbAll.append(initSize); 392 } else { 393 sbAll.append(sbCornerRadii.toString().trim()); 394 } 395 if (index != pvArray.length - 1) { 396 sbAll.append(", "); //NOI18N 397 } 398 index++; 399 } 400 return removeDotZeroPxPercent(sbAll.toString()); 401 } 402 403 private static String retrieveValue(String property, Object eventValue) { 404 if (eventValue instanceof ParsedValue) { 405 eventValue = convert((ParsedValue<?, ?>) eventValue); 406 } 407 408 if (eventValue == null) { 409 return "null"; //NOI18N 410 } 411 StringBuilder builder = new StringBuilder(); 412 if (eventValue instanceof List) { 413 List<?> values = (List<?>) eventValue; 414 int length = values.size(); 415 for (int i = 0; i < length; i++) { 416 String val = retrieveValue(property, values.get(i)); 417 builder.append(val); 418 if ((i < length - 1) && val.length() > 0) { 419 builder.append(", "); //NOI18N 420 } 421 } 422 } else if (eventValue.getClass().isArray()) { 423 int length = Array.getLength(eventValue); 424 for (int i = 0; i < length; i++) { 425 String val = retrieveValue(property, Array.get(eventValue, i)); 426 builder.append(val); 427 if ((i < length - 1) && val.length() > 0) { 428 builder.append(", "); //NOI18N 429 } 430 } 431 } else if (eventValue instanceof Background) { 432 Background background = (Background) eventValue; 433 if (background.getFills() != null) { 434 return retrieveValue(property, background.getFills()); 435 } else if (background.getImages() != null) { 436 return retrieveValue(property, background.getImages()); 437 } 438 } else if (eventValue instanceof Border) { 439 Border border = (Border) eventValue; 440 if (border.getStrokes() != null) { 441 return retrieveValue(property, border.getStrokes()); 442 } else if (border.getImages() != null) { 443 return retrieveValue(property, border.getImages()); 444 } 445 } else if (eventValue instanceof BackgroundFill) { 446 builder.append(backgroundFillToString(property, (BackgroundFill) eventValue)); 447 } else if (eventValue instanceof CornerRadii) { 448 builder.append(cornerRadiiToString(property, (CornerRadii) eventValue)); 449 } else if (eventValue instanceof BackgroundImage) { 450 builder.append(backgroundImageToString(property, (BackgroundImage) eventValue)); 451 } else if (eventValue instanceof BorderStroke) { 452 builder.append(borderStrokeToString(property, (BorderStroke) eventValue)); 453 } else if (eventValue instanceof BorderImage) { 454 builder.append(borderImageToString(property, (BorderImage) eventValue)); 455 } else if (eventValue instanceof Font) { 456 builder.append(fontToString(property, (Font) eventValue)); 457 } else if (eventValue instanceof Paint) { 458 builder.append(paintToString((Paint) eventValue).toLowerCase(Locale.ROOT)); 459 } else if (eventValue instanceof Insets) { 460 builder.append(insetsValue((Insets) eventValue)); 461 } else if (eventValue instanceof Effect) { 462 builder.append(effectValue((Effect) eventValue)); 463 } else { 464 String str = EditorUtils.valAsStr(eventValue); 465 if (str == null) { 466 str = "null"; //NOI18N 467 } else { 468 str = str.replaceAll("\n", " ");//NOI18N 469 // Remove memory address if any 470 str = str.split("@")[0]; //NOI18N 471 str = removeDotZeroPxPercent(str); 472 } 473 builder.append(str); 474 } 475 return builder.toString(); 476 } 477 478 private static String getColorAsWebString(Color c) { 479 int red = (int) Math.round(c.getRed() * 255.0); 480 int green = (int) Math.round(c.getGreen() * 255.0); 481 int blue = (int) Math.round(c.getBlue() * 255.0); 482 int alpha = (int) Math.round(c.getOpacity() * 255.0); 483 if (alpha == 255) { 484 return String.format("#%02x%02x%02x", red, green, blue); //NOI18N 485 } else { 486 return String.format("#%02x%02x%02x%02x", red, green, blue, alpha); //NOI18N 487 } 488 } 489 490 private static String getColorAsString(Color color) { 491 if (isStandardColor(color)) { 492 return getStandardColorAsString(color); 493 } else { 494 return getColorAsWebString(color); 495 } 496 } 497 498 private static boolean isStandardColor(Color c) { 499 return standardColors.containsKey(c); 500 } 501 static Map<Color, String> standardColors = ColorEncoder.getStandardColorNames(); 502 503 private static String getStandardColorAsString(Color c) { 504 return standardColors.get(c); 505 } 506 507 private static String backgroundFillToString(String property, BackgroundFill bf) { 508 if (property == null) { 509 return bf.toString(); 510 } 511 StringBuilder builder = new StringBuilder(); 512 if (property.equals("-fx-background-color")) { //NOI18N 513 Paint p = bf.getFill(); 514 builder.append(paintToString(p)); 515 } else { 516 if (property.equals("-fx-background-insets")) { //NOI18N 517 builder.append(insetsValue(bf.getInsets())); 518 } else { 519 //the top right, bottom right, bottom left, and top left 520 if (property.equals("-fx-background-radius")) { //NOI18N 521 handleCornerRadii(bf.getRadii(), builder); 522 } 523 } 524 } 525 return builder.toString(); 526 } 527 528 private static String cornerRadiiToString(String property, CornerRadii cr) { 529 if (property == null) { 530 return cr.toString(); 531 } 532 StringBuilder builder = new StringBuilder(); 533 handleCornerRadii(cr, builder); 534 return builder.toString(); 535 } 536 537 private static String backgroundImageToString(String property, BackgroundImage bi) { 538 if (property == null) { 539 return bi.toString(); 540 } 541 StringBuilder builder = new StringBuilder(); 542 if (property.equals("-fx-background-image")) { //NOI18N 543 Image p = bi.getImage(); 544 builder.append(Deprecation.getUrl(p)); 545 } else { 546 if (property.equals("-fx-background-position")) { //NOI18N 547 double left = 0, right = 0, top = 0, bottom = 0; 548 if (bi.getPosition().getHorizontalSide() == Side.LEFT) { 549 left = bi.getPosition().getHorizontalPosition(); 550 } else { 551 right = bi.getPosition().getHorizontalPosition(); 552 } 553 if (bi.getPosition().getVerticalSide() == Side.TOP) { 554 top = bi.getPosition().getVerticalPosition(); 555 } else { 556 bottom = bi.getPosition().getVerticalPosition(); 557 } 558 builder.append("left:"); //NOI18N 559 builder.append(EditorUtils.valAsStr(left)); 560 builder.append(" right:"); //NOI18N 561 builder.append(EditorUtils.valAsStr(right)); 562 builder.append(" top:"); //NOI18N 563 builder.append(EditorUtils.valAsStr(top)); 564 builder.append(" bottom:"); //NOI18N 565 builder.append(EditorUtils.valAsStr(bottom)); 566 } else { 567 if (property.equals("-fx-background-repeat")) { //NOI18N 568 if (bi.getRepeatX() != null) { 569 builder.append(bi.getRepeatX().toString()); 570 } else { 571 if (bi.getRepeatY() != null) { 572 builder.append(bi.getRepeatY().toString()); 573 } else { 574 builder.append("unknown repeat"); //NOI18N 575 } 576 } 577 } else { 578 if (property.equals("-fx-background-size")) { //NOI18N 579 BackgroundSize bs = bi.getSize(); 580 if (bs.isContain()) { 581 builder.append("contain"); //NOI18N 582 } else { 583 if (bs.isCover()) { 584 builder.append("cover"); //NOI18N 585 } else { 586 if (bs.getWidth() == BackgroundSize.AUTO) { 587 builder.append("width: auto"); //NOI18N 588 } else { 589 builder.append("width: ").append(EditorUtils.valAsStr(bs.getWidth())); //NOI18N 590 } 591 if (bs.getHeight() == BackgroundSize.AUTO) { 592 builder.append("height: auto"); //NOI18N 593 } else { 594 builder.append("height: ").append(EditorUtils.valAsStr(bs.getHeight())); //NOI18N 595 } 596 } 597 } 598 } 599 } 600 } 601 } 602 return builder.toString(); 603 } 604 605 private static String borderImageToString(String property, BorderImage bi) { 606 if (property == null) { 607 return bi.toString(); 608 } 609 StringBuilder builder = new StringBuilder(); 610 if (property.equals("-fx-border-image")) { //NOI18N 611 Image p = bi.getImage(); 612 builder.append(Deprecation.getUrl(p)); 613 } else { 614 if (property.equals("-fx-background-position")) { //NOI18N 615 616 } else { 617 if (property.equals("-fx-border-image-repeat")) { //NOI18N 618 if (bi.getRepeatX() != null) { 619 builder.append(bi.getRepeatX().toString()); 620 } else { 621 if (bi.getRepeatY() != null) { 622 builder.append(bi.getRepeatY().toString()); 623 } else { 624 builder.append("unknown repeat"); //NOI18N 625 } 626 } 627 } else { 628 if (property.equals("-fx-border-image-insets")) { //NOI18N 629 builder.append(insetsValue(bi.getInsets())); 630 } else { 631 if (property.equals("-fx-border-image-width")) { //NOI18N 632 BorderWidths bw = bi.getWidths(); 633 if (MathUtils.equals(bw.getTop(), bw.getBottom()) 634 && MathUtils.equals(bw.getLeft(), bw.getRight())) { 635 builder.append(EditorUtils.valAsStr(bw.getTop())); 636 } else { 637 builder.append(EditorUtils.valAsStr(bw.getTop())).append(" "). //NOI18N 638 append(EditorUtils.valAsStr(bw.getRight())).append(" "). //NOI18N 639 append(EditorUtils.valAsStr(bw.getBottom())).append(" "). //NOI18N 640 append(EditorUtils.valAsStr(bw.getLeft())); 641 } 642 } else { 643 if (property.equals("-fx-border-image-slice")) { //NOI18N 644 BorderWidths bw = bi.getSlices(); 645 if (MathUtils.equals(bw.getTop(), bw.getBottom()) 646 && MathUtils.equals(bw.getLeft(), bw.getRight())) { 647 builder.append(EditorUtils.valAsStr(bw.getTop())); 648 } else { 649 builder.append(EditorUtils.valAsStr(bw.getTop())).append(" "). //NOI18N 650 append(EditorUtils.valAsStr(bw.getRight())).append(" "). //NOI18N 651 append(EditorUtils.valAsStr(bw.getBottom())).append(" "). //NOI18N 652 append(EditorUtils.valAsStr(bw.getLeft())); 653 } 654 } 655 } 656 } 657 } 658 } 659 } 660 return builder.toString(); 661 } 662 663 private static String borderStrokeToString(String property, BorderStroke bs) { 664 if (property == null) { 665 return bs.toString(); 666 } 667 StringBuilder builder = new StringBuilder(); 668 //top, right, bottom, and left 669 if (property.equals("-fx-border-color")) { //NOI18N 670 if (bs.getTopStroke().equals(bs.getBottomStroke()) 671 && bs.getRightStroke().equals(bs.getBottomStroke()) 672 && bs.getLeftStroke().equals(bs.getBottomStroke())) { 673 builder.append(paintToString(bs.getBottomStroke())); 674 } else { 675 builder.append(paintToString(bs.getTopStroke())).append(" "); //NOI18N 676 builder.append(paintToString(bs.getRightStroke())).append(" "); //NOI18N 677 builder.append(paintToString(bs.getBottomStroke())).append(" "); //NOI18N 678 builder.append(paintToString(bs.getLeftStroke())); 679 } 680 } else { 681 if (property.equals("-fx-border-insets")) { //NOI18N 682 builder.append(insetsValue(bs.getInsets())); 683 } else { 684 //the top right, bottom right, bottom left, and top left 685 if (property.equals("-fx-border-radius")) { //NOI18N 686 handleCornerRadii(bs.getRadii(), builder); 687 } else { 688 if (property.equals("-fx-border-style")) { //NOI18N 689 builder.append(bs.getTopStyle().toString()).append(", "); //NOI18N 690 builder.append(bs.getRightStyle().toString()).append(", "); //NOI18N 691 builder.append(bs.getBottomStyle().toString()).append(", "); //NOI18N 692 builder.append(bs.getLeftStyle().toString()); 693 } else { 694 if (property.equals("-fx-border-width")) { //NOI18N 695 BorderWidths bw = bs.getWidths(); 696 if (MathUtils.equals(bw.getTop(), bw.getBottom()) 697 && MathUtils.equals(bw.getRight(), bw.getBottom()) 698 && MathUtils.equals(bw.getLeft(), bw.getBottom())) { 699 builder.append(EditorUtils.valAsStr(bw.getBottom())); 700 } else { 701 builder.append(EditorUtils.valAsStr(bw.getTop())).append(" "). //NOI18N 702 append(EditorUtils.valAsStr(bw.getRight())).append(" "). //NOI18N 703 append(EditorUtils.valAsStr(bw.getBottom())).append(" "). //NOI18N 704 append(EditorUtils.valAsStr(bw.getLeft())); 705 } 706 } 707 } 708 } 709 } 710 } 711 return builder.toString(); 712 } 713 714 private static String paintToString(Paint p) { 715 if (p instanceof Color) { 716 return getColorAsString((Color) p).toLowerCase(Locale.ROOT); 717 } else { 718 String gradient = p.toString(); 719 // Workaround for RT-22910 720 gradient = gradient.replaceAll("0x", "#");//NOI18N 721 gradient = removeDotZeroPxPercent(gradient); 722 return gradient; 723 } 724 } 725 726 private static String fontToString(String property, Font font) { 727 if (property == null) { 728 return removeAllDotZero(font.toString()); 729 } 730 StringBuilder builder = new StringBuilder(); 731 if (property.equals("-fx-font")) { //NOI18N 732 String size = EditorUtils.valAsStr(font.getSize()); //NOI18N 733 String previewStr = font.getFamily() + " " + size + "px" //NOI18N 734 + (!font.getName().equals(font.getFamily()) 735 && !"Regular".equals(font.getStyle()) //NOI18N 736 ? " (" + font.getStyle() + ")" : ""); //NOI18N 737 builder.append(previewStr); 738 } else { 739 if (property.equals("-fx-font-size")) { //NOI18N 740 double p = font.getSize(); 741 builder.append(EditorUtils.valAsStr(p)).append("px"); //NOI18N 742 743 } else { 744 if (property.equals("-fx-font-family")) { //NOI18N 745 builder.append(font.getFamily()); 746 } else { 747 if (property.equals("-fx-font-weight")) { //NOI18N 748 // There is no such property. 749 builder.append(removeAllDotZero(font.toString())); 750 } else { 751 if (property.equals("-fx-font-style")) { //NOI18N 752 builder.append(font.getStyle()); 753 } 754 } 755 } 756 } 757 } 758 return builder.toString(); 759 } 760 761 private static String insetsValue(Insets insets) { 762 if (MathUtils.equals(insets.getBottom(), insets.getLeft()) 763 && MathUtils.equals(insets.getRight(), insets.getLeft()) 764 && MathUtils.equals(insets.getTop(), insets.getLeft())) { 765 return EditorUtils.valAsStr(insets.getLeft()); 766 } else { 767 return EditorUtils.valAsStr(insets.getTop()) + " " + EditorUtils.valAsStr(insets.getRight()) //NOI18N 768 + " " + EditorUtils.valAsStr(insets.getBottom()) + " " + EditorUtils.valAsStr(insets.getLeft()); //NOI18N 769 } 770 } 771 772 private static String effectValue(Effect effect) { 773 StringBuilder strBuild = new StringBuilder(); 774 Effect adding = effect; 775 while (adding != null) { 776 strBuild.append(adding.getClass().getSimpleName()); 777 adding = getEffectInput(adding); 778 if (adding != null) { 779 strBuild.append(", "); //NOI18N 780 } 781 } 782 return strBuild.toString(); 783 } 784 785 private static Object subBackgroundFill(String property, BackgroundFill bf) { 786 if (property == null) { 787 return bf; 788 } 789 if (property.equals("-fx-background-color")) { //NOI18N 790 return bf.getFill(); 791 } else { 792 if (property.equals("-fx-background-insets")) { //NOI18N 793 return bf.getInsets(); 794 } else { 795 return backgroundFillToString(property, bf); 796 } 797 } 798 } 799 800 private static Object subBackgroundImage(String property, BackgroundImage bi) { 801 if (property == null) { 802 return bi; 803 } 804 if (property.equals("-fx-background-image")) { //NOI18N 805 return bi.getImage(); 806 } else { 807 return backgroundImageToString(property, bi); 808 } 809 } 810 811 private static Object subBorderImage(String property, BorderImage bi) { 812 if (property == null) { 813 return bi; 814 } 815 if (property.equals("-fx-border-image")) { //NOI18N 816 return bi.getImage(); 817 } else { 818 return borderImageToString(property, bi); 819 } 820 } 821 822 private static Object subBorderStroke(String property, BorderStroke bs) { 823 if (property == null) { 824 return bs; 825 } 826 //top, right, bottom, and left 827 if (property.equals("-fx-border-color")) { //NOI18N 828 if (bs.getTopStroke().equals(bs.getBottomStroke()) 829 && bs.getRightStroke().equals(bs.getBottomStroke()) 830 && bs.getLeftStroke().equals(bs.getBottomStroke())) { 831 return bs.getBottomStroke(); 832 } else { 833 Paint[] p = new Paint[4]; 834 p[0] = bs.getTopStroke(); 835 p[1] = bs.getRightStroke(); 836 p[2] = bs.getBottomStroke(); 837 p[3] = bs.getLeftStroke(); 838 return p; 839 } 840 } else { 841 if (property.equals("-fx-border-insets")) { //NOI18N 842 return bs.getInsets(); 843 } else { 844 return borderStrokeToString(property, bs); 845 } 846 } 847 } 848 849 private static Object subFont(String property, Font font) { 850 if (property == null) { 851 return font; 852 } 853 if (property.equals("-fx-font-size")) { //NOI18N 854 return EditorUtils.valAsStr(font.getSize()); 855 } else { 856 if (property.equals("-fx-font-style")) { //NOI18N 857 return font.getStyle(); 858 } else { 859 if (property.equals("-fx-font-family")) { //NOI18N 860 return font.getFamily(); 861 } else { 862 if (property.equals("-fx-font-weight")) { //NOI18N 863 // No font weight 864 return font.getFamily() + " " + font.getStyle(); //NOI18N 865 } else { 866 return font; 867 } 868 } 869 } 870 } 871 } 872 873 private static String removeDotZeroPxPercent(String str) { 874 // Remove ".0" in strings, for "px" and "%" notations 875 str = str.replaceAll("\\.0px", "px"); //NOI18N 876 str = str.replaceAll("\\.0em", "em"); //NOI18N 877 str = str.replaceAll("\\.0\\%", "%"); //NOI18N 878 return str; 879 } 880 881 private static String removeAllDotZero(String str) { 882 // Remove all ".0" in string 883 str = str.replaceAll("\\.0", ""); //NOI18N 884 return str; 885 } 886 887 private static void handleCornerRadii(CornerRadii cr, StringBuilder builder) { 888 // Each radius has a vertical and horizontal radius 889 // See http://www.w3.org/TR/css3-background/#the-border-radius 890 891 double topLeftH = cr.getTopLeftHorizontalRadius(); 892 double topLeftV = cr.getTopLeftVerticalRadius(); 893 double topRightH = cr.getTopRightHorizontalRadius(); 894 double topRightV = cr.getTopRightVerticalRadius(); 895 double bottomLeftH = cr.getBottomLeftHorizontalRadius(); 896 double bottomLeftV = cr.getBottomLeftVerticalRadius(); 897 double bottomRightH = cr.getBottomRightHorizontalRadius(); 898 double bottomRightV = cr.getBottomRightVerticalRadius(); 899 900 if (MathUtils.equals(topLeftH, topLeftV) && MathUtils.equals(topRightH, topRightV) 901 && MathUtils.equals(bottomLeftH, bottomLeftV) && MathUtils.equals(bottomRightH, bottomRightV)) { 902 if (MathUtils.equals(topLeftH, topRightH) && MathUtils.equals(topRightH, bottomLeftH) 903 && MathUtils.equals(bottomLeftH, bottomRightH)) { 904 // Same radius for all => single value 905 builder.append(EditorUtils.valAsStr(topLeftH)); 906 } else { 907 // Same value for vertical and horizontal radii 908 // => 4 values for topLeft, topRight, bottomLeft, bottomRight 909 builder.append(EditorUtils.valAsStr(topLeftH)).append(" "). //NOI18N 910 append(EditorUtils.valAsStr(topRightH)).append(" "). //NOI18N 911 append(EditorUtils.valAsStr(bottomRightH)).append(" "). //NOI18N 912 append(EditorUtils.valAsStr(bottomLeftH)); 913 } 914 } else { 915 // Separate value for each. 916 // Syntax: "horizontal values / vertical values" 917 builder.append(EditorUtils.valAsStr(topLeftH)).append(" "). //NOI18N 918 append(EditorUtils.valAsStr(topRightH)).append(" "). //NOI18N 919 append(EditorUtils.valAsStr(bottomRightH)).append(" "). //NOI18N 920 append(EditorUtils.valAsStr(bottomLeftH)). 921 append(" / ").//NOI18N 922 append(EditorUtils.valAsStr(topLeftV)).append(" "). //NOI18N 923 append(EditorUtils.valAsStr(topRightV)).append(" "). //NOI18N 924 append(EditorUtils.valAsStr(bottomRightV)).append(" "). //NOI18N 925 append(EditorUtils.valAsStr(bottomLeftV)); 926 } 927 } 928 929 // @SuppressWarnings({"BroadCatchBlock", "TooBroadCatch"}) //NOI18N 930 private static Effect getEffectInput(Effect effect) { 931 Effect found = null; 932 try { 933 found = (Effect) effect.getClass().getMethod("getInput").invoke(effect); //NOI18N 934 } catch (Throwable e) { 935 // DO NOT use multi-catch syntax here, this generates a FindBugs Warning (because of SecurityException catching) 936 // e.printStackTrace(); 937 try { 938 found = (Effect) effect.getClass().getMethod("getContentInput").invoke(effect); //NOI18N 939 } catch (Throwable ee) { 940 // DO NOT use multi-catch syntax here, this generates a FindBugs Warning (because of SecurityException catching) 941 // ee.printStackTrace(); 942 } 943 } 944 return found; 945 } 946 }