javafx: Inspection of FXML attributes and tags which contain default values and therefore are redundant (IDEA-102276)

This commit is contained in:
Pavel Dolgov
2016-03-02 19:59:27 +03:00
parent 4572bb8723
commit b1a2b1e838
20 changed files with 1723 additions and 1 deletions
@@ -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<IntentionAction> intentions = myFixture.getAvailableIntentions();
final IntentionAction intention = CodeInsightTestUtil.findIntentionByText(intentions, getHint(tagName));
assertNull(intention);
}
}
@@ -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
* <p>
* When launched without arguments it produces default values for JavaFX classes having default constructor and their superclasses, including some (but not all) abstract classes
* <p>
* When launched with <code>-fromSource</code> 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<Class, Class> 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<String, String> 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<String> 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<String, Map<String, ContainedValue>> containedProperties = new TreeMap<>();
final Map<String, Map<String, TypedValue>> declaredProperties = new TreeMap<>();
final Map<String, Map<String, Set<String>>> overriddenProperties = new TreeMap<>();
final Map<String, String> 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<String> 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<String, TypedValue> 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<String> 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<String, Set<String>> 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<String, Map<String, ContainedValue>> 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<String, ContainedValue> 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<String, Set<String>> 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<String, ContainedValue> 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<String> 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<String, String> 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));
}
}
@@ -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 + "'";
}
}
@@ -0,0 +1,6 @@
<html>
<body>
This inspection reports properties in .fxml files (both attributes and tags) having default values and therefore redundant.
<!-- tooltip end -->
</body>
</html>
@@ -22,6 +22,8 @@
enabledByDefault="true" level="WARNING" groupName="JavaFX"/>
<localInspection language="XML" shortName="JavaFxUnusedImports" implementationClass="org.jetbrains.plugins.javaFX.fxml.codeInsight.inspections.JavaFxUnusedImportsInspection"
enabledByDefault="true" level="WARNING" displayName="JavaFX unused imports" groupName="JavaFX"/>
<localInspection language="XML" shortName="JavaFxRedundantPropertyValue" implementationClass="org.jetbrains.plugins.javaFX.fxml.codeInsight.inspections.JavaFxRedundantPropertyValueInspection"
enabledByDefault="true" level="WARNING" displayName="JavaFX redundant property values" groupName="JavaFX"/>
<applicationService serviceInterface="org.jetbrains.plugins.javaFX.JavaFxSettings" serviceImplementation="org.jetbrains.plugins.javaFX.JavaFxSettings"/>
<applicationConfigurable groupId="language" displayName="JavaFX" id="preferences.JavaFX" instance="org.jetbrains.plugins.javaFX.JavaFxSettingsConfigurable"/>
@@ -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<Map<String, Map<String, Object>>> 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<PsiClass> 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<String, Object> 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<String, Object> getDefaultPropertyValues(String classQualifiedName) {
if (ourDefaultPropertyValues == null) {
ourDefaultPropertyValues = loadDefaultPropertyValues(JavaFxRedundantPropertyValueInspection.class.getSimpleName() + "8.txt");
}
Map<String, Map<String, Object>> values = SoftReference.dereference(ourDefaultPropertyValues);
return values != null ? values.get(classQualifiedName) : null;
}
/**
* The file format is <code>ClassName#propertyName:type=value</code> per line, line with leading double dash (--) is commented out
*/
private static Reference<Map<String, Map<String, Object>>> loadDefaultPropertyValues(String resourceName) {
final URL resource = JavaFxRedundantPropertyValueInspection.class.getResource(resourceName);
if (resource == null) {
LOG.warn("Resource not found: " + resourceName);
return null;
}
final Map<String, Map<String, Object>> 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<String, Object> properties = result.computeIfAbsent(className, ignored -> new THashMap<String, Object>());
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<Map<String, Map<String, Object>>>(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;
}
}
}
@@ -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
@@ -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);
}
}
}
}
}
@@ -0,0 +1,11 @@
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<GridPane xmlns:fx="http://javafx.com/fxml"
<warning descr="Attribute is redundant because it contains default value">maxHeight="-1"</warning>
maxWidth="500"
<warning descr="Attribute is redundant because it contains default value">vgap="0"</warning>
hgap="1"
<warning descr="Attribute is redundant because it contains default value">cache="false"</warning>
opacity="0.99">
<Label text="Hi" alignment="TOP_LEFT"/>
</GridPane>
@@ -0,0 +1,8 @@
<?import javafx.scene.layout.GridPane?>
<GridPane xmlns:fx="http://javafx.com/fxml"
maxHeight="-1"
minHeight="-Infinity"
alignm<caret>ent="TOP_LEFT"
prefHeight="400.0"
prefWidth="600.0">
</GridPane>
@@ -0,0 +1,7 @@
<?import javafx.scene.layout.GridPane?>
<GridPane xmlns:fx="http://javafx.com/fxml"
maxHeight="-1"
minHeight="-Infinity"
prefHeight="400.0"
prefWidth="600.0">
</GridPane>
@@ -0,0 +1,12 @@
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<GridPane xmlns:fx="http://javafx.com/fxml"
maxHeight="-1"
minHeight="-Infinity"
prefHeight="400.0"
prefWidth="600.0">
<alignm<caret>ent>TOP_LEFT</alignment>
<Label text="Hi">
<alignment>TOP_LEFT</alignment>
</Label>
</GridPane>
@@ -0,0 +1,11 @@
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<GridPane xmlns:fx="http://javafx.com/fxml"
maxHeight="-1"
minHeight="-Infinity"
prefHeight="400.0"
prefWidth="600.0">
<Label text="Hi">
<alignment>TOP_LEFT</alignment>
</Label>
</GridPane>
@@ -0,0 +1,9 @@
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<GridPane xmlns:fx="http://javafx.com/fxml"
maxHei<caret>ght="-1"
minHeight="-Infinity"
alignment="TOP_LEFT"
prefHeight="400.0"
prefWidth="600.0">
</GridPane>
@@ -0,0 +1,8 @@
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<GridPane xmlns:fx="http://javafx.com/fxml"
minHeight="-Infinity"
alignment="TOP_LEFT"
prefHeight="400.0"
prefWidth="600.0">
</GridPane>
@@ -0,0 +1,15 @@
<?import javafx.scene.layout.GridPane?>
<GridPane xmlns:fx="http://javafx.com/fxml">
<hgap>1</hgap>
<vgap>0</vgap>
<minWidth>
100
</minWidth>
<minHeight>
-1
</minHeight>
<maxWidth>500</maxWidth>
<maxHe<caret>ight>-1</maxHeight>
<opacity>0.99</opacity>
<cache>false</cache>
</GridPane>
@@ -0,0 +1,14 @@
<?import javafx.scene.layout.GridPane?>
<GridPane xmlns:fx="http://javafx.com/fxml">
<hgap>1</hgap>
<vgap>0</vgap>
<minWidth>
100
</minWidth>
<minHeight>
-1
</minHeight>
<maxWidth>500</maxWidth>
<opacity>0.99</opacity>
<cache>false</cache>
</GridPane>
@@ -0,0 +1,8 @@
<?import javafx.scene.layout.GridPane?>
<GridPane xmlns:fx="http://javafx.com/fxml"
maxHeight="-1"
minHeight="-Infinity"
alignm<caret>ent="CENTER"
prefHeight="400.0"
prefWidth="600.0">
</GridPane>
@@ -0,0 +1,12 @@
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<GridPane xmlns:fx="http://javafx.com/fxml"
maxHeight="-1"
minHeight="-Infinity"
prefHeight="400.0"
prefWidth="600.0">
<alignm<caret>ent>CENTER</alignment>
<Label text="Hi">
<alignment>TOP_LEFT</alignment>
</Label>
</GridPane>
@@ -0,0 +1,19 @@
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<GridPane xmlns:fx="http://javafx.com/fxml">
<minWidth>
100
</minWidth>
<warning descr="Tag is redundant because it contains default value"><minHeight>
-1
</minHeight></warning>
<maxWidth>500</maxWidth>
<warning descr="Tag is redundant because it contains default value"><maxHeight>-1</maxHeight></warning>
<hgap>1</hgap>
<warning descr="Tag is redundant because it contains default value"><vgap>0</vgap></warning>
<opacity>0.99</opacity>
<warning descr="Tag is redundant because it contains default value"><cache>false</cache></warning>
<Label text="Hi">
<alignment>TOP_LEFT</alignment>
</Label>
</GridPane>