diff --git a/sb/src/main/java/com/oracle/javafx/scenebuilder/app/info/InfoPanelController.java b/sb/src/main/java/com/oracle/javafx/scenebuilder/app/info/InfoPanelController.java index 82b07ecd06b11bc027498dff8081a2ef63eafe87..e6745e8de14819f8dd1a3dc3eb0f0bc3483f25d2 100644 --- a/sb/src/main/java/com/oracle/javafx/scenebuilder/app/info/InfoPanelController.java +++ b/sb/src/main/java/com/oracle/javafx/scenebuilder/app/info/InfoPanelController.java @@ -215,6 +215,10 @@ public class InfoPanelController extends AbstractFxmlPanelController { updateControllerAndControllerClassEditor(controllerClassEditor.getTextField().getText()); } }); + + getEditorController().getGlossary().revisionProperty().addListener((ov, t, t1) -> { + resetSuggestedControllerClasses(getEditorController().getFxmlLocation()); + }); leftTableColumn.setCellValueFactory(new PropertyValueFactory<>("key")); //NOI18N rightTableColumn.setCellValueFactory(new PropertyValueFactory<>("fxomObject")); //NOI18N @@ -468,7 +472,7 @@ public class InfoPanelController extends AbstractFxmlPanelController { private final ChangeListener<Boolean> checkBoxListener = (ov, t, t1) -> toggleFxRoot(); - public void resetSuggestedControllerClasses(URL location) { + private void resetSuggestedControllerClasses(URL location) { if (controllerClassEditor != null) { // The listener on fxmlLocationProperty is called before the file // denoted by the location is created on disk, hence the runLater. diff --git a/sb/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/InspectorPanelController.java b/sb/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/InspectorPanelController.java index 12fd88bee258916fb90d71426c88f5b33bb18bd9..589009af4f204fd760b54b07e78b020d393e5a84 100644 --- a/sb/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/InspectorPanelController.java +++ b/sb/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/InspectorPanelController.java @@ -223,6 +223,10 @@ public class InspectorPanelController extends AbstractFxmlPanelController { // Charsets for the properties of included elements private Map<String, Charset> availableCharsets; + + private ChangeListener<Number> glossaryRevisionListener = (ov, t, t1) -> { + updateInspector(); + }; /* * Public @@ -519,6 +523,8 @@ public class InspectorPanelController extends AbstractFxmlPanelController { // Listen the Scene stylesheets changes getEditorController().sceneStyleSheetProperty().addListener((ChangeListener<ObservableList<File>>) (ov, t, t1) -> updateInspector()); + getEditorController().getGlossary().revisionProperty().addListener(glossaryRevisionListener); + selectionState = new SelectionState(editorController); viewModeChanged(null, getViewMode()); expandedSectionChanged(); @@ -533,6 +539,12 @@ public class InspectorPanelController extends AbstractFxmlPanelController { searchResultDividerPosition = inspectorRoot.getDividerPositions()[0]; searchPatternDidChange(); } + + @Override + protected void editorControllerChanged(EditorController oldController, EditorController newController) { + oldController.getGlossary().revisionProperty().removeListener(glossaryRevisionListener); + newController.getGlossary().revisionProperty().addListener(glossaryRevisionListener); + } /* * Private diff --git a/sb/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/util/AbstractPanelController.java b/sb/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/util/AbstractPanelController.java index 1d97ce9a7a283910ddbdf11a29f7d337f2e41f40..3d06d4c3f2fa0fa0767e51362f65354ae37b46ca 100644 --- a/sb/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/util/AbstractPanelController.java +++ b/sb/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/util/AbstractPanelController.java @@ -120,9 +120,14 @@ public abstract class AbstractPanelController { if (old != null) { editorSelectionDidChange(); + editorControllerChanged(old, editorController); } } + protected void editorControllerChanged(EditorController oldController, EditorController newController) { + + } + /** * Returns the root FX object of this panel. * When called the first time, this method invokes {@link #makePanel()} diff --git a/sb4e.test/src/no/tobask/sb4e/test/JavaProjectGlossaryTest.java b/sb4e.test/src/no/tobask/sb4e/test/JavaProjectGlossaryTest.java index 485f7609ccf45c1af48a47d3f5a463c37793c24b..8fd0046660542549a5385e3e26706db28d8b0604 100644 --- a/sb4e.test/src/no/tobask/sb4e/test/JavaProjectGlossaryTest.java +++ b/sb4e.test/src/no/tobask/sb4e/test/JavaProjectGlossaryTest.java @@ -3,6 +3,7 @@ package no.tobask.sb4e.test; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.jdt.core.ElementChangedEvent; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.JavaCore; @@ -25,20 +26,14 @@ public class JavaProjectGlossaryTest { JavaProjectGlossary glossary; URL fxmlLocation; IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject("testProject"); - InfoPanelController infoPanelController; @Rule public final ExternalResource projectResource = new JavaProjectResource(project); - @Before - public void setUp() throws Exception { - infoPanelController = mock(InfoPanelController.class); - } - @Test public void discoversCandidates() throws Exception { fxmlLocation = project.getFolder("src").getFile("Test.fxml").getLocationURI().toURL(); - glossary = new JavaProjectGlossary("", fxmlLocation, infoPanelController); + glossary = new JavaProjectGlossary("", fxmlLocation); List<String> controllerSuggestions = glossary.queryControllerClasses(fxmlLocation); assertEquals(1, controllerSuggestions.size()); } @@ -46,10 +41,11 @@ public class JavaProjectGlossaryTest { @Test public void reactsToRemoval() throws Exception { fxmlLocation = project.getFolder("src").getFile("Test.fxml").getLocationURI().toURL(); - glossary = new JavaProjectGlossary("", fxmlLocation, infoPanelController); + glossary = new JavaProjectGlossary("", fxmlLocation); + JavaCore.addElementChangedListener(glossary, ElementChangedEvent.POST_CHANGE); assertEquals(1, glossary.queryControllerClasses(fxmlLocation).size()); getController().delete(true, null); - verify(infoPanelController).resetSuggestedControllerClasses(fxmlLocation); + assertEquals(1, glossary.getRevision());; assertEquals(0, glossary.queryControllerClasses(fxmlLocation).size()); } diff --git a/sb4e/src/no/tobask/sb4e/ControllerCandidateChecker.java b/sb4e/src/no/tobask/sb4e/ControllerCandidateChecker.java deleted file mode 100644 index 52892aa99e40787a434bb90ec7622113962f552c..0000000000000000000000000000000000000000 --- a/sb4e/src/no/tobask/sb4e/ControllerCandidateChecker.java +++ /dev/null @@ -1,54 +0,0 @@ -package no.tobask.sb4e; - -import java.util.List; - -import org.eclipse.jdt.core.dom.ASTVisitor; -import org.eclipse.jdt.core.dom.Annotation; -import org.eclipse.jdt.core.dom.BodyDeclaration; -import org.eclipse.jdt.core.dom.FieldDeclaration; -import org.eclipse.jdt.core.dom.IExtendedModifier; -import org.eclipse.jdt.core.dom.MethodDeclaration; - -import javafx.fxml.FXML; - -public class ControllerCandidateChecker extends ASTVisitor { - - private static final String FXML_ANNOTATION = FXML.class.getSimpleName(); - private boolean hasEmptyConstructor; - private boolean hasFxmlAnnotatedMembers; - - private boolean hasConstructor; - - @Override - public boolean visit(FieldDeclaration node) { - return visitBodyDeclaration(node); - } - - @Override - public boolean visit(MethodDeclaration node) { - if (node.isConstructor()) { - hasConstructor = true; - hasEmptyConstructor = hasEmptyConstructor || node.parameters().size() == 0; - } - return visitBodyDeclaration(node); - } - - @SuppressWarnings("unchecked") - private boolean visitBodyDeclaration(BodyDeclaration node) { - List<IExtendedModifier> modifiers = node.modifiers(); - for (IExtendedModifier modifier : modifiers) { - if (modifier.isAnnotation()) { - String annotationName = ((Annotation) modifier).getTypeName().toString(); - if (annotationName != null && annotationName.equals(FXML_ANNOTATION)) { - hasFxmlAnnotatedMembers = true; - } - } - } - return false; - } - - public boolean visitedCandidate() { - return (hasEmptyConstructor || !hasConstructor) && hasFxmlAnnotatedMembers; - } - -} diff --git a/sb4e/src/no/tobask/sb4e/FxControllerValidator.java b/sb4e/src/no/tobask/sb4e/FxControllerValidator.java index ad81556e3d3aa98f392c8c52ebf2864c15e5668f..e5438781131f590b9a809da22069312b05bf2251 100644 --- a/sb4e/src/no/tobask/sb4e/FxControllerValidator.java +++ b/sb4e/src/no/tobask/sb4e/FxControllerValidator.java @@ -50,8 +50,7 @@ public class FxControllerValidator extends CompilationParticipant { String fxmlContent = FXOMDocument.readContentFromURL(documentLocation); FXOMDocument document = new FXOMDocument(fxmlContent, documentLocation, Activator.getClassLoader(), I18N.getBundle()); - CompilationUnit ast = getAst(clazz); - file.recordNewProblems(getProblems(ast, document)); + file.recordNewProblems(getProblems(clazz, document)); } catch (IOException e) { e.printStackTrace(); } @@ -71,19 +70,28 @@ public class FxControllerValidator extends CompilationParticipant { return true; } - private CategorizedProblem[] getProblems(CompilationUnit ast, FXOMDocument document) { - FxControllerVisitor visitor = new FxControllerVisitor(); + private String getDocumentName(URL url) { + String path = url.getFile(); + int start = path.lastIndexOf("/") + 1; + int end = path.lastIndexOf("."); + return path.substring(start, end); + } + + private CategorizedProblem[] getProblems(ICompilationUnit clazz, FXOMDocument document) { + CompilationUnit ast = getAst(clazz); + String documentName = getDocumentName(document.getLocation()); + FxControllerVisitor visitor = new FxControllerVisitor(documentName, clazz.getElementName()); ast.accept(visitor); - Map<String, String> controllerIds = visitor.getFxIds(); + Map<String, List<String>> controllerIds = visitor.getFxIds(); Map<String, FXOMObject> documentIds = document.collectFxIds(); List<String> missingIds = new ArrayList<>(); - for (Entry<String, FXOMObject> id : documentIds.entrySet()) { - if (!controllerIds.containsKey(id.getKey())) { - FXOMObject fxomObject = id.getValue(); - if (fxomObject instanceof FXOMInstance) { - FXOMInstance instance = (FXOMInstance) fxomObject; - missingIds.add(id.getKey() + ";" + instance.getDeclaredClass().getName()); - } + for (Entry<String, FXOMObject> docId : documentIds.entrySet()) { + FXOMInstance instance = (FXOMInstance) docId.getValue(); + String componentType = instance.getDeclaredClass().getSimpleName(); + String id = docId.getKey(); + List<String> idsForInstanceType = controllerIds.get(componentType); + if (idsForInstanceType == null || !idsForInstanceType.contains(id)) { + missingIds.add(id + ";" + instance.getDeclaredClass().getName()); } } diff --git a/sb4e/src/no/tobask/sb4e/FxControllerVisitor.java b/sb4e/src/no/tobask/sb4e/FxControllerVisitor.java index 468295d23cdda04f96aab38b3367d06d1daad7ae..86c5578843ba6813a6a64aece9e5036babf6e0dd 100644 --- a/sb4e/src/no/tobask/sb4e/FxControllerVisitor.java +++ b/sb4e/src/no/tobask/sb4e/FxControllerVisitor.java @@ -1,66 +1,144 @@ package no.tobask.sb4e; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; - import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.BodyDeclaration; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.IExtendedModifier; import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.SimpleType; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import javafx.beans.value.ObservableValue; +import javafx.event.Event; import javafx.fxml.FXML; public class FxControllerVisitor extends ASTVisitor { - private Map<String, String> fxIds = new HashMap<>(); // var name -> type private static final String FXML_ANNOTATION = FXML.class.getSimpleName(); + private List<String> eventHandlers = new ArrayList<>(); + private Map<String, List<String>> fxIds = new HashMap<>(); + + private boolean hasEmptyConstructor; + private boolean hasConstructor; + private String documentName; + private String className; + public FxControllerVisitor(String documentName, String className) { + this.documentName = documentName; + this.className = className; + } + @Override - public boolean visit(FieldDeclaration node) { - if (isFxIdCandidate(node)) { - Type type = node.getType(); - ITypeBinding typeBinding = type.resolveBinding(); - String typeName; - if (typeBinding != null) { - typeName = typeBinding.getQualifiedName(); - } else { - if (type.isSimpleType()) { - typeName = ((SimpleType) type).getName().getFullyQualifiedName(); - } else { - typeName = "UNKNOWN"; - } - } - List<VariableDeclarationFragment> fragments = node.fragments(); - String variableName = fragments.get(0).getName().toString(); - fxIds.put(variableName, typeName); + public boolean visit(MethodDeclaration node) { + if (node.isConstructor()) { + hasConstructor = true; + hasEmptyConstructor = hasEmptyConstructor || node.parameters().size() == 0; + } + + if ((isPublic(node) || hasFxmlAnnotation(node)) && isValidEventHandler(node)) { + String name = node.getName().getFullyQualifiedName(); + eventHandlers.add(name); } - return super.visit(node); + return false; } + @SuppressWarnings("unchecked") + private boolean isValidEventHandler(MethodDeclaration node) { + List<SingleVariableDeclaration> parameters = node.parameters(); + if (parameters.size() == 0) { + return true; + } else if (parameters.size() == 1) { + SingleVariableDeclaration param = parameters.get(0); + return isSubClassOf(Event.class.getName(), param.getType()); + } else if (parameters.size() == 3) { + SingleVariableDeclaration firstParam = parameters.get(0); + return isSubClassOf(ObservableValue.class.getName(), firstParam.getType()); + } + return false; + } - public Map<String, String> getFxIds() { - return fxIds; + private boolean isPublic(BodyDeclaration node) { + return Modifier.isPublic(node.getModifiers()); } - private boolean isFxIdCandidate(FieldDeclaration field) { - List<IExtendedModifier> modifiers = field.modifiers(); - if (Modifier.isPublic(field.getModifiers())) { - return true; + @Override + public boolean visit(FieldDeclaration node) { + if ((isPublic(node) || hasFxmlAnnotation(node)) && isSubClassOf("javafx", node.getType())) { + addToIds(node); } - boolean hasFxmlAnnotation = false; + return true; + } + + @SuppressWarnings("unchecked") + private void addToIds(FieldDeclaration node) { + Type type = node.getType(); + if (type.isSimpleType()) { + String typeName = ((SimpleType) type).getName().getFullyQualifiedName(); + List<VariableDeclarationFragment> fragments = node.fragments(); + String varName = fragments.get(0).getName().getFullyQualifiedName(); + List<String> prevIds = fxIds.putIfAbsent(typeName, + new ArrayList<>(Arrays.asList(varName))); + if (prevIds != null) { + prevIds.add(varName); + } + } + } + + @SuppressWarnings("unchecked") + private boolean hasFxmlAnnotation(BodyDeclaration node) { + List<IExtendedModifier> modifiers = node.modifiers(); for (IExtendedModifier modifier : modifiers) { if (modifier.isAnnotation()) { - String annotationName = ((Annotation) modifier).getTypeName().toString(); - hasFxmlAnnotation = hasFxmlAnnotation || annotationName.equals(FXML_ANNOTATION); + String annotationName = ((Annotation) modifier).getTypeName().getFullyQualifiedName(); + if (annotationName != null && annotationName.equals(FXML_ANNOTATION)) { + return true; + } } } - return hasFxmlAnnotation; + return false; + } + + private boolean isSubClassOf(String superClass, Type classType) { + if (classType.isPrimitiveType()) { + return false; + } + ITypeBinding binding = classType.resolveBinding(); + if (binding != null) { + ITypeBinding objectType = classType.getAST().resolveWellKnownType("java.lang.Object"); + ITypeBinding type = binding; + while (type.getSuperclass() != objectType) { + type = type.getSuperclass(); + if (type.getQualifiedName().startsWith(superClass)) { + return true; + } + } + } + return false; } + public List<String> getEventHandlers() { + return new ArrayList<>(eventHandlers); + } + + public Map<String, List<String>> getFxIds() { + return new HashMap<>(fxIds); + } + + public boolean isCandidate() { + boolean hasFxMembers = eventHandlers.size() > 0 || fxIds.size() > 0; + boolean contentMatch = (hasEmptyConstructor || !hasConstructor) && hasFxMembers; + boolean nameMatch = className.startsWith(documentName); + return contentMatch || nameMatch; + } + } diff --git a/sb4e/src/no/tobask/sb4e/JavaProjectGlossary.java b/sb4e/src/no/tobask/sb4e/JavaProjectGlossary.java index de54d8fc8a3f5d3a5b03a036067c003d2a7bba2b..fa8864210e4d6c3880138eae32dde926aa94f71f 100644 --- a/sb4e/src/no/tobask/sb4e/JavaProjectGlossary.java +++ b/sb4e/src/no/tobask/sb4e/JavaProjectGlossary.java @@ -2,7 +2,6 @@ package no.tobask.sb4e; import java.net.URL; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -10,45 +9,29 @@ import java.util.stream.Collectors; import org.eclipse.jdt.core.ElementChangedEvent; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IElementChangedListener; -import org.eclipse.jdt.core.IField; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaElementDelta; import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.ILocalVariable; -import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IPackageFragment; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; -import com.oracle.javafx.scenebuilder.app.info.InfoPanelController; import com.oracle.javafx.scenebuilder.kit.glossary.Glossary; -import javafx.beans.value.ObservableValue; -import javafx.event.Event; -import javafx.fxml.FXML; - public class JavaProjectGlossary extends Glossary implements IElementChangedListener { - - private static final String FXML_ANNOTATION = FXML.class.getSimpleName(); private String qualifiedControllerName; private URL fxmlLocation; - private InfoPanelController infoPanelController; - private Map<String, Map<String, List<String>>> fxIds = new HashMap<>(); - private Map<String, List<String>> eventHandlers = new HashMap<>(); + private Map<String, List<String>> fxIds; + private List<String> eventHandlers; private List<ICompilationUnit> candidateControllers; - public JavaProjectGlossary(String controllerClassName, URL fxmlLocation, - InfoPanelController infoPanelController) { + public JavaProjectGlossary(String controllerClassName, URL fxmlLocation) { this.qualifiedControllerName = controllerClassName; this.fxmlLocation = fxmlLocation; - this.infoPanelController = infoPanelController; - JavaCore.addElementChangedListener(this, ElementChangedEvent.POST_CHANGE); } @Override @@ -69,85 +52,91 @@ public class JavaProjectGlossary extends Glossary implements IElementChangedList @Override public List<String> queryFxIds(URL fxmlLocation, String controllerClass, Class<?> targetType) { - if (qualifiedControllerName == null || !qualifiedControllerName.equals(controllerClass)) { - qualifiedControllerName = controllerClass; + String previousController = qualifiedControllerName; + qualifiedControllerName = controllerClass; + if (qualifiedControllerName == null) { + return new ArrayList<>(); } - Map<String, List<String>> controllerFxIds = fxIds.get(qualifiedControllerName); - if (controllerFxIds == null) { + if (!previousController.equals(qualifiedControllerName) || fxIds == null) { ICompilationUnit controller = JavaModelUtils.getClass(fxmlLocation, qualifiedControllerName); - controllerFxIds = controller == null ? - new HashMap<>() : getFxIds(controller); - fxIds.put(qualifiedControllerName, controllerFxIds); + fxIds = controller == null ? new HashMap<>() : getFxIds(controller); } String typeName = Signature.getSimpleName(targetType.getName()); - List<String> ids = controllerFxIds.getOrDefault(typeName, new ArrayList<>()); + List<String> ids = fxIds.getOrDefault(typeName, new ArrayList<>()); return new ArrayList<>(ids); } @Override public List<String> queryEventHandlers(URL fxmlLocation, String controllerClass) { - if (qualifiedControllerName == null || !qualifiedControllerName.equals(controllerClass)) { - qualifiedControllerName = controllerClass; + String previousController = qualifiedControllerName; + qualifiedControllerName = controllerClass; + if (qualifiedControllerName == null) { + return new ArrayList<>(); } - List<String> controllerEventHandlers = eventHandlers.get(qualifiedControllerName); - if (controllerEventHandlers == null) { + if (!previousController.equals(qualifiedControllerName) || eventHandlers == null) { ICompilationUnit controller = JavaModelUtils.getClass(fxmlLocation, qualifiedControllerName); - controllerEventHandlers = controller == null ? + eventHandlers = controller == null ? new ArrayList<>() : getEventHandlers(controller); - eventHandlers.put(qualifiedControllerName, controllerEventHandlers); } - return new ArrayList<>(controllerEventHandlers); + return new ArrayList<>(eventHandlers); } @Override public void elementChanged(ElementChangedEvent event) { IJavaElementDelta delta = getClassChangedDelta(event.getDelta()); + boolean revised = false; if (delta != null) { ICompilationUnit affectedClass = (ICompilationUnit) delta.getElement(); if (fxmlLocation != null && inSameProject(affectedClass)) { - if (updateControllerCandidates(delta)) { - infoPanelController.resetSuggestedControllerClasses(fxmlLocation); + List<ICompilationUnit> prevCandidates = new ArrayList<>(candidateControllers); + updateControllerCandidates(delta); + if (!prevCandidates.equals(candidateControllers)) { + revised = true; } if (delta.getKind() != IJavaElementDelta.REMOVED && qualifiedControllerName != null && JavaModelUtils.getQualifiedName(affectedClass).equals(qualifiedControllerName)) { - fxIds.put(qualifiedControllerName, getFxIds(affectedClass)); - eventHandlers.put(qualifiedControllerName, getEventHandlers(affectedClass)); + Map<String, List<String>> prevFxIds = fxIds; + fxIds = getFxIds(affectedClass); + List<String> prevEventHandlers = eventHandlers; + eventHandlers = getEventHandlers(affectedClass); + if (!fxIds.equals(prevFxIds) || !eventHandlers.equals(prevEventHandlers)) { + revised = true; + } } } } + if (revised) { + incrementRevision(); + } } - private boolean updateControllerCandidates(IJavaElementDelta delta) { - boolean updated = false; + private void updateControllerCandidates(IJavaElementDelta delta) { ICompilationUnit compUnit = (ICompilationUnit) delta.getElement(); if (delta.getKind() == IJavaElementDelta.REMOVED) { if (candidateControllers.contains(compUnit)) { candidateControllers.remove(compUnit); - updated = true; } } else { CompilationUnit ast = delta.getCompilationUnitAST(); CompilationUnit clazz = ast != null ? ast : getAst(compUnit); - ControllerCandidateChecker checker = new ControllerCandidateChecker(); + FxControllerVisitor checker = new FxControllerVisitor(getDocumentName(fxmlLocation), + compUnit.getElementName()); clazz.accept(checker); - boolean isCandidateController = checker.visitedCandidate(); - if (candidateControllers.contains(compUnit)) { - if (!isCandidateController) { - candidateControllers.remove(compUnit); - updated = true; - } - } else if (isCandidateController) { + boolean isCandidateController = checker.isCandidate(); + + if (isCandidateController && !candidateControllers.contains(compUnit)) { candidateControllers.add(compUnit); - updated = true; + } else if (!isCandidateController && candidateControllers.contains(compUnit)) { + candidateControllers.remove(compUnit); } } - return updated; } private CompilationUnit getAst(ICompilationUnit source) { ASTParser parser = ASTParser.newParser(AST.JLS9); parser.setSource(source); + parser.setResolveBindings(true); return (CompilationUnit) parser.createAST(null); } @@ -168,7 +157,7 @@ public class JavaProjectGlossary extends Glossary implements IElementChangedList private List<ICompilationUnit> getCandidateControllers(IPackageFragment pkg) { ASTParser parser = ASTParser.newParser(AST.JLS9); - Requestor requestor = new Requestor(); + Requestor requestor = new Requestor(getDocumentName(fxmlLocation)); try { parser.createASTs(pkg.getCompilationUnits(), null, requestor, null); return requestor.getCandidates(); @@ -177,77 +166,28 @@ public class JavaProjectGlossary extends Glossary implements IElementChangedList return new ArrayList<>(); } } + + private String getDocumentName(URL url) { + String path = url.getFile(); + int start = path.lastIndexOf("/") + 1; + int end = path.lastIndexOf("."); + return path.substring(start, end); + } private List<String> getEventHandlers(ICompilationUnit controller) { - List<String> eventHandlers = new ArrayList<>(); - IType type = controller.findPrimaryType(); - try { - for (IMethod method : type.getMethods()) { - if (isEventHandler(method)) { - eventHandlers.add(method.getElementName()); - } - } - } catch (JavaModelException e) { - e.printStackTrace(); - } - return eventHandlers; - } - - private boolean isEventHandler(IMethod method) throws JavaModelException { - boolean isFxmlAnnotated = method.getAnnotation(FXML_ANNOTATION).exists(); - boolean returnsVoid = method.getReturnType().equals(Signature.SIG_VOID); - if (isFxmlAnnotated && returnsVoid) { - // valid ones are: - // 1) zero parameters - // 2) one parameter of javafx.event type - // 3) three parameters, where the first one is of ObservableValue type - ILocalVariable[] parameters = method.getParameters(); - if (parameters.length == 0) { - return true; - } else if (parameters.length == 1) { - return isSubclass(parameters[0], Event.class); - } else if (parameters.length == 3) { - return isSubclass(parameters[0], ObservableValue.class); - } - } - return false; - } - - private boolean isSubclass(ILocalVariable parameter, Class<?> clazz) throws JavaModelException { - String simpleName = Signature.getSignatureSimpleName(parameter.getTypeSignature()); - IType declaringType = parameter.getDeclaringMember().getDeclaringType(); - String[][] resolvedNames = declaringType.resolveType(simpleName); - if (resolvedNames != null && resolvedNames.length > 0) { - String superClassName = resolvedNames[0][0]; - return superClassName.equals(Signature.getQualifier(clazz.getName())); - } else { - return false; - } + CompilationUnit compUnit = getAst(controller); + FxControllerVisitor checker = new FxControllerVisitor(getDocumentName(fxmlLocation), + controller.getElementName()); + compUnit.accept(checker); + return checker.getEventHandlers(); } private Map<String, List<String>> getFxIds(ICompilationUnit controller) { - Map<String, List<String>> ids = new HashMap<>(); - IType type = controller.findPrimaryType(); - try { - for (IField field : type.getFields()) { - if (field.getAnnotation(FXML_ANNOTATION).exists()) { - addToIds(ids, field); - } - } - } catch (JavaModelException e) { - e.printStackTrace(); - } - return ids; - } - - private void addToIds(Map<String, List<String>> ids, IField field) throws JavaModelException { - String typeName = Signature.getSignatureSimpleName(field.getTypeSignature()); - String fieldName = field.getElementName(); - List<String> existingIds = ids.putIfAbsent(typeName, - new ArrayList<>(Arrays.asList(fieldName))); - if (existingIds != null) { - existingIds.add(fieldName); - } + CompilationUnit compUnit = getAst(controller); + FxControllerVisitor checker = new FxControllerVisitor(getDocumentName(fxmlLocation), + controller.getElementName()); + compUnit.accept(checker); + return checker.getFxIds(); } } diff --git a/sb4e/src/no/tobask/sb4e/Requestor.java b/sb4e/src/no/tobask/sb4e/Requestor.java index 6d3db506eaa2c74521911d113ccbe13e99f46192..44e2e5f384aa7106f31cfadc1b3670db5bef52d3 100644 --- a/sb4e/src/no/tobask/sb4e/Requestor.java +++ b/sb4e/src/no/tobask/sb4e/Requestor.java @@ -10,12 +10,17 @@ import org.eclipse.jdt.core.dom.CompilationUnit; public class Requestor extends ASTRequestor { private List<ICompilationUnit> candidates = new ArrayList<>(); + private String documentName; + + public Requestor(String documentName) { + this.documentName = documentName; + } @Override public void acceptAST(ICompilationUnit source, CompilationUnit ast) { - ControllerCandidateChecker checker = new ControllerCandidateChecker(); - ast.accept(checker); - if (checker.visitedCandidate()) { + FxControllerVisitor visitor = new FxControllerVisitor(documentName, source.getElementName()); + ast.accept(visitor); + if (visitor.isCandidate()) { candidates.add(source); } } diff --git a/sb4e/src/no/tobask/sb4e/editors/FXMLEditor.java b/sb4e/src/no/tobask/sb4e/editors/FXMLEditor.java index d8a499391170b57be525ba04bdbb3267a3207b03..5ee21dcce3895d6f033c5eadff71f414ec8c070c 100644 --- a/sb4e/src/no/tobask/sb4e/editors/FXMLEditor.java +++ b/sb4e/src/no/tobask/sb4e/editors/FXMLEditor.java @@ -15,6 +15,7 @@ import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.ElementChangedEvent; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IField; import org.eclipse.jdt.core.ISourceRange; @@ -147,9 +148,9 @@ public class FXMLEditor extends EditorPart { if (root != null) { controllerName = root.getFxController(); } - glossary = new JavaProjectGlossary(controllerName, fxmlUrl, - editorWindowController.infoPanelController); + glossary = new JavaProjectGlossary(controllerName, fxmlUrl); editorController.setGlossary(glossary); + JavaCore.addElementChangedListener(glossary, ElementChangedEvent.POST_CHANGE); copyHandler = new SceneBuilderControlActionHandler(editorController, ControlAction.COPY); cutHandler = new SceneBuilderEditActionHandler(editorController, EditAction.CUT);