From b1a2b1e838a9d32c63a0ff39739c6bae47ca6cbf Mon Sep 17 00:00:00 2001 From: Pavel Dolgov Date: Wed, 2 Mar 2016 19:59:27 +0300 Subject: [PATCH] javafx: Inspection of FXML attributes and tags which contain default values and therefore are redundant (IDEA-102276) --- .../fxml/AbstractJavaFXQuickFixTest.java | 6 +- ...FxGenerateDefaultPropertyValuesScript.java | 533 ++++++++++++++ .../javaFX/fxml/JavaFxRedundantValueTest.java | 66 ++ .../JavaFxRedundantPropertyValue.html | 6 + .../src/META-INF/common-javaFX-plugin.xml | 2 + ...avaFxRedundantPropertyValueInspection.java | 239 +++++++ ...avaFxRedundantPropertyValueInspection8.txt | 677 ++++++++++++++++++ .../codeInsight/inspections/RemoveTagFix.java | 61 ++ .../redundantValue/attributeHighlighting.fxml | 11 + .../redundantValue/immediateAttribute.fxml | 8 + .../immediateAttribute_after.fxml | 7 + .../redundantValue/immediateTag.fxml | 12 + .../redundantValue/immediateTag_after.fxml | 11 + .../redundantValue/inheritedAttribute.fxml | 9 + .../inheritedAttribute_after.fxml | 8 + .../redundantValue/inheritedTag.fxml | 15 + .../redundantValue/inheritedTag_after.fxml | 14 + .../redundantValue/modifiedAttribute.fxml | 8 + .../redundantValue/modifiedTag.fxml | 12 + .../redundantValue/tagHighlighting.fxml | 19 + 20 files changed, 1723 insertions(+), 1 deletion(-) create mode 100644 plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFxGenerateDefaultPropertyValuesScript.java create mode 100644 plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFxRedundantValueTest.java create mode 100644 plugins/javaFX/resources/inspectionDescriptions/JavaFxRedundantPropertyValue.html create mode 100644 plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/inspections/JavaFxRedundantPropertyValueInspection.java create mode 100644 plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/inspections/JavaFxRedundantPropertyValueInspection8.txt create mode 100644 plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/inspections/RemoveTagFix.java create mode 100644 plugins/javaFX/testData/inspections/redundantValue/attributeHighlighting.fxml create mode 100644 plugins/javaFX/testData/inspections/redundantValue/immediateAttribute.fxml create mode 100644 plugins/javaFX/testData/inspections/redundantValue/immediateAttribute_after.fxml create mode 100644 plugins/javaFX/testData/inspections/redundantValue/immediateTag.fxml create mode 100644 plugins/javaFX/testData/inspections/redundantValue/immediateTag_after.fxml create mode 100644 plugins/javaFX/testData/inspections/redundantValue/inheritedAttribute.fxml create mode 100644 plugins/javaFX/testData/inspections/redundantValue/inheritedAttribute_after.fxml create mode 100644 plugins/javaFX/testData/inspections/redundantValue/inheritedTag.fxml create mode 100644 plugins/javaFX/testData/inspections/redundantValue/inheritedTag_after.fxml create mode 100644 plugins/javaFX/testData/inspections/redundantValue/modifiedAttribute.fxml create mode 100644 plugins/javaFX/testData/inspections/redundantValue/modifiedTag.fxml create mode 100644 plugins/javaFX/testData/inspections/redundantValue/tagHighlighting.fxml diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/AbstractJavaFXQuickFixTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/AbstractJavaFXQuickFixTest.java index feaaf1ec1336..88a468abc7d4 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/AbstractJavaFXQuickFixTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/AbstractJavaFXQuickFixTest.java @@ -16,6 +16,9 @@ package org.jetbrains.plugins.javaFX.fxml; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.testFramework.fixtures.CodeInsightTestUtil; + +import java.util.List; /** * User: anna @@ -34,7 +37,8 @@ public abstract class AbstractJavaFXQuickFixTest extends AbstractJavaFXTestCase protected void checkQuickFixNotAvailable(String tagName) throws Exception { myFixture.configureByFiles(getTestName(true) + ".fxml"); - final IntentionAction intention = myFixture.getAvailableIntention(getHint(tagName)); + final List intentions = myFixture.getAvailableIntentions(); + final IntentionAction intention = CodeInsightTestUtil.findIntentionByText(intentions, getHint(tagName)); assertNull(intention); } } diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFxGenerateDefaultPropertyValuesScript.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFxGenerateDefaultPropertyValuesScript.java new file mode 100644 index 000000000000..7a68e1756e5c --- /dev/null +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFxGenerateDefaultPropertyValuesScript.java @@ -0,0 +1,533 @@ +package org.jetbrains.plugins.javaFX.fxml; + +import javafx.application.Application; +import javafx.application.Platform; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.layout.StackPane; +import javafx.stage.Stage; +import org.jetbrains.annotations.NotNull; + +import java.beans.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.lang.reflect.*; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * This is not a test, this is a config generator for JavaFxRedundantPropertyValueInspection + *

+ * When launched without arguments it produces default values for JavaFX classes having default constructor and their superclasses, including some (but not all) abstract classes + *

+ * When launched with -fromSource argument it attempts to extract default property values from the sources (JavaDoc and declarations), + * the results can be used for updating the contents of {@link #ourFromSource} map, which contains manually edited properties + * + * @author Pavel.Dolgov + */ +public class JavaFxGenerateDefaultPropertyValuesScript extends Application { + public static final String BINARIES_PATH = "/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar"; + public static final String SOURCE_PATH = "/usr/lib/jvm/java-8-oracle/javafx-src.zip"; + + public static void main(String[] args) { + if (args.length == 1 && "-fromSource".equals(args[0])) { + scanSource(); + } + else { + launch(args); + } + } + + @Override + public void start(Stage primaryStage) throws Exception { + Button b = new Button(); + b.setText("Generate"); + b.setOnAction(event -> generate()); + + StackPane pane = new StackPane(); + pane.getChildren().add(b); + primaryStage.setScene(new Scene(pane, 300, 200)); + primaryStage.show(); + + Platform.runLater(() -> { + generate(); + primaryStage.close(); + }); + } + + private static final Map ourToBoxed = new HashMap<>(); + + static { + ourToBoxed.put(Boolean.TYPE, Boolean.class); + ourToBoxed.put(Character.TYPE, Character.class); + ourToBoxed.put(Byte.TYPE, Byte.class); + ourToBoxed.put(Short.TYPE, Short.class); + ourToBoxed.put(Integer.TYPE, Integer.class); + ourToBoxed.put(Long.TYPE, Long.class); + ourToBoxed.put(Float.TYPE, Float.class); + ourToBoxed.put(Double.TYPE, Double.class); + } + + private static final Map ourFromSource = new TreeMap<>(); + + static { + ourFromSource.put("javafx.concurrent.ScheduledService#maximumFailureCount", "Integer=2147483647"); + ourFromSource.put("javafx.concurrent.ScheduledService#restartOnFailure", "Boolean=true"); + ourFromSource.put("javafx.scene.Node#accessibleRole", "Enum=NODE"); + ourFromSource.put("javafx.scene.Node#focusTraversable", "Boolean=false"); + ourFromSource.put("javafx.scene.Node#nodeOrientation", "Enum=INHERIT"); + ourFromSource.put("javafx.scene.Node#pickOnBounds", "Boolean=false"); + ourFromSource.put("javafx.scene.SubScene#height", "Double=0.0"); + ourFromSource.put("javafx.scene.SubScene#width", "Double=0.0"); + ourFromSource.put("javafx.scene.chart.AreaChart#createSymbols", "Boolean=true"); + ourFromSource.put("javafx.scene.chart.Axis#label", "String="); + ourFromSource.put("javafx.scene.chart.BarChart#barGap", "Double=4.0"); + ourFromSource.put("javafx.scene.chart.BarChart#categoryGap", "Double=10.0"); + ourFromSource.put("javafx.scene.chart.Chart#title", "String="); + ourFromSource.put("javafx.scene.chart.LineChart#axisSortingPolicy", "Enum=X_AXIS"); + ourFromSource.put("javafx.scene.chart.LineChart#createSymbols", "Boolean=true"); + ourFromSource.put("javafx.scene.chart.StackedAreaChart#createSymbols", "Boolean=true"); + ourFromSource.put("javafx.scene.chart.StackedBarChart#categoryGap", "Double=10.0"); + ourFromSource.put("javafx.scene.chart.XYChart#alternativeColumnFillVisible", "Boolean=false"); + ourFromSource.put("javafx.scene.chart.XYChart#alternativeRowFillVisible", "Boolean=true"); + ourFromSource.put("javafx.scene.chart.XYChart#horizontalGridLinesVisible", "Boolean=true"); + ourFromSource.put("javafx.scene.chart.XYChart#horizontalZeroLineVisible", "Boolean=true"); + ourFromSource.put("javafx.scene.chart.XYChart#verticalGridLinesVisible", "Boolean=true"); + ourFromSource.put("javafx.scene.chart.XYChart#verticalZeroLineVisible", "Boolean=true"); + ourFromSource.put("javafx.scene.control.ComboBoxBase#editable", "Boolean=false"); + ourFromSource.put("javafx.scene.control.CustomMenuItem#hideOnClick", "Boolean=true"); + ourFromSource.put("javafx.scene.control.Labeled#alignment", "Enum=CENTER_LEFT"); + ourFromSource.put("javafx.scene.control.Labeled#mnemonicParsing", "Boolean=false"); + ourFromSource.put("javafx.scene.control.SpinnerValueFactory#wrapAround", "Boolean=false"); + ourFromSource.put("javafx.scene.control.TableSelectionModel#cellSelectionEnabled", "Boolean=false"); + ourFromSource.put("javafx.scene.media.AudioClip#balance", "Double=0.0"); + ourFromSource.put("javafx.scene.media.AudioClip#cycleCount", "Integer=1"); + ourFromSource.put("javafx.scene.media.AudioClip#pan", "Double=0.0"); + ourFromSource.put("javafx.scene.media.AudioClip#priority", "Integer=0"); + ourFromSource.put("javafx.scene.media.AudioClip#rate", "Double=1.0"); + ourFromSource.put("javafx.scene.media.AudioClip#volume", "Double=1.0"); + ourFromSource.put("javafx.scene.media.AudioEqualizer#enabled", "Boolean=false"); + ourFromSource.put("javafx.scene.media.MediaPlayer#audioSpectrumInterval", "Double=0.1"); + ourFromSource.put("javafx.scene.media.MediaPlayer#audioSpectrumNumBands", "Integer=128"); + ourFromSource.put("javafx.scene.media.MediaPlayer#audioSpectrumThreshold", "Integer=-60"); + ourFromSource.put("javafx.scene.media.MediaPlayer#autoPlay", "Boolean=false"); + ourFromSource.put("javafx.scene.media.MediaPlayer#balance", "Double=0.0"); + ourFromSource.put("javafx.scene.media.MediaPlayer#cycleCount", "Integer=1"); + ourFromSource.put("javafx.scene.media.MediaPlayer#mute", "Boolean=false"); + ourFromSource.put("javafx.scene.media.MediaPlayer#rate", "Double=1.0"); + ourFromSource.put("javafx.scene.media.MediaPlayer#volume", "Double=1.0"); + ourFromSource.put("javafx.stage.PopupWindow#anchorLocation", "Enum=WINDOW_TOP_LEFT"); + ourFromSource.put("javafx.stage.PopupWindow#autoHide", "Boolean=false"); + ourFromSource.put("javafx.stage.PopupWindow#consumeAutoHidingEvents", "Boolean=true"); + } + + private static final Set ourSkippedProperties = new HashSet<>( + Arrays.asList("javafx.scene.web.HTMLEditor#htmlText", + "javafx.scene.web.WebEngine#userAgent", + "javafx.scene.control.ButtonBar#buttonOrder")); + + static class TypedValue { + + private final String kind; + private final String value; + + TypedValue(@NotNull String kind, @NotNull String value) { + this.kind = kind; + this.value = value; + } + + public String getKind() { + return kind; + } + + public String getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TypedValue)) return false; + + TypedValue that = (TypedValue)o; + return kind.equals(that.kind) && value.equals(that.value); + } + + @Override + public int hashCode() { + return kind.hashCode() ^ value.hashCode(); + } + + @Override + public String toString() { + return kind + ':' + value; + } + } + + static class ContainedValue extends TypedValue { + private final String declaringClass; + + public ContainedValue(@NotNull String kind, @NotNull String value, @NotNull String declaringClass) { + super(kind, value); + this.declaringClass = declaringClass; + } + + public String getDeclaringClass() { + return declaringClass; + } + } + + /** + * Attempt to instantiate JavaFX classes on the FX thread, obtain default property values from instantiated objects. + */ + private static void generate() { + System.out.println("--- JavaFX default property values ---"); + + final Map> containedProperties = new TreeMap<>(); + final Map> declaredProperties = new TreeMap<>(); + final Map>> overriddenProperties = new TreeMap<>(); + final Map superClasses = new TreeMap<>(); + try (final ZipInputStream zip = new ZipInputStream(new FileInputStream(new File(BINARIES_PATH)))) { + for (ZipEntry ze = zip.getNextEntry(); ze != null; ze = zip.getNextEntry()) { + final String entryName = ze.getName(); + if (!ze.isDirectory() && entryName.endsWith(".class") && entryName.startsWith("javafx")) { + final String className = entryName.substring(0, entryName.lastIndexOf('.')).replace('/', '.'); + Class currentClass = Class.forName(className); + if (!Modifier.isPublic(currentClass.getModifiers()) && !Modifier.isProtected(currentClass.getModifiers())) continue; + if (currentClass.getName().startsWith("javafx.beans")) continue; + if (currentClass.getName().matches(".*\\$\\d+$")) continue; + BeanInfo info = Introspector.getBeanInfo(currentClass); + if (currentClass.getSuperclass() != null) { + superClasses.put(currentClass.getName(), currentClass.getSuperclass().getName()); + } + + Set bindings = new TreeSet<>(); + for (MethodDescriptor methodDesc : info.getMethodDescriptors()) { + if (methodDesc.getName().endsWith("Property") && + !methodDesc.getMethod().getGenericReturnType().toString().contains("ReadOnly")) { + bindings.add(methodDesc.getName()); + } + } + + Object obj = null; + for (PropertyDescriptor desc : info.getPropertyDescriptors()) { + final String propName = desc.getName(); + final String propQualifiedName = currentClass.getName() + "#" + propName; + if (ourSkippedProperties.contains(propQualifiedName)) continue; + final Method setter = desc.getWriteMethod(); + final boolean hasBinding = bindings.contains(propName + "Property"); + if (setter == null || !hasBinding) continue; + final Type type = setter.getGenericParameterTypes()[0]; + + if (type instanceof Class) { + final Class paramCls = (Class)type; + final String kind = kind(paramCls); + if (kind != null) { + if (obj == null) { + obj = instantiate(currentClass); + if (obj == null) break; + } + final Object value; + final Method getter = desc.getReadMethod(); + try { + value = getter.invoke(obj); + } + catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Can't invoke " + getter + " on " + currentClass, e); + } + if (value != null) { + if (!value.equals(validate(paramCls, value))) { + throw new RuntimeException("Invalid " + currentClass + "#" + propName + ":" + paramCls + "=" + value); + } + final Class declaringClass = getter.getDeclaringClass(); + final ContainedValue newValue = new ContainedValue(kind, String.valueOf(value), declaringClass.getName()); + if (declaringClass.getName().startsWith("javafx")) { + containedProperties + .computeIfAbsent(currentClass.getName(), unused -> new TreeMap<>()) + .put(propName, newValue); + } + + final Map shareableProperties = + declaredProperties.computeIfAbsent(declaringClass.getName(), unused -> new TreeMap<>()); + final TypedValue sharedValue = shareableProperties.get(propName); + + if (sharedValue == null) { + shareableProperties.put(propName, newValue); + } + else if (!sharedValue.equals(newValue)) { + final Set multipleValues = overriddenProperties + .computeIfAbsent(declaringClass.getName(), unused -> new TreeMap<>()) + .computeIfAbsent(propName, unused -> new TreeSet<>()); + multipleValues.add(sharedValue.getValue()); + multipleValues.add(newValue.getValue()); + } + } + } + } + } + } + } + } + catch (IOException | ClassNotFoundException | IntrospectionException e) { + e.printStackTrace(); + } + + System.out.println("-------- Collected from sources ---------"); + ourFromSource.forEach((k, v) -> System.out.println(k + ":" + v)); + + System.out.println("-------- Shared (not overridden) ---------"); + declaredProperties.forEach( + (className, propertyMap) -> { + final Map> multipleValues = overriddenProperties.getOrDefault(className, Collections.emptyMap()); + propertyMap.forEach((propName, typedValue) -> { + if (!multipleValues.containsKey(propName)) { + System.out.println(className + "#" + propName + ":" + typedValue.getKind() + "=" + typedValue.getValue()); + } + }); + }); + System.out.println("-------- Overridden in subclass ---------"); + final Map> fromSource = new TreeMap<>(); + ourFromSource.forEach((classAndPropName, kindAndValue) -> { + final int p1 = classAndPropName.indexOf('#'); + if (p1 > 0 && p1 < classAndPropName.length()) { + final String className = classAndPropName.substring(0, p1); + final String propName = classAndPropName.substring(p1 + 1); + final int p3 = kindAndValue.indexOf('='); + if (p3 > 0 && p3 < kindAndValue.length()) { + final String kind = kindAndValue.substring(0, p3); + final String value = kindAndValue.substring(p3 + 1); + final Map propMap = fromSource.computeIfAbsent(className, unused -> new TreeMap<>()); + if (!propMap.containsKey(propName)) { + final ContainedValue valueFromSource = new ContainedValue(kind, value, className); + propMap.put(propName, valueFromSource); + } + } + } + }); + containedProperties.forEach( + (className, propertyMap) -> propertyMap.forEach( + (propName, propValue) -> { + final ContainedValue sourceValue = fromSource.getOrDefault(className, Collections.emptyMap()).get(propName); + if (sourceValue != null && + sourceValue.equals(propValue)) { + return; + } + final Map> multipleValues = overriddenProperties.get(propValue.getDeclaringClass()); + if (multipleValues != null && multipleValues.get(propName) != null) { + boolean sameValueInSuperClass = false; + for (String scName = superClasses.get(className); scName != null; scName = superClasses.get(scName)) { + final Map superPropMap = containedProperties.getOrDefault(scName, fromSource.get(scName)); + if (superPropMap != null) { + ContainedValue superValue = superPropMap.get(propName); + if (superValue != null) { + sameValueInSuperClass = superValue.equals(propValue); + break; + } + } + } + if (!sameValueInSuperClass) { + System.out.println(className + "#" + propName + ":" + propValue.getKind() + "=" + propValue.getValue()); + } + } + })); + + System.out.println("-------- Overridden properties' values ---------"); + overriddenProperties + .forEach((className, propertyValues) -> propertyValues.entrySet() + .forEach(e -> System.out.println("-- " + className + "#" + e.getKey() + e.getValue()))); + System.out.println("-------- Skipped properties ---------"); + ourSkippedProperties.forEach(propName -> System.out.println("-- " + propName)); + } + + private static String kind(Class aCls) { + String kind = null; + if (aCls.isPrimitive()) { + kind = ourToBoxed.get(aCls).getSimpleName(); + } + else if (aCls.isEnum()) { + kind = "Enum"; + } + else if (Number.class.isAssignableFrom(aCls) || Boolean.class.isAssignableFrom(aCls) || Character.class.isAssignableFrom(aCls)) { + kind = aCls.getSimpleName(); + } + else if (CharSequence.class.isAssignableFrom(aCls)) { + kind = "String"; + } + return kind; + } + + private static Object validate(Class aCls, Object val) { + try { + if (aCls.isPrimitive()) { + aCls = ourToBoxed.get(aCls); + Method valueOf = aCls.getDeclaredMethod("valueOf", String.class); + return valueOf.invoke(null, String.valueOf(val)); + } + if (Number.class.isAssignableFrom(aCls) || Boolean.class.isAssignableFrom(aCls) || Character.class.isAssignableFrom(aCls)) { + Method valueOf = aCls.getDeclaredMethod("valueOf", String.class); + return valueOf.invoke(null, String.valueOf(val)); + } + if (CharSequence.class.isAssignableFrom(aCls)) { + return val; + } + if (aCls.isEnum()) { + Method valueOf = aCls.getDeclaredMethod("valueOf", String.class); + return valueOf.invoke(null, String.valueOf(val)); + } + throw new IllegalStateException("Cannot cast " + val + " to unsupported class " + aCls); + } + catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + throw new IllegalStateException("Cannot cast " + val + " to " + aCls, e); + } + } + + private static Object instantiate(Class aClass) { + try { + if (Modifier.isAbstract(aClass.getModifiers())) return null; + + final Constructor constructor; + try { + constructor = aClass.getConstructor(); + } + catch (NoSuchMethodException e) { + return null; + } + constructor.setAccessible(true); + return constructor.newInstance(); + } + catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + System.err.println("Not instantiated " + aClass + " " + e); + return null; + } + } + + private static final Set ourSourceClasses = new TreeSet<>(); + + static { + ourSourceClasses.add("javafx.animation.Animation"); + ourSourceClasses.add("javafx.concurrent.ScheduledService"); + ourSourceClasses.add("javafx.print.JobSettings"); + ourSourceClasses.add("javafx.scene.Camera"); + ourSourceClasses.add("javafx.scene.LightBase"); + ourSourceClasses.add("javafx.scene.Node"); + ourSourceClasses.add("javafx.scene.Scene"); + ourSourceClasses.add("javafx.scene.SubScene"); + ourSourceClasses.add("javafx.scene.canvas.GraphicsContext"); + ourSourceClasses.add("javafx.scene.chart.AreaChart"); + ourSourceClasses.add("javafx.scene.chart.Axis"); + ourSourceClasses.add("javafx.scene.chart.BarChart"); + ourSourceClasses.add("javafx.scene.chart.Chart"); + ourSourceClasses.add("javafx.scene.chart.LineChart"); + ourSourceClasses.add("javafx.scene.chart.PieChart"); + ourSourceClasses.add("javafx.scene.chart.StackedAreaChart"); + ourSourceClasses.add("javafx.scene.chart.StackedBarChart"); + ourSourceClasses.add("javafx.scene.chart.ValueAxis"); + ourSourceClasses.add("javafx.scene.chart.XYChart"); + ourSourceClasses.add("javafx.scene.control.Alert"); + ourSourceClasses.add("javafx.scene.control.ComboBoxBase"); + ourSourceClasses.add("javafx.scene.control.Labeled"); + ourSourceClasses.add("javafx.scene.control.MultipleSelectionModel"); + ourSourceClasses.add("javafx.scene.control.SpinnerValueFactory"); + ourSourceClasses.add("javafx.scene.control.SpinnerValueFactory"); + ourSourceClasses.add("javafx.scene.control.SpinnerValueFactory"); + ourSourceClasses.add("javafx.scene.control.TableColumnBase"); + ourSourceClasses.add("javafx.scene.control.TableSelectionModel"); + ourSourceClasses.add("javafx.scene.control.TextFormatter"); + ourSourceClasses.add("javafx.scene.control.TextInputControl"); + ourSourceClasses.add("javafx.scene.control.Toggle"); + ourSourceClasses.add("javafx.scene.input.DragEvent"); + ourSourceClasses.add("javafx.scene.input.Dragboard"); + ourSourceClasses.add("javafx.scene.input.MouseEvent"); + ourSourceClasses.add("javafx.scene.media.AudioClip"); + ourSourceClasses.add("javafx.scene.media.AudioEqualizer"); + ourSourceClasses.add("javafx.scene.media.MediaPlayer"); + ourSourceClasses.add("javafx.scene.shape.PathElement"); + ourSourceClasses.add("javafx.scene.shape.Shape"); + ourSourceClasses.add("javafx.scene.shape.Shape3D"); + ourSourceClasses.add("javafx.scene.web.WebHistory"); + ourSourceClasses.add("javafx.stage.PopupWindow"); + ourSourceClasses.add("javafx.stage.Window"); + } + + private static void scanSource() { + + Pattern defaultValueJavaDoc = Pattern.compile("^.*\\*\\s*@defaultValue\\s*(.+)\\s*$"); + Pattern fieldDecl = Pattern.compile("^.*\\s(\\w+)\\s*(=.*|;)\\s*$"); + Pattern methodDecl = Pattern.compile("^.*\\s(\\w+)\\s*\\(\\).*$"); + Pattern propertyDecl = Pattern.compile("^.*Property\\S*\\s+(\\w+)\\s*=\\s*(.+)[;{].*$"); + + Map props = new TreeMap<>(); + try (final ZipInputStream zip = new ZipInputStream(new FileInputStream(new File(SOURCE_PATH)))) { + byte[] buffer = new byte[1 << 16]; + for (ZipEntry ze = zip.getNextEntry(); ze != null; ze = zip.getNextEntry()) { + final String eName = ze.getName(); + if (!ze.isDirectory() && eName.endsWith(".java") && eName.startsWith("javafx")) { + String className = eName.substring(0, eName.lastIndexOf('.')).replace('/', '.'); + if (ourSourceClasses.contains(className)) { + StringBuilder text = new StringBuilder(); + int len; + while ((len = zip.read(buffer)) > 0) { + String str = new String(buffer, 0, len); + text.append(str); + } + String[] lines = text.toString().split("\n"); + int state = 0; + String name = null; + String value = null; + for (String s : lines) { + Matcher m; + m = propertyDecl.matcher(s); + if (m.matches()) { + name = m.group(1); + value = m.group(2); + if (!"null".equals(value)) { + props.put(className + "#" + name, value); + } + } + switch (state) { + case 0: + m = defaultValueJavaDoc.matcher(s); + if (m.matches()) { + state = 1; + value = m.group(1); + } + break; + case 1: + if (s.contains("*/")) state = 2; + break; + case 2: + if (!s.trim().isEmpty()) { + state = 0; + m = fieldDecl.matcher(s); + if (m.matches()) { + name = m.group(1); + } + else { + m = methodDecl.matcher(s); + if (m.matches()) { + name = m.group(1); + } + } + if (name != null && value != null && !"null".equals(value)) { + props.put(className + "#" + name, value); + } + name = value = null; + } + break; + } + } + } + } + } + } + catch (IOException e) { + System.err.println("Failed to read sources " + e); + } + System.out.println("--- Default values collected from JavaDoc and declarations. To be reviewed and manually edited ---"); + props.forEach((n, v) -> System.out.println(n + "=" + v)); + } +} diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFxRedundantValueTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFxRedundantValueTest.java new file mode 100644 index 000000000000..aab4ee8d5d7f --- /dev/null +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFxRedundantValueTest.java @@ -0,0 +1,66 @@ +package org.jetbrains.plugins.javaFX.fxml; + +import com.intellij.openapi.application.PluginPathManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.javaFX.fxml.codeInsight.inspections.JavaFxRedundantPropertyValueInspection; + +/** + * @author Pavel.Dolgov + */ +public class JavaFxRedundantValueTest extends AbstractJavaFXQuickFixTest { + @Override + protected void enableInspections() { + myFixture.enableInspections(new JavaFxRedundantPropertyValueInspection()); + } + + public void testModifiedAttribute() throws Exception { + checkQuickFixNotAvailable("alignment"); + } + + public void testImmediateAttribute() throws Exception { + doLaunchQuickfixTest("alignment"); + } + + public void testInheritedAttribute() throws Exception { + doLaunchQuickfixTest("maxHeight"); + } + + public void testModifiedTag() throws Exception { + checkQuickFixNotAvailable("alignment"); + } + + public void testImmediateTag() throws Exception { + doLaunchQuickfixTest("alignment"); + } + + public void testInheritedTag() throws Exception { + doLaunchQuickfixTest("maxHeight"); + } + + public void testAttributeHighlighting() throws Exception { + doTestHighlighting(); + } + + public void testTagHighlighting() throws Exception { + doTestHighlighting(); + } + + private void doTestHighlighting() { + myFixture.configureByFiles(getTestName(true) + ".fxml"); + myFixture.checkHighlighting(); + } + + @NotNull + @Override + protected String getTestDataPath() { + return PluginPathManager.getPluginHomePath("javaFX") + "/testData/inspections/redundantValue/"; + } + + @Override + protected String getHint(String tagName) { + if (getTestName(false).endsWith("Attribute")) { + return "Remove attribute " + tagName; + } + return "Remove tag '" + tagName + "'"; + } +} diff --git a/plugins/javaFX/resources/inspectionDescriptions/JavaFxRedundantPropertyValue.html b/plugins/javaFX/resources/inspectionDescriptions/JavaFxRedundantPropertyValue.html new file mode 100644 index 000000000000..c03a1326c547 --- /dev/null +++ b/plugins/javaFX/resources/inspectionDescriptions/JavaFxRedundantPropertyValue.html @@ -0,0 +1,6 @@ + + +This inspection reports properties in .fxml files (both attributes and tags) having default values and therefore redundant. + + + \ No newline at end of file diff --git a/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml b/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml index 84ca5e512a1d..ca16653de0a9 100644 --- a/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml +++ b/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml @@ -22,6 +22,8 @@ enabledByDefault="true" level="WARNING" groupName="JavaFX"/> + diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/inspections/JavaFxRedundantPropertyValueInspection.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/inspections/JavaFxRedundantPropertyValueInspection.java new file mode 100644 index 000000000000..fbd642fa8d74 --- /dev/null +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/inspections/JavaFxRedundantPropertyValueInspection.java @@ -0,0 +1,239 @@ +package org.jetbrains.plugins.javaFX.fxml.codeInsight.inspections; + +import com.intellij.codeInsight.daemon.impl.analysis.RemoveAttributeIntentionFix; +import com.intellij.codeInspection.*; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.psi.*; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.reference.SoftReference; +import com.intellij.xml.XmlAttributeDescriptor; +import com.intellij.xml.XmlElementDescriptor; +import gnu.trove.THashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.javaFX.fxml.FxmlConstants; +import org.jetbrains.plugins.javaFX.fxml.JavaFxFileTypeFactory; +import org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxClassBackedElementDescriptor; +import org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxPropertyAttributeDescriptor; +import org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxPropertyElementDescriptor; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.ref.Reference; +import java.net.URL; +import java.util.LinkedHashSet; +import java.util.Map; + +/** + * @author Pavel.Dolgov + */ +public class JavaFxRedundantPropertyValueInspection extends XmlSuppressableInspectionTool { + private static final Logger LOG = Logger.getInstance("#" + JavaFxRedundantPropertyValueInspection.class.getName()); + + private static Reference>> ourDefaultPropertyValues; + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + return new XmlElementVisitor() { + @Override + public void visitXmlFile(XmlFile file) { + if (!JavaFxFileTypeFactory.isFxml(file)) return; + super.visitXmlFile(file); + } + + @Override + public void visitXmlAttribute(XmlAttribute attribute) { + super.visitXmlAttribute(attribute); + final XmlAttributeDescriptor descriptor = attribute.getDescriptor(); + if (!(descriptor instanceof JavaFxPropertyAttributeDescriptor)) return; + final String attributeName = attribute.getName(); + final String attributeValue = attribute.getValue(); + if (attributeValue == null || + attributeValue.startsWith("$") || + attributeValue.startsWith("#") || + attributeValue.startsWith("%") || + FxmlConstants.FX_ID.equals(attributeName) || + FxmlConstants.FX_CONTROLLER.equals(attributeName)) { + return; + } + + final Object defaultValue = getDefaultValue(attributeName, attribute.getParent()); + if (defaultValue == null) return; + + if (isEqualValue(attributeValue, defaultValue)) { + holder.registerProblem(attribute, "Attribute is redundant because it contains default value", + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + new RemoveAttributeIntentionFix(attributeName, attribute)); + } + } + + @Override + public void visitXmlTag(XmlTag tag) { + super.visitXmlTag(tag); + final XmlElementDescriptor descriptor = tag.getDescriptor(); + if (!(descriptor instanceof JavaFxPropertyElementDescriptor) && + !(descriptor instanceof JavaFxClassBackedElementDescriptor)) { + return; + } + if (tag.getSubTags().length != 0) return; + final String tagText = tag.getValue().getText().trim(); + if (tagText.startsWith("$") || + tagText.startsWith("#") || + tagText.startsWith("%")) { + return; + } + + final Object defaultValue = getDefaultValue(tag.getName(), tag.getParentTag()); + if (defaultValue == null) return; + + if (isEqualValue(tagText, defaultValue)) { + holder.registerProblem(tag, "Tag is redundant because it contains default value", + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + new RemoveTagFix(tag.getName())); + } + } + }; + } + + @Nullable + private static Object getDefaultValue(@NotNull String propertyName, @Nullable XmlTag enclosingTag) { + if (enclosingTag != null) { + final XmlElementDescriptor descriptor = enclosingTag.getDescriptor(); + if (descriptor != null) { + final PsiElement declaration = descriptor.getDeclaration(); + if (declaration instanceof PsiClass) { + PsiClass containingClass = ((PsiClass)declaration); + { + final Object defaultValue = getDefaultPropertyValue(containingClass.getQualifiedName(), propertyName); + if (defaultValue != null) return defaultValue; + } + final LinkedHashSet superClasses = InheritanceUtil.getSuperClasses(containingClass); + for (PsiClass superClass : superClasses) { + final Object defaultValue = getDefaultPropertyValue(superClass.getQualifiedName(), propertyName); + if (defaultValue != null) return defaultValue; + } + } + } + } + return null; + } + + private static boolean isEqualValue(@NotNull String attributeValue, @NotNull Object defaultValue) { + if (defaultValue instanceof String && defaultValue.equals(attributeValue)) return true; + if (defaultValue instanceof Boolean) return defaultValue == Boolean.valueOf(attributeValue); + if (defaultValue instanceof Double) { + try { + return Double.compare((Double)defaultValue, Double.valueOf(attributeValue)) == 0; + } + catch (NumberFormatException ignored) { + return false; + } + } + if (defaultValue instanceof Integer) { + try { + return Integer.compare((Integer)defaultValue, Integer.valueOf(attributeValue)) == 0; + } + catch (NumberFormatException ignored) { + return false; + } + } + return false; + } + + private static Object getDefaultPropertyValue(String classQualifiedName, String propertyName) { + final Map values = getDefaultPropertyValues(classQualifiedName); + return values != null ? values.get(propertyName) : null; + } + + /** + * Load property values config. The config is produced with the script JavaFxGenerateDefaultPropertyValuesScript (can be found in tests) + */ + private static Map getDefaultPropertyValues(String classQualifiedName) { + if (ourDefaultPropertyValues == null) { + ourDefaultPropertyValues = loadDefaultPropertyValues(JavaFxRedundantPropertyValueInspection.class.getSimpleName() + "8.txt"); + } + Map> values = SoftReference.dereference(ourDefaultPropertyValues); + return values != null ? values.get(classQualifiedName) : null; + } + + /** + * The file format is ClassName#propertyName:type=value per line, line with leading double dash (--) is commented out + */ + private static Reference>> loadDefaultPropertyValues(String resourceName) { + final URL resource = JavaFxRedundantPropertyValueInspection.class.getResource(resourceName); + if (resource == null) { + LOG.warn("Resource not found: " + resourceName); + return null; + } + + final Map> result = new THashMap<>(200); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream(), CharsetToolkit.UTF8_CHARSET))) { + for (String line : FileUtil.loadLines(reader)) { + if (line.isEmpty() || line.startsWith("--")) continue; + boolean lineParsed = false; + final int p1 = line.indexOf('#'); + if (p1 > 0 && p1 < line.length()) { + final String className = line.substring(0, p1); + final int p2 = line.indexOf(':', p1); + if (p2 > p1 && p2 < line.length()) { + final String propertyName = line.substring(p1 + 1, p2); + final int p3 = line.indexOf('=', p2 + 1); + if (p3 > 0 && p3 < line.length()) { + final String type = line.substring(p2 + 1, p3); + final String text = line.substring(p3 + 1); + final Object value = parseValue(type, text); + if (value != null) { + lineParsed = true; + final Map properties = result.computeIfAbsent(className, ignored -> new THashMap()); + if (properties.put(propertyName, value) != null) { + LOG.warn("Duplicate default property value " + line); + } + } + } + } + } + if (!lineParsed) { + LOG.warn("Can't parse default property value " + line); + } + } + } + catch (IOException e) { + LOG.warn("Cannot read resource: " + resourceName, e); + return null; + } + + return new SoftReference>>(result); + } + + @Nullable + private static Object parseValue(String type, String text) { + try { + switch (type) { + case "Boolean": + return Boolean.valueOf(text); + case "Integer": + return Integer.valueOf(text); + case "Double": + return Double.valueOf(text); + case "String": + case "Enum": + return text; + default: + LOG.warn("Unsupported value type " + type + " for '" + text + "'"); + return null; + } + } + catch (NumberFormatException ignored) { + LOG.warn("Invalid format of " + type + ": '" + text + "'"); + return null; + } + } +} + diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/inspections/JavaFxRedundantPropertyValueInspection8.txt b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/inspections/JavaFxRedundantPropertyValueInspection8.txt new file mode 100644 index 000000000000..5fae741200b7 --- /dev/null +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/inspections/JavaFxRedundantPropertyValueInspection8.txt @@ -0,0 +1,677 @@ +--- JavaFX default property values --- +-------- Collected from sources --------- +javafx.concurrent.ScheduledService#maximumFailureCount:Integer=2147483647 +javafx.concurrent.ScheduledService#restartOnFailure:Boolean=true +javafx.scene.Node#accessibleRole:Enum=NODE +javafx.scene.Node#focusTraversable:Boolean=false +javafx.scene.Node#nodeOrientation:Enum=INHERIT +javafx.scene.Node#pickOnBounds:Boolean=false +javafx.scene.SubScene#height:Double=0.0 +javafx.scene.SubScene#width:Double=0.0 +javafx.scene.chart.AreaChart#createSymbols:Boolean=true +javafx.scene.chart.Axis#label:String= +javafx.scene.chart.BarChart#barGap:Double=4.0 +javafx.scene.chart.BarChart#categoryGap:Double=10.0 +javafx.scene.chart.Chart#title:String= +javafx.scene.chart.LineChart#axisSortingPolicy:Enum=X_AXIS +javafx.scene.chart.LineChart#createSymbols:Boolean=true +javafx.scene.chart.StackedAreaChart#createSymbols:Boolean=true +javafx.scene.chart.StackedBarChart#categoryGap:Double=10.0 +javafx.scene.chart.XYChart#alternativeColumnFillVisible:Boolean=false +javafx.scene.chart.XYChart#alternativeRowFillVisible:Boolean=true +javafx.scene.chart.XYChart#horizontalGridLinesVisible:Boolean=true +javafx.scene.chart.XYChart#horizontalZeroLineVisible:Boolean=true +javafx.scene.chart.XYChart#verticalGridLinesVisible:Boolean=true +javafx.scene.chart.XYChart#verticalZeroLineVisible:Boolean=true +javafx.scene.control.ComboBoxBase#editable:Boolean=false +javafx.scene.control.CustomMenuItem#hideOnClick:Boolean=true +javafx.scene.control.Labeled#alignment:Enum=CENTER_LEFT +javafx.scene.control.Labeled#mnemonicParsing:Boolean=false +javafx.scene.control.SpinnerValueFactory#wrapAround:Boolean=false +javafx.scene.control.TableSelectionModel#cellSelectionEnabled:Boolean=false +javafx.scene.media.AudioClip#balance:Double=0.0 +javafx.scene.media.AudioClip#cycleCount:Integer=1 +javafx.scene.media.AudioClip#pan:Double=0.0 +javafx.scene.media.AudioClip#priority:Integer=0 +javafx.scene.media.AudioClip#rate:Double=1.0 +javafx.scene.media.AudioClip#volume:Double=1.0 +javafx.scene.media.AudioEqualizer#enabled:Boolean=false +javafx.scene.media.MediaPlayer#audioSpectrumInterval:Double=0.1 +javafx.scene.media.MediaPlayer#audioSpectrumNumBands:Integer=128 +javafx.scene.media.MediaPlayer#audioSpectrumThreshold:Integer=-60 +javafx.scene.media.MediaPlayer#autoPlay:Boolean=false +javafx.scene.media.MediaPlayer#balance:Double=0.0 +javafx.scene.media.MediaPlayer#cycleCount:Integer=1 +javafx.scene.media.MediaPlayer#mute:Boolean=false +javafx.scene.media.MediaPlayer#rate:Double=1.0 +javafx.scene.media.MediaPlayer#volume:Double=1.0 +javafx.stage.PopupWindow#anchorLocation:Enum=WINDOW_TOP_LEFT +javafx.stage.PopupWindow#autoHide:Boolean=false +javafx.stage.PopupWindow#consumeAutoHidingEvents:Boolean=true +-------- Shared (not overridden) --------- +javafx.animation.Animation#autoReverse:Boolean=false +javafx.animation.Animation#cycleCount:Integer=1 +javafx.animation.Animation#rate:Double=1.0 +javafx.animation.FadeTransition#byValue:Double=0.0 +javafx.animation.FadeTransition#fromValue:Double=NaN +javafx.animation.FadeTransition#toValue:Double=NaN +javafx.animation.PathTransition#orientation:Enum=NONE +javafx.animation.RotateTransition#byAngle:Double=0.0 +javafx.animation.RotateTransition#fromAngle:Double=NaN +javafx.animation.RotateTransition#toAngle:Double=NaN +javafx.animation.ScaleTransition#byX:Double=0.0 +javafx.animation.ScaleTransition#byY:Double=0.0 +javafx.animation.ScaleTransition#byZ:Double=0.0 +javafx.animation.ScaleTransition#fromX:Double=NaN +javafx.animation.ScaleTransition#fromY:Double=NaN +javafx.animation.ScaleTransition#fromZ:Double=NaN +javafx.animation.ScaleTransition#toX:Double=NaN +javafx.animation.ScaleTransition#toY:Double=NaN +javafx.animation.ScaleTransition#toZ:Double=NaN +javafx.animation.TranslateTransition#byX:Double=0.0 +javafx.animation.TranslateTransition#byY:Double=0.0 +javafx.animation.TranslateTransition#byZ:Double=0.0 +javafx.animation.TranslateTransition#fromX:Double=NaN +javafx.animation.TranslateTransition#fromY:Double=NaN +javafx.animation.TranslateTransition#fromZ:Double=NaN +javafx.animation.TranslateTransition#toX:Double=NaN +javafx.animation.TranslateTransition#toY:Double=NaN +javafx.animation.TranslateTransition#toZ:Double=NaN +javafx.scene.Camera#farClip:Double=100.0 +javafx.scene.Camera#nearClip:Double=0.1 +javafx.scene.Group#autoSizeChildren:Boolean=true +javafx.scene.LightBase#lightOn:Boolean=true +javafx.scene.Node#cache:Boolean=false +javafx.scene.Node#cacheHint:Enum=DEFAULT +javafx.scene.Node#depthTest:Enum=INHERIT +javafx.scene.Node#disable:Boolean=false +javafx.scene.Node#layoutX:Double=0.0 +javafx.scene.Node#layoutY:Double=0.0 +javafx.scene.Node#managed:Boolean=true +javafx.scene.Node#mouseTransparent:Boolean=false +javafx.scene.Node#opacity:Double=1.0 +javafx.scene.Node#rotate:Double=0.0 +javafx.scene.Node#scaleX:Double=1.0 +javafx.scene.Node#scaleY:Double=1.0 +javafx.scene.Node#scaleZ:Double=1.0 +javafx.scene.Node#style:String= +javafx.scene.Node#translateX:Double=0.0 +javafx.scene.Node#translateY:Double=0.0 +javafx.scene.Node#translateZ:Double=0.0 +javafx.scene.Node#visible:Boolean=true +javafx.scene.PerspectiveCamera#fieldOfView:Double=30.0 +javafx.scene.PerspectiveCamera#verticalFieldOfView:Boolean=true +javafx.scene.canvas.Canvas#height:Double=0.0 +javafx.scene.canvas.Canvas#width:Double=0.0 +javafx.scene.chart.Axis#animated:Boolean=true +javafx.scene.chart.Axis#autoRanging:Boolean=true +javafx.scene.chart.Axis#tickLabelGap:Double=3.0 +javafx.scene.chart.Axis#tickLabelRotation:Double=0.0 +javafx.scene.chart.Axis#tickLabelsVisible:Boolean=true +javafx.scene.chart.Axis#tickLength:Double=8.0 +javafx.scene.chart.Axis#tickMarkVisible:Boolean=true +javafx.scene.chart.Axis$TickMark#position:Double=0.0 +javafx.scene.chart.CategoryAxis#endMargin:Double=5.0 +javafx.scene.chart.CategoryAxis#gapStartAndEnd:Boolean=true +javafx.scene.chart.CategoryAxis#startMargin:Double=5.0 +javafx.scene.chart.Chart#animated:Boolean=true +javafx.scene.chart.Chart#legendSide:Enum=BOTTOM +javafx.scene.chart.Chart#legendVisible:Boolean=true +javafx.scene.chart.Chart#titleSide:Enum=TOP +javafx.scene.chart.NumberAxis#forceZeroInRange:Boolean=true +javafx.scene.chart.NumberAxis#tickUnit:Double=5.0 +javafx.scene.chart.PieChart#clockwise:Boolean=true +javafx.scene.chart.PieChart#labelLineLength:Double=20.0 +javafx.scene.chart.PieChart#labelsVisible:Boolean=true +javafx.scene.chart.PieChart#startAngle:Double=0.0 +javafx.scene.chart.ValueAxis#lowerBound:Double=0.0 +javafx.scene.chart.ValueAxis#minorTickCount:Integer=5 +javafx.scene.chart.ValueAxis#minorTickLength:Double=5.0 +javafx.scene.chart.ValueAxis#minorTickVisible:Boolean=true +javafx.scene.chart.ValueAxis#upperBound:Double=100.0 +javafx.scene.control.Button#cancelButton:Boolean=false +javafx.scene.control.Button#defaultButton:Boolean=false +javafx.scene.control.ButtonBar#buttonMinWidth:Double=85.0 +javafx.scene.control.Cell#editable:Boolean=true +javafx.scene.control.CheckBox#allowIndeterminate:Boolean=false +javafx.scene.control.CheckBox#indeterminate:Boolean=false +javafx.scene.control.CheckBox#selected:Boolean=false +javafx.scene.control.CheckBoxTreeItem#independent:Boolean=false +javafx.scene.control.CheckBoxTreeItem#indeterminate:Boolean=false +javafx.scene.control.CheckBoxTreeItem#selected:Boolean=false +javafx.scene.control.CheckMenuItem#selected:Boolean=false +javafx.scene.control.ComboBox#visibleRowCount:Integer=10 +javafx.scene.control.ComboBoxBase#promptText:String= +javafx.scene.control.ContextMenu#impl_showRelativeToWindow:Boolean=false +javafx.scene.control.DatePicker#showWeekNumbers:Boolean=false +javafx.scene.control.Dialog#headerText:String=Confirmation +javafx.scene.control.Dialog#resizable:Boolean=false +javafx.scene.control.Dialog#title:String=Confirmation +javafx.scene.control.DialogPane#expanded:Boolean=false +javafx.scene.control.Hyperlink#visited:Boolean=false +javafx.scene.control.Labeled#contentDisplay:Enum=LEFT +javafx.scene.control.Labeled#ellipsisString:String=... +javafx.scene.control.Labeled#graphicTextGap:Double=4.0 +javafx.scene.control.Labeled#lineSpacing:Double=0.0 +javafx.scene.control.Labeled#text:String= +javafx.scene.control.Labeled#textAlignment:Enum=LEFT +javafx.scene.control.Labeled#textOverrun:Enum=ELLIPSIS +javafx.scene.control.Labeled#underline:Boolean=false +javafx.scene.control.Labeled#wrapText:Boolean=false +javafx.scene.control.ListView#editable:Boolean=false +javafx.scene.control.ListView#fixedCellSize:Double=-1.0 +javafx.scene.control.ListView#orientation:Enum=VERTICAL +javafx.scene.control.MenuBar#useSystemMenuBar:Boolean=false +javafx.scene.control.MenuButton#popupSide:Enum=BOTTOM +javafx.scene.control.MenuItem#disable:Boolean=false +javafx.scene.control.MenuItem#mnemonicParsing:Boolean=true +javafx.scene.control.MenuItem#text:String= +javafx.scene.control.MenuItem#visible:Boolean=true +javafx.scene.control.Pagination#currentPageIndex:Integer=0 +javafx.scene.control.Pagination#maxPageIndicatorCount:Integer=10 +javafx.scene.control.Pagination#pageCount:Integer=2147483647 +javafx.scene.control.PopupControl#maxHeight:Double=-1.0 +javafx.scene.control.PopupControl#maxWidth:Double=-1.0 +javafx.scene.control.PopupControl#minHeight:Double=-1.0 +javafx.scene.control.PopupControl#minWidth:Double=-1.0 +javafx.scene.control.PopupControl#prefHeight:Double=-1.0 +javafx.scene.control.PopupControl#prefWidth:Double=-1.0 +javafx.scene.control.PopupControl#style:String= +javafx.scene.control.ProgressIndicator#progress:Double=-1.0 +javafx.scene.control.RadioMenuItem#selected:Boolean=false +javafx.scene.control.ScrollBar#blockIncrement:Double=10.0 +javafx.scene.control.ScrollBar#max:Double=100.0 +javafx.scene.control.ScrollBar#min:Double=0.0 +javafx.scene.control.ScrollBar#orientation:Enum=HORIZONTAL +javafx.scene.control.ScrollBar#unitIncrement:Double=1.0 +javafx.scene.control.ScrollBar#value:Double=0.0 +javafx.scene.control.ScrollBar#visibleAmount:Double=15.0 +javafx.scene.control.ScrollPane#fitToHeight:Boolean=false +javafx.scene.control.ScrollPane#fitToWidth:Boolean=false +javafx.scene.control.ScrollPane#hbarPolicy:Enum=AS_NEEDED +javafx.scene.control.ScrollPane#hmax:Double=1.0 +javafx.scene.control.ScrollPane#hmin:Double=0.0 +javafx.scene.control.ScrollPane#hvalue:Double=0.0 +javafx.scene.control.ScrollPane#minViewportHeight:Double=0.0 +javafx.scene.control.ScrollPane#minViewportWidth:Double=0.0 +javafx.scene.control.ScrollPane#pannable:Boolean=false +javafx.scene.control.ScrollPane#prefViewportHeight:Double=0.0 +javafx.scene.control.ScrollPane#prefViewportWidth:Double=0.0 +javafx.scene.control.ScrollPane#vbarPolicy:Enum=AS_NEEDED +javafx.scene.control.ScrollPane#vmax:Double=1.0 +javafx.scene.control.ScrollPane#vmin:Double=0.0 +javafx.scene.control.ScrollPane#vvalue:Double=0.0 +javafx.scene.control.Separator#halignment:Enum=CENTER +javafx.scene.control.Separator#orientation:Enum=HORIZONTAL +javafx.scene.control.Separator#valignment:Enum=CENTER +javafx.scene.control.Slider#blockIncrement:Double=10.0 +javafx.scene.control.Slider#majorTickUnit:Double=25.0 +javafx.scene.control.Slider#max:Double=100.0 +javafx.scene.control.Slider#min:Double=0.0 +javafx.scene.control.Slider#minorTickCount:Integer=3 +javafx.scene.control.Slider#orientation:Enum=HORIZONTAL +javafx.scene.control.Slider#showTickLabels:Boolean=false +javafx.scene.control.Slider#showTickMarks:Boolean=false +javafx.scene.control.Slider#snapToTicks:Boolean=false +javafx.scene.control.Slider#value:Double=0.0 +javafx.scene.control.Slider#valueChanging:Boolean=false +javafx.scene.control.Spinner#editable:Boolean=false +javafx.scene.control.SplitPane#orientation:Enum=HORIZONTAL +javafx.scene.control.SplitPane$Divider#position:Double=0.5 +javafx.scene.control.Tab#closable:Boolean=true +javafx.scene.control.Tab#disable:Boolean=false +javafx.scene.control.TabPane#rotateGraphic:Boolean=false +javafx.scene.control.TabPane#side:Enum=TOP +javafx.scene.control.TabPane#tabClosingPolicy:Enum=SELECTED_TAB +javafx.scene.control.TabPane#tabMaxHeight:Double=1.7976931348623157E308 +javafx.scene.control.TabPane#tabMaxWidth:Double=1.7976931348623157E308 +javafx.scene.control.TabPane#tabMinHeight:Double=0.0 +javafx.scene.control.TabPane#tabMinWidth:Double=0.0 +javafx.scene.control.TableColumn#sortType:Enum=ASCENDING +javafx.scene.control.TableColumnBase#editable:Boolean=true +javafx.scene.control.TableColumnBase#maxWidth:Double=5000.0 +javafx.scene.control.TableColumnBase#minWidth:Double=10.0 +javafx.scene.control.TableColumnBase#prefWidth:Double=80.0 +javafx.scene.control.TableColumnBase#resizable:Boolean=true +javafx.scene.control.TableColumnBase#sortable:Boolean=true +javafx.scene.control.TableColumnBase#style:String= +javafx.scene.control.TableColumnBase#text:String= +javafx.scene.control.TableColumnBase#visible:Boolean=true +javafx.scene.control.TableView#editable:Boolean=false +javafx.scene.control.TableView#fixedCellSize:Double=-1.0 +javafx.scene.control.TableView#tableMenuButtonVisible:Boolean=false +javafx.scene.control.TextArea#prefColumnCount:Integer=40 +javafx.scene.control.TextArea#prefRowCount:Integer=10 +javafx.scene.control.TextArea#scrollLeft:Double=0.0 +javafx.scene.control.TextArea#scrollTop:Double=0.0 +javafx.scene.control.TextArea#wrapText:Boolean=false +javafx.scene.control.TextField#alignment:Enum=CENTER_LEFT +javafx.scene.control.TextField#prefColumnCount:Integer=12 +javafx.scene.control.TextInputControl#editable:Boolean=true +javafx.scene.control.TextInputControl#promptText:String= +javafx.scene.control.TextInputControl#text:String= +javafx.scene.control.TitledPane#animated:Boolean=true +javafx.scene.control.TitledPane#collapsible:Boolean=true +javafx.scene.control.TitledPane#expanded:Boolean=true +javafx.scene.control.ToggleButton#selected:Boolean=false +javafx.scene.control.ToolBar#orientation:Enum=HORIZONTAL +javafx.scene.control.Tooltip#contentDisplay:Enum=LEFT +javafx.scene.control.Tooltip#graphicTextGap:Double=4.0 +javafx.scene.control.Tooltip#text:String= +javafx.scene.control.Tooltip#textAlignment:Enum=LEFT +javafx.scene.control.Tooltip#textOverrun:Enum=ELLIPSIS +javafx.scene.control.Tooltip#wrapText:Boolean=false +javafx.scene.control.TreeItem#expanded:Boolean=false +javafx.scene.control.TreeTableColumn#sortType:Enum=ASCENDING +javafx.scene.control.TreeTableView#editable:Boolean=false +javafx.scene.control.TreeTableView#fixedCellSize:Double=-1.0 +javafx.scene.control.TreeTableView#showRoot:Boolean=true +javafx.scene.control.TreeTableView#sortMode:Enum=ALL_DESCENDANTS +javafx.scene.control.TreeTableView#tableMenuButtonVisible:Boolean=false +javafx.scene.control.TreeView#editable:Boolean=false +javafx.scene.control.TreeView#fixedCellSize:Double=-1.0 +javafx.scene.control.TreeView#showRoot:Boolean=true +javafx.scene.control.cell.ComboBoxListCell#comboBoxEditable:Boolean=false +javafx.scene.control.cell.ComboBoxTableCell#comboBoxEditable:Boolean=false +javafx.scene.control.cell.ComboBoxTreeCell#comboBoxEditable:Boolean=false +javafx.scene.control.cell.ComboBoxTreeTableCell#comboBoxEditable:Boolean=false +javafx.scene.effect.Blend#mode:Enum=SRC_OVER +javafx.scene.effect.Blend#opacity:Double=1.0 +javafx.scene.effect.Bloom#threshold:Double=0.3 +javafx.scene.effect.BoxBlur#height:Double=5.0 +javafx.scene.effect.BoxBlur#iterations:Integer=1 +javafx.scene.effect.BoxBlur#width:Double=5.0 +javafx.scene.effect.ColorAdjust#brightness:Double=0.0 +javafx.scene.effect.ColorAdjust#contrast:Double=0.0 +javafx.scene.effect.ColorAdjust#hue:Double=0.0 +javafx.scene.effect.ColorAdjust#saturation:Double=0.0 +javafx.scene.effect.ColorInput#height:Double=0.0 +javafx.scene.effect.ColorInput#width:Double=0.0 +javafx.scene.effect.ColorInput#x:Double=0.0 +javafx.scene.effect.ColorInput#y:Double=0.0 +javafx.scene.effect.DisplacementMap#offsetX:Double=0.0 +javafx.scene.effect.DisplacementMap#offsetY:Double=0.0 +javafx.scene.effect.DisplacementMap#scaleX:Double=1.0 +javafx.scene.effect.DisplacementMap#scaleY:Double=1.0 +javafx.scene.effect.DisplacementMap#wrap:Boolean=false +javafx.scene.effect.DropShadow#blurType:Enum=THREE_PASS_BOX +javafx.scene.effect.DropShadow#height:Double=21.0 +javafx.scene.effect.DropShadow#offsetX:Double=0.0 +javafx.scene.effect.DropShadow#offsetY:Double=0.0 +javafx.scene.effect.DropShadow#radius:Double=10.0 +javafx.scene.effect.DropShadow#spread:Double=0.0 +javafx.scene.effect.DropShadow#width:Double=21.0 +javafx.scene.effect.FloatMap#height:Integer=1 +javafx.scene.effect.FloatMap#width:Integer=1 +javafx.scene.effect.GaussianBlur#radius:Double=10.0 +javafx.scene.effect.Glow#level:Double=0.3 +javafx.scene.effect.ImageInput#x:Double=0.0 +javafx.scene.effect.ImageInput#y:Double=0.0 +javafx.scene.effect.InnerShadow#blurType:Enum=THREE_PASS_BOX +javafx.scene.effect.InnerShadow#choke:Double=0.0 +javafx.scene.effect.InnerShadow#height:Double=21.0 +javafx.scene.effect.InnerShadow#offsetX:Double=0.0 +javafx.scene.effect.InnerShadow#offsetY:Double=0.0 +javafx.scene.effect.InnerShadow#radius:Double=10.0 +javafx.scene.effect.InnerShadow#width:Double=21.0 +javafx.scene.effect.Light$Distant#azimuth:Double=45.0 +javafx.scene.effect.Light$Distant#elevation:Double=45.0 +javafx.scene.effect.Light$Point#x:Double=0.0 +javafx.scene.effect.Light$Point#y:Double=0.0 +javafx.scene.effect.Light$Point#z:Double=0.0 +javafx.scene.effect.Light$Spot#pointsAtX:Double=0.0 +javafx.scene.effect.Light$Spot#pointsAtY:Double=0.0 +javafx.scene.effect.Light$Spot#pointsAtZ:Double=0.0 +javafx.scene.effect.Light$Spot#specularExponent:Double=1.0 +javafx.scene.effect.Lighting#diffuseConstant:Double=1.0 +javafx.scene.effect.Lighting#specularConstant:Double=0.3 +javafx.scene.effect.Lighting#specularExponent:Double=20.0 +javafx.scene.effect.Lighting#surfaceScale:Double=1.5 +javafx.scene.effect.MotionBlur#angle:Double=0.0 +javafx.scene.effect.MotionBlur#radius:Double=10.0 +javafx.scene.effect.PerspectiveTransform#llx:Double=0.0 +javafx.scene.effect.PerspectiveTransform#lly:Double=0.0 +javafx.scene.effect.PerspectiveTransform#lrx:Double=0.0 +javafx.scene.effect.PerspectiveTransform#lry:Double=0.0 +javafx.scene.effect.PerspectiveTransform#ulx:Double=0.0 +javafx.scene.effect.PerspectiveTransform#uly:Double=0.0 +javafx.scene.effect.PerspectiveTransform#urx:Double=0.0 +javafx.scene.effect.PerspectiveTransform#ury:Double=0.0 +javafx.scene.effect.Reflection#bottomOpacity:Double=0.0 +javafx.scene.effect.Reflection#fraction:Double=0.75 +javafx.scene.effect.Reflection#topOffset:Double=0.0 +javafx.scene.effect.Reflection#topOpacity:Double=0.5 +javafx.scene.effect.SepiaTone#level:Double=1.0 +javafx.scene.effect.Shadow#blurType:Enum=THREE_PASS_BOX +javafx.scene.effect.Shadow#height:Double=21.0 +javafx.scene.effect.Shadow#radius:Double=10.0 +javafx.scene.effect.Shadow#width:Double=21.0 +javafx.scene.image.ImageView#fitHeight:Double=0.0 +javafx.scene.image.ImageView#fitWidth:Double=0.0 +javafx.scene.image.ImageView#preserveRatio:Boolean=false +javafx.scene.image.ImageView#smooth:Boolean=true +javafx.scene.image.ImageView#x:Double=0.0 +javafx.scene.image.ImageView#y:Double=0.0 +javafx.scene.layout.ColumnConstraints#fillWidth:Boolean=true +javafx.scene.layout.ColumnConstraints#maxWidth:Double=-1.0 +javafx.scene.layout.ColumnConstraints#minWidth:Double=-1.0 +javafx.scene.layout.ColumnConstraints#percentWidth:Double=-1.0 +javafx.scene.layout.ColumnConstraints#prefWidth:Double=-1.0 +javafx.scene.layout.FlowPane#alignment:Enum=TOP_LEFT +javafx.scene.layout.FlowPane#columnHalignment:Enum=LEFT +javafx.scene.layout.FlowPane#hgap:Double=0.0 +javafx.scene.layout.FlowPane#orientation:Enum=HORIZONTAL +javafx.scene.layout.FlowPane#prefWrapLength:Double=400.0 +javafx.scene.layout.FlowPane#rowValignment:Enum=CENTER +javafx.scene.layout.FlowPane#vgap:Double=0.0 +javafx.scene.layout.GridPane#alignment:Enum=TOP_LEFT +javafx.scene.layout.GridPane#gridLinesVisible:Boolean=false +javafx.scene.layout.GridPane#hgap:Double=0.0 +javafx.scene.layout.GridPane#vgap:Double=0.0 +javafx.scene.layout.HBox#alignment:Enum=TOP_LEFT +javafx.scene.layout.HBox#fillHeight:Boolean=true +javafx.scene.layout.HBox#spacing:Double=0.0 +javafx.scene.layout.Region#cacheShape:Boolean=true +javafx.scene.layout.Region#centerShape:Boolean=true +javafx.scene.layout.Region#maxHeight:Double=-1.0 +javafx.scene.layout.Region#maxWidth:Double=-1.0 +javafx.scene.layout.Region#minHeight:Double=-1.0 +javafx.scene.layout.Region#minWidth:Double=-1.0 +javafx.scene.layout.Region#prefHeight:Double=-1.0 +javafx.scene.layout.Region#prefWidth:Double=-1.0 +javafx.scene.layout.Region#scaleShape:Boolean=true +javafx.scene.layout.Region#snapToPixel:Boolean=true +javafx.scene.layout.RowConstraints#fillHeight:Boolean=true +javafx.scene.layout.RowConstraints#maxHeight:Double=-1.0 +javafx.scene.layout.RowConstraints#minHeight:Double=-1.0 +javafx.scene.layout.RowConstraints#percentHeight:Double=-1.0 +javafx.scene.layout.RowConstraints#prefHeight:Double=-1.0 +javafx.scene.layout.StackPane#alignment:Enum=CENTER +javafx.scene.layout.TilePane#alignment:Enum=TOP_LEFT +javafx.scene.layout.TilePane#hgap:Double=0.0 +javafx.scene.layout.TilePane#orientation:Enum=HORIZONTAL +javafx.scene.layout.TilePane#prefColumns:Integer=5 +javafx.scene.layout.TilePane#prefRows:Integer=5 +javafx.scene.layout.TilePane#prefTileHeight:Double=-1.0 +javafx.scene.layout.TilePane#prefTileWidth:Double=-1.0 +javafx.scene.layout.TilePane#tileAlignment:Enum=CENTER +javafx.scene.layout.TilePane#vgap:Double=0.0 +javafx.scene.layout.VBox#alignment:Enum=TOP_LEFT +javafx.scene.layout.VBox#fillWidth:Boolean=true +javafx.scene.layout.VBox#spacing:Double=0.0 +javafx.scene.media.EqualizerBand#bandwidth:Double=0.0 +javafx.scene.media.EqualizerBand#centerFrequency:Double=0.0 +javafx.scene.media.EqualizerBand#gain:Double=0.0 +javafx.scene.media.MediaView#fitHeight:Double=0.0 +javafx.scene.media.MediaView#fitWidth:Double=0.0 +javafx.scene.media.MediaView#preserveRatio:Boolean=true +javafx.scene.media.MediaView#smooth:Boolean=true +javafx.scene.media.MediaView#x:Double=0.0 +javafx.scene.media.MediaView#y:Double=0.0 +javafx.scene.paint.PhongMaterial#specularPower:Double=32.0 +javafx.scene.shape.Arc#centerX:Double=0.0 +javafx.scene.shape.Arc#centerY:Double=0.0 +javafx.scene.shape.Arc#length:Double=0.0 +javafx.scene.shape.Arc#radiusX:Double=0.0 +javafx.scene.shape.Arc#radiusY:Double=0.0 +javafx.scene.shape.Arc#startAngle:Double=0.0 +javafx.scene.shape.Arc#type:Enum=OPEN +javafx.scene.shape.ArcTo#XAxisRotation:Double=0.0 +javafx.scene.shape.ArcTo#largeArcFlag:Boolean=false +javafx.scene.shape.ArcTo#radiusX:Double=0.0 +javafx.scene.shape.ArcTo#radiusY:Double=0.0 +javafx.scene.shape.ArcTo#sweepFlag:Boolean=false +javafx.scene.shape.ArcTo#x:Double=0.0 +javafx.scene.shape.ArcTo#y:Double=0.0 +javafx.scene.shape.Box#depth:Double=2.0 +javafx.scene.shape.Box#height:Double=2.0 +javafx.scene.shape.Box#width:Double=2.0 +javafx.scene.shape.Circle#centerX:Double=0.0 +javafx.scene.shape.Circle#centerY:Double=0.0 +javafx.scene.shape.Circle#radius:Double=0.0 +javafx.scene.shape.CubicCurve#controlX1:Double=0.0 +javafx.scene.shape.CubicCurve#controlX2:Double=0.0 +javafx.scene.shape.CubicCurve#controlY1:Double=0.0 +javafx.scene.shape.CubicCurve#controlY2:Double=0.0 +javafx.scene.shape.CubicCurve#endX:Double=0.0 +javafx.scene.shape.CubicCurve#endY:Double=0.0 +javafx.scene.shape.CubicCurve#startX:Double=0.0 +javafx.scene.shape.CubicCurve#startY:Double=0.0 +javafx.scene.shape.CubicCurveTo#controlX1:Double=0.0 +javafx.scene.shape.CubicCurveTo#controlX2:Double=0.0 +javafx.scene.shape.CubicCurveTo#controlY1:Double=0.0 +javafx.scene.shape.CubicCurveTo#controlY2:Double=0.0 +javafx.scene.shape.CubicCurveTo#x:Double=0.0 +javafx.scene.shape.CubicCurveTo#y:Double=0.0 +javafx.scene.shape.Cylinder#height:Double=2.0 +javafx.scene.shape.Cylinder#radius:Double=1.0 +javafx.scene.shape.Ellipse#centerX:Double=0.0 +javafx.scene.shape.Ellipse#centerY:Double=0.0 +javafx.scene.shape.Ellipse#radiusX:Double=0.0 +javafx.scene.shape.Ellipse#radiusY:Double=0.0 +javafx.scene.shape.HLineTo#x:Double=0.0 +javafx.scene.shape.Line#endX:Double=0.0 +javafx.scene.shape.Line#endY:Double=0.0 +javafx.scene.shape.Line#startX:Double=0.0 +javafx.scene.shape.Line#startY:Double=0.0 +javafx.scene.shape.LineTo#x:Double=0.0 +javafx.scene.shape.LineTo#y:Double=0.0 +javafx.scene.shape.MoveTo#x:Double=0.0 +javafx.scene.shape.MoveTo#y:Double=0.0 +javafx.scene.shape.Path#fillRule:Enum=NON_ZERO +javafx.scene.shape.PathElement#absolute:Boolean=true +javafx.scene.shape.QuadCurve#controlX:Double=0.0 +javafx.scene.shape.QuadCurve#controlY:Double=0.0 +javafx.scene.shape.QuadCurve#endX:Double=0.0 +javafx.scene.shape.QuadCurve#endY:Double=0.0 +javafx.scene.shape.QuadCurve#startX:Double=0.0 +javafx.scene.shape.QuadCurve#startY:Double=0.0 +javafx.scene.shape.QuadCurveTo#controlX:Double=0.0 +javafx.scene.shape.QuadCurveTo#controlY:Double=0.0 +javafx.scene.shape.QuadCurveTo#x:Double=0.0 +javafx.scene.shape.QuadCurveTo#y:Double=0.0 +javafx.scene.shape.Rectangle#arcHeight:Double=0.0 +javafx.scene.shape.Rectangle#arcWidth:Double=0.0 +javafx.scene.shape.Rectangle#height:Double=0.0 +javafx.scene.shape.Rectangle#width:Double=0.0 +javafx.scene.shape.Rectangle#x:Double=0.0 +javafx.scene.shape.Rectangle#y:Double=0.0 +javafx.scene.shape.SVGPath#content:String= +javafx.scene.shape.SVGPath#fillRule:Enum=NON_ZERO +javafx.scene.shape.Shape#smooth:Boolean=true +javafx.scene.shape.Shape#strokeDashOffset:Double=0.0 +javafx.scene.shape.Shape#strokeLineCap:Enum=SQUARE +javafx.scene.shape.Shape#strokeLineJoin:Enum=MITER +javafx.scene.shape.Shape#strokeMiterLimit:Double=10.0 +javafx.scene.shape.Shape#strokeType:Enum=CENTERED +javafx.scene.shape.Shape#strokeWidth:Double=1.0 +javafx.scene.shape.Shape3D#cullFace:Enum=BACK +javafx.scene.shape.Shape3D#drawMode:Enum=FILL +javafx.scene.shape.Sphere#radius:Double=1.0 +javafx.scene.shape.VLineTo#y:Double=0.0 +javafx.scene.text.Text#boundsType:Enum=LOGICAL +javafx.scene.text.Text#fontSmoothingType:Enum=GRAY +javafx.scene.text.Text#impl_caretBias:Boolean=true +javafx.scene.text.Text#impl_caretPosition:Integer=-1 +javafx.scene.text.Text#impl_selectionEnd:Integer=-1 +javafx.scene.text.Text#impl_selectionStart:Integer=-1 +javafx.scene.text.Text#lineSpacing:Double=0.0 +javafx.scene.text.Text#strikethrough:Boolean=false +javafx.scene.text.Text#text:String= +javafx.scene.text.Text#textAlignment:Enum=LEFT +javafx.scene.text.Text#textOrigin:Enum=BASELINE +javafx.scene.text.Text#underline:Boolean=false +javafx.scene.text.Text#wrappingWidth:Double=0.0 +javafx.scene.text.Text#x:Double=0.0 +javafx.scene.text.Text#y:Double=0.0 +javafx.scene.text.TextFlow#lineSpacing:Double=0.0 +javafx.scene.text.TextFlow#textAlignment:Enum=LEFT +javafx.scene.transform.Affine#mxx:Double=1.0 +javafx.scene.transform.Affine#mxy:Double=0.0 +javafx.scene.transform.Affine#mxz:Double=0.0 +javafx.scene.transform.Affine#myx:Double=0.0 +javafx.scene.transform.Affine#myy:Double=1.0 +javafx.scene.transform.Affine#myz:Double=0.0 +javafx.scene.transform.Affine#mzx:Double=0.0 +javafx.scene.transform.Affine#mzy:Double=0.0 +javafx.scene.transform.Affine#mzz:Double=1.0 +javafx.scene.transform.Affine#tx:Double=0.0 +javafx.scene.transform.Affine#ty:Double=0.0 +javafx.scene.transform.Affine#tz:Double=0.0 +javafx.scene.transform.Rotate#angle:Double=0.0 +javafx.scene.transform.Rotate#pivotX:Double=0.0 +javafx.scene.transform.Rotate#pivotY:Double=0.0 +javafx.scene.transform.Rotate#pivotZ:Double=0.0 +javafx.scene.transform.Scale#pivotX:Double=0.0 +javafx.scene.transform.Scale#pivotY:Double=0.0 +javafx.scene.transform.Scale#pivotZ:Double=0.0 +javafx.scene.transform.Scale#x:Double=1.0 +javafx.scene.transform.Scale#y:Double=1.0 +javafx.scene.transform.Scale#z:Double=1.0 +javafx.scene.transform.Shear#pivotX:Double=0.0 +javafx.scene.transform.Shear#pivotY:Double=0.0 +javafx.scene.transform.Shear#x:Double=0.0 +javafx.scene.transform.Shear#y:Double=0.0 +javafx.scene.transform.Translate#x:Double=0.0 +javafx.scene.transform.Translate#y:Double=0.0 +javafx.scene.transform.Translate#z:Double=0.0 +javafx.scene.web.WebEngine#javaScriptEnabled:Boolean=true +javafx.scene.web.WebView#contextMenuEnabled:Boolean=true +javafx.scene.web.WebView#fontScale:Double=1.0 +javafx.scene.web.WebView#fontSmoothingType:Enum=LCD +javafx.scene.web.WebView#maxHeight:Double=1.7976931348623157E308 +javafx.scene.web.WebView#maxWidth:Double=1.7976931348623157E308 +javafx.scene.web.WebView#minHeight:Double=0.0 +javafx.scene.web.WebView#minWidth:Double=0.0 +javafx.scene.web.WebView#prefHeight:Double=600.0 +javafx.scene.web.WebView#prefWidth:Double=800.0 +javafx.scene.web.WebView#zoom:Double=1.0 +javafx.stage.PopupWindow#autoFix:Boolean=true +javafx.stage.PopupWindow#hideOnEscape:Boolean=true +javafx.stage.Stage#maxHeight:Double=1.7976931348623157E308 +javafx.stage.Stage#maxWidth:Double=1.7976931348623157E308 +javafx.stage.Stage#minHeight:Double=0.0 +javafx.stage.Stage#minWidth:Double=0.0 +javafx.stage.Stage#resizable:Boolean=true +javafx.stage.Window#opacity:Double=1.0 +-------- Overridden in subclass --------- +javafx.embed.swing.SwingNode#focusTraversable:Boolean=true +javafx.scene.Group#accessibleRole:Enum=PARENT +javafx.scene.canvas.Canvas#nodeOrientation:Enum=LEFT_TO_RIGHT +javafx.scene.control.Button#accessibleRole:Enum=BUTTON +javafx.scene.control.Button#focusTraversable:Boolean=true +javafx.scene.control.Button#mnemonicParsing:Boolean=true +javafx.scene.control.CheckBox#accessibleRole:Enum=CHECK_BOX +javafx.scene.control.CheckBox#focusTraversable:Boolean=true +javafx.scene.control.CheckBox#mnemonicParsing:Boolean=true +javafx.scene.control.ChoiceBox#accessibleRole:Enum=COMBO_BOX +javafx.scene.control.ChoiceBox#focusTraversable:Boolean=true +javafx.scene.control.ColorPicker#focusTraversable:Boolean=true +javafx.scene.control.ComboBox#accessibleRole:Enum=COMBO_BOX +javafx.scene.control.ComboBox#focusTraversable:Boolean=true +javafx.scene.control.ContextMenu#autoHide:Boolean=true +javafx.scene.control.ContextMenu#consumeAutoHidingEvents:Boolean=false +javafx.scene.control.DatePicker#accessibleRole:Enum=DATE_PICKER +javafx.scene.control.DatePicker#editable:Boolean=true +javafx.scene.control.DatePicker#focusTraversable:Boolean=true +javafx.scene.control.Hyperlink#accessibleRole:Enum=HYPERLINK +javafx.scene.control.Hyperlink#focusTraversable:Boolean=true +javafx.scene.control.Label#accessibleRole:Enum=TEXT +javafx.scene.control.ListCell#accessibleRole:Enum=LIST_ITEM +javafx.scene.control.ListView#accessibleRole:Enum=LIST_VIEW +javafx.scene.control.ListView#focusTraversable:Boolean=true +javafx.scene.control.MenuBar#accessibleRole:Enum=MENU_BAR +javafx.scene.control.MenuButton#accessibleRole:Enum=MENU_BUTTON +javafx.scene.control.MenuButton#focusTraversable:Boolean=true +javafx.scene.control.MenuButton#mnemonicParsing:Boolean=true +javafx.scene.control.Pagination#accessibleRole:Enum=PAGINATION +javafx.scene.control.Pagination#focusTraversable:Boolean=true +javafx.scene.control.PasswordField#accessibleRole:Enum=PASSWORD_FIELD +javafx.scene.control.PopupControl#anchorLocation:Enum=CONTENT_TOP_LEFT +javafx.scene.control.ProgressIndicator#accessibleRole:Enum=PROGRESS_INDICATOR +javafx.scene.control.RadioButton#accessibleRole:Enum=RADIO_BUTTON +javafx.scene.control.RadioButton#alignment:Enum=CENTER_LEFT +javafx.scene.control.ScrollBar#accessibleRole:Enum=SCROLL_BAR +javafx.scene.control.ScrollPane#accessibleRole:Enum=SCROLL_PANE +javafx.scene.control.SeparatorMenuItem#hideOnClick:Boolean=false +javafx.scene.control.Slider#accessibleRole:Enum=SLIDER +javafx.scene.control.Slider#focusTraversable:Boolean=true +javafx.scene.control.Spinner#accessibleRole:Enum=SPINNER +javafx.scene.control.Spinner#focusTraversable:Boolean=true +javafx.scene.control.SplitMenuButton#accessibleRole:Enum=SPLIT_MENU_BUTTON +javafx.scene.control.TabPane#accessibleRole:Enum=TAB_PANE +javafx.scene.control.TabPane#focusTraversable:Boolean=true +javafx.scene.control.TableCell#accessibleRole:Enum=TABLE_CELL +javafx.scene.control.TableRow#accessibleRole:Enum=TABLE_ROW +javafx.scene.control.TableView#accessibleRole:Enum=TABLE_VIEW +javafx.scene.control.TableView#focusTraversable:Boolean=true +javafx.scene.control.TextArea#accessibleRole:Enum=TEXT_AREA +javafx.scene.control.TextArea#focusTraversable:Boolean=true +javafx.scene.control.TextField#accessibleRole:Enum=TEXT_FIELD +javafx.scene.control.TextField#focusTraversable:Boolean=true +javafx.scene.control.TitledPane#accessibleRole:Enum=TITLED_PANE +javafx.scene.control.TitledPane#focusTraversable:Boolean=true +javafx.scene.control.ToggleButton#accessibleRole:Enum=TOGGLE_BUTTON +javafx.scene.control.ToggleButton#alignment:Enum=CENTER +javafx.scene.control.ToggleButton#focusTraversable:Boolean=true +javafx.scene.control.ToggleButton#mnemonicParsing:Boolean=true +javafx.scene.control.ToolBar#accessibleRole:Enum=TOOL_BAR +javafx.scene.control.TreeCell#accessibleRole:Enum=TREE_ITEM +javafx.scene.control.TreeTableCell#accessibleRole:Enum=TREE_TABLE_CELL +javafx.scene.control.TreeTableRow#accessibleRole:Enum=TREE_TABLE_ROW +javafx.scene.control.TreeTableView#accessibleRole:Enum=TREE_TABLE_VIEW +javafx.scene.control.TreeTableView#focusTraversable:Boolean=true +javafx.scene.control.TreeView#accessibleRole:Enum=TREE_VIEW +javafx.scene.control.TreeView#focusTraversable:Boolean=true +javafx.scene.control.cell.CheckBoxTreeCell#accessibleRole:Enum=TREE_ITEM +javafx.scene.control.cell.CheckBoxTreeCell#alignment:Enum=CENTER_LEFT +javafx.scene.control.cell.CheckBoxTreeCell#focusTraversable:Boolean=false +javafx.scene.control.cell.CheckBoxTreeCell#mnemonicParsing:Boolean=false +javafx.scene.control.cell.CheckBoxTreeCell#nodeOrientation:Enum=INHERIT +javafx.scene.control.cell.CheckBoxTreeCell#pickOnBounds:Boolean=true +javafx.scene.control.cell.ChoiceBoxTreeCell#accessibleRole:Enum=TREE_ITEM +javafx.scene.control.cell.ChoiceBoxTreeCell#alignment:Enum=CENTER_LEFT +javafx.scene.control.cell.ChoiceBoxTreeCell#focusTraversable:Boolean=false +javafx.scene.control.cell.ChoiceBoxTreeCell#mnemonicParsing:Boolean=false +javafx.scene.control.cell.ChoiceBoxTreeCell#nodeOrientation:Enum=INHERIT +javafx.scene.control.cell.ChoiceBoxTreeCell#pickOnBounds:Boolean=true +javafx.scene.control.cell.ComboBoxTreeCell#accessibleRole:Enum=TREE_ITEM +javafx.scene.control.cell.ComboBoxTreeCell#alignment:Enum=CENTER_LEFT +javafx.scene.control.cell.ComboBoxTreeCell#focusTraversable:Boolean=false +javafx.scene.control.cell.ComboBoxTreeCell#mnemonicParsing:Boolean=false +javafx.scene.control.cell.ComboBoxTreeCell#nodeOrientation:Enum=INHERIT +javafx.scene.control.cell.ComboBoxTreeCell#pickOnBounds:Boolean=true +javafx.scene.control.cell.TextFieldTreeCell#accessibleRole:Enum=TREE_ITEM +javafx.scene.control.cell.TextFieldTreeCell#alignment:Enum=CENTER_LEFT +javafx.scene.control.cell.TextFieldTreeCell#focusTraversable:Boolean=false +javafx.scene.control.cell.TextFieldTreeCell#mnemonicParsing:Boolean=false +javafx.scene.control.cell.TextFieldTreeCell#nodeOrientation:Enum=INHERIT +javafx.scene.control.cell.TextFieldTreeCell#pickOnBounds:Boolean=true +javafx.scene.image.ImageView#accessibleRole:Enum=IMAGE_VIEW +javafx.scene.image.ImageView#nodeOrientation:Enum=LEFT_TO_RIGHT +javafx.scene.layout.Region#accessibleRole:Enum=PARENT +javafx.scene.layout.Region#pickOnBounds:Boolean=true +javafx.scene.media.MediaView#nodeOrientation:Enum=LEFT_TO_RIGHT +javafx.scene.text.Text#accessibleRole:Enum=TEXT +javafx.scene.text.Text#pickOnBounds:Boolean=true +javafx.scene.text.TextFlow#accessibleRole:Enum=TEXT +javafx.scene.web.HTMLEditor#focusTraversable:Boolean=true +javafx.scene.web.WebView#accessibleRole:Enum=PARENT +javafx.scene.web.WebView#focusTraversable:Boolean=true +javafx.scene.web.WebView#nodeOrientation:Enum=LEFT_TO_RIGHT +-------- Overridden properties' values --------- +-- javafx.scene.Node#accessibleRole[BUTTON, CHECK_BOX, COMBO_BOX, DATE_PICKER, HYPERLINK, IMAGE_VIEW, LIST_ITEM, LIST_VIEW, MENU_BAR, MENU_BUTTON, NODE, PAGINATION, PARENT, PASSWORD_FIELD, PROGRESS_INDICATOR, RADIO_BUTTON, SCROLL_BAR, SCROLL_PANE, SLIDER, SPINNER, SPLIT_MENU_BUTTON, TABLE_CELL, TABLE_ROW, TABLE_VIEW, TAB_PANE, TEXT, TEXT_AREA, TEXT_FIELD, TITLED_PANE, TOGGLE_BUTTON, TOOL_BAR, TREE_ITEM, TREE_TABLE_CELL, TREE_TABLE_ROW, TREE_TABLE_VIEW, TREE_VIEW] +-- javafx.scene.Node#focusTraversable[false, true] +-- javafx.scene.Node#nodeOrientation[INHERIT, LEFT_TO_RIGHT] +-- javafx.scene.Node#pickOnBounds[false, true] +-- javafx.scene.control.ComboBoxBase#editable[false, true] +-- javafx.scene.control.CustomMenuItem#hideOnClick[false, true] +-- javafx.scene.control.Labeled#alignment[CENTER, CENTER_LEFT] +-- javafx.scene.control.Labeled#mnemonicParsing[false, true] +-- javafx.stage.PopupWindow#anchorLocation[CONTENT_TOP_LEFT, WINDOW_TOP_LEFT] +-- javafx.stage.PopupWindow#autoHide[false, true] +-- javafx.stage.PopupWindow#consumeAutoHidingEvents[false, true] +-------- Skipped properties --------- +-- javafx.scene.web.WebEngine#userAgent +-- javafx.scene.web.HTMLEditor#htmlText +-- javafx.scene.control.ButtonBar#buttonOrder diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/inspections/RemoveTagFix.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/inspections/RemoveTagFix.java new file mode 100644 index 000000000000..0e97afd08bc7 --- /dev/null +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/inspections/RemoveTagFix.java @@ -0,0 +1,61 @@ +package org.jetbrains.plugins.javaFX.fxml.codeInsight.inspections; + +import com.intellij.codeInsight.FileModificationService; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlTag; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.javaFX.fxml.JavaFxFileTypeFactory; + +/** + * @author Pavel.Dolgov + */ +public class RemoveTagFix implements LocalQuickFix { + private static final Logger LOG = Logger.getInstance("#" + RemoveTagFix.class.getName()); + + private final String myTagName; + + public RemoveTagFix(String name) { + myTagName = name; + } + + @Nls + @NotNull + @Override + public String getName() { + return "Remove tag '" + myTagName + "'"; + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return "Remove tag"; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + final PsiElement element = descriptor.getPsiElement(); + if (element != null) { + final PsiFile containingFile = element.getContainingFile(); + LOG.assertTrue(containingFile != null && JavaFxFileTypeFactory.isFxml(containingFile), + containingFile == null ? "no containing file found" : "containing file: " + containingFile.getName()); + final XmlTag xmlTag = PsiTreeUtil.getParentOfType(element, XmlTag.class, false); + if (xmlTag != null) { + final XmlTag parentTag = xmlTag.getParentTag(); + if (!FileModificationService.getInstance().preparePsiElementsForWrite(element)) return; + xmlTag.delete(); + if (parentTag != null) { + CodeStyleManager.getInstance(project).reformat(parentTag); + } + } + } + } +} diff --git a/plugins/javaFX/testData/inspections/redundantValue/attributeHighlighting.fxml b/plugins/javaFX/testData/inspections/redundantValue/attributeHighlighting.fxml new file mode 100644 index 000000000000..0b1bc41452ad --- /dev/null +++ b/plugins/javaFX/testData/inspections/redundantValue/attributeHighlighting.fxml @@ -0,0 +1,11 @@ + + +maxHeight="-1" + maxWidth="500" + vgap="0" + hgap="1" + cache="false" + opacity="0.99"> + \ No newline at end of file diff --git a/plugins/javaFX/testData/inspections/redundantValue/immediateAttribute.fxml b/plugins/javaFX/testData/inspections/redundantValue/immediateAttribute.fxml new file mode 100644 index 000000000000..d0590a9eb58c --- /dev/null +++ b/plugins/javaFX/testData/inspections/redundantValue/immediateAttribute.fxml @@ -0,0 +1,8 @@ + +ent="TOP_LEFT" + prefHeight="400.0" + prefWidth="600.0"> + \ No newline at end of file diff --git a/plugins/javaFX/testData/inspections/redundantValue/immediateAttribute_after.fxml b/plugins/javaFX/testData/inspections/redundantValue/immediateAttribute_after.fxml new file mode 100644 index 000000000000..d2064eb21f78 --- /dev/null +++ b/plugins/javaFX/testData/inspections/redundantValue/immediateAttribute_after.fxml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/plugins/javaFX/testData/inspections/redundantValue/immediateTag.fxml b/plugins/javaFX/testData/inspections/redundantValue/immediateTag.fxml new file mode 100644 index 000000000000..05fce217fa21 --- /dev/null +++ b/plugins/javaFX/testData/inspections/redundantValue/immediateTag.fxml @@ -0,0 +1,12 @@ + + + + ent>TOP_LEFT + + \ No newline at end of file diff --git a/plugins/javaFX/testData/inspections/redundantValue/immediateTag_after.fxml b/plugins/javaFX/testData/inspections/redundantValue/immediateTag_after.fxml new file mode 100644 index 000000000000..da5fe05a55bc --- /dev/null +++ b/plugins/javaFX/testData/inspections/redundantValue/immediateTag_after.fxml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/plugins/javaFX/testData/inspections/redundantValue/inheritedAttribute.fxml b/plugins/javaFX/testData/inspections/redundantValue/inheritedAttribute.fxml new file mode 100644 index 000000000000..6f07a3d0b651 --- /dev/null +++ b/plugins/javaFX/testData/inspections/redundantValue/inheritedAttribute.fxml @@ -0,0 +1,9 @@ + + +ght="-1" + minHeight="-Infinity" + alignment="TOP_LEFT" + prefHeight="400.0" + prefWidth="600.0"> + \ No newline at end of file diff --git a/plugins/javaFX/testData/inspections/redundantValue/inheritedAttribute_after.fxml b/plugins/javaFX/testData/inspections/redundantValue/inheritedAttribute_after.fxml new file mode 100644 index 000000000000..39056fe8dabf --- /dev/null +++ b/plugins/javaFX/testData/inspections/redundantValue/inheritedAttribute_after.fxml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/plugins/javaFX/testData/inspections/redundantValue/inheritedTag.fxml b/plugins/javaFX/testData/inspections/redundantValue/inheritedTag.fxml new file mode 100644 index 000000000000..353c3a6adf33 --- /dev/null +++ b/plugins/javaFX/testData/inspections/redundantValue/inheritedTag.fxml @@ -0,0 +1,15 @@ + + + 1 + 0 + + 100 + + + -1 + + 500 + ight>-1 + 0.99 + false + \ No newline at end of file diff --git a/plugins/javaFX/testData/inspections/redundantValue/inheritedTag_after.fxml b/plugins/javaFX/testData/inspections/redundantValue/inheritedTag_after.fxml new file mode 100644 index 000000000000..5502aa35c848 --- /dev/null +++ b/plugins/javaFX/testData/inspections/redundantValue/inheritedTag_after.fxml @@ -0,0 +1,14 @@ + + + 1 + 0 + + 100 + + + -1 + + 500 + 0.99 + false + \ No newline at end of file diff --git a/plugins/javaFX/testData/inspections/redundantValue/modifiedAttribute.fxml b/plugins/javaFX/testData/inspections/redundantValue/modifiedAttribute.fxml new file mode 100644 index 000000000000..e748266cb151 --- /dev/null +++ b/plugins/javaFX/testData/inspections/redundantValue/modifiedAttribute.fxml @@ -0,0 +1,8 @@ + +ent="CENTER" + prefHeight="400.0" + prefWidth="600.0"> + \ No newline at end of file diff --git a/plugins/javaFX/testData/inspections/redundantValue/modifiedTag.fxml b/plugins/javaFX/testData/inspections/redundantValue/modifiedTag.fxml new file mode 100644 index 000000000000..d85785304456 --- /dev/null +++ b/plugins/javaFX/testData/inspections/redundantValue/modifiedTag.fxml @@ -0,0 +1,12 @@ + + + + ent>CENTER + + \ No newline at end of file diff --git a/plugins/javaFX/testData/inspections/redundantValue/tagHighlighting.fxml b/plugins/javaFX/testData/inspections/redundantValue/tagHighlighting.fxml new file mode 100644 index 000000000000..66d15f708c8c --- /dev/null +++ b/plugins/javaFX/testData/inspections/redundantValue/tagHighlighting.fxml @@ -0,0 +1,19 @@ + + + + + 100 + + + -1 + + 500 + -1 + 1 + 0 + 0.99 + false + + \ No newline at end of file