Skip to content
Snippets Groups Projects
Commit e3df57bb authored by Tobias Ask's avatar Tobias Ask
Browse files

Merge branch '10-update-controller-name-suggestions-on-changes-in-the-workspace' into 'master'

Resolve "Update controller name suggestions on changes in the workspace"

Closes #10

See merge request !8
parents 829272cc a6536da4
Branches
No related tags found
1 merge request!8Resolve "Update controller name suggestions on changes in the workspace"
Pipeline #
...@@ -468,7 +468,7 @@ public class InfoPanelController extends AbstractFxmlPanelController { ...@@ -468,7 +468,7 @@ public class InfoPanelController extends AbstractFxmlPanelController {
private final ChangeListener<Boolean> checkBoxListener = (ov, t, t1) -> toggleFxRoot(); private final ChangeListener<Boolean> checkBoxListener = (ov, t, t1) -> toggleFxRoot();
private void resetSuggestedControllerClasses(URL location) { public void resetSuggestedControllerClasses(URL location) {
if (controllerClassEditor != null) { if (controllerClassEditor != null) {
// The listener on fxmlLocationProperty is called before the file // The listener on fxmlLocationProperty is called before the file
// denoted by the location is created on disk, hence the runLater. // denoted by the location is created on disk, hence the runLater.
......
package no.tobask.sb4e.examples;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class Controller {
@FXML
Label lbl;
}
...@@ -24,6 +24,7 @@ Import-Package: com.oracle.javafx.scenebuilder.app.selectionbar, ...@@ -24,6 +24,7 @@ Import-Package: com.oracle.javafx.scenebuilder.app.selectionbar,
org.eclipse.e4.core.di.annotations;version="1.6.0", org.eclipse.e4.core.di.annotations;version="1.6.0",
org.eclipse.e4.ui.di, org.eclipse.e4.ui.di,
org.eclipse.jdt.core, org.eclipse.jdt.core,
org.eclipse.jdt.core.dom,
org.eclipse.jdt.launching org.eclipse.jdt.launching
Bundle-ActivationPolicy: lazy Bundle-ActivationPolicy: lazy
Bundle-ClassPath: . Bundle-ClassPath: .
......
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;
}
}
...@@ -3,14 +3,10 @@ package no.tobask.sb4e; ...@@ -3,14 +3,10 @@ package no.tobask.sb4e;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Stack;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.core.ElementChangedEvent; import org.eclipse.jdt.core.ElementChangedEvent;
import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IElementChangedListener; import org.eclipse.jdt.core.IElementChangedListener;
...@@ -25,7 +21,10 @@ import org.eclipse.jdt.core.IType; ...@@ -25,7 +21,10 @@ import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature; 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 com.oracle.javafx.scenebuilder.kit.glossary.Glossary;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
...@@ -34,25 +33,35 @@ import javafx.fxml.FXML; ...@@ -34,25 +33,35 @@ import javafx.fxml.FXML;
public class JavaProjectGlossary extends Glossary implements IElementChangedListener { public class JavaProjectGlossary extends Glossary implements IElementChangedListener {
private static final String FXML_ANNOTATION = FXML.class.getSimpleName();
private String controllerClassName; private String controllerClassName;
private URL fxmlLocation;
private InfoPanelController infoPanelController;
private Map<String, List<String>> fxIds; private Map<String, List<String>> fxIds;
private List<String> eventHandlers; private List<String> eventHandlers;
private static final String FXML_ANNOTATION = FXML.class.getSimpleName(); private List<ICompilationUnit> candidateControllers;
public JavaProjectGlossary(String controllerClassName) { public JavaProjectGlossary(String controllerClassName, URL fxmlLocation,
InfoPanelController infoPanelController) {
this.controllerClassName = controllerClassName; this.controllerClassName = controllerClassName;
JavaCore.addElementChangedListener(this, ElementChangedEvent.POST_CHANGE); this.fxmlLocation = fxmlLocation;
this.infoPanelController = infoPanelController;
JavaCore.addElementChangedListener(this);
} }
@Override @Override
public List<String> queryControllerClasses(URL fxmlLocation) { public List<String> queryControllerClasses(URL fxmlLocation) {
if (candidateControllers == null) {
IJavaProject project = JavaModelUtils.getJavaProjectFromUrl(fxmlLocation); IJavaProject project = JavaModelUtils.getJavaProjectFromUrl(fxmlLocation);
String packageName = JavaModelUtils.getPackageContainingFile(fxmlLocation).getElementName(); String packageName = JavaModelUtils.getPackageContainingFile(fxmlLocation).getElementName();
List<ICompilationUnit> candidates = new ArrayList<>(); candidateControllers = new ArrayList<>();
for (IPackageFragment pkg : JavaModelUtils.getAllMatchingPackages(packageName, project)) { for (IPackageFragment pkg : JavaModelUtils.getAllMatchingPackages(packageName, project)) {
candidates.addAll(getCandidateControllers(pkg, fxmlLocation)); candidateControllers.addAll(getCandidateControllers(pkg));
}
} }
return candidates.stream().map(c -> c.getElementName()).collect(Collectors.toList()); return candidateControllers.stream().map(c -> c.getElementName()).collect(Collectors.toList());
} }
@Override @Override
...@@ -82,60 +91,81 @@ public class JavaProjectGlossary extends Glossary implements IElementChangedList ...@@ -82,60 +91,81 @@ public class JavaProjectGlossary extends Glossary implements IElementChangedList
@Override @Override
public void elementChanged(ElementChangedEvent event) { public void elementChanged(ElementChangedEvent event) {
if (controllerClassName != null) { IJavaElementDelta delta = getClassChangedDelta(event.getDelta());
ICompilationUnit controllerClass = getControllerClass(event); if (delta != null) {
if (controllerClass != null) { ICompilationUnit affectedClass = (ICompilationUnit) delta.getElement();
fxIds = getFxIds(controllerClass); if (fxmlLocation != null && inSameProject(affectedClass)) {
eventHandlers = getEventHandlers(controllerClass); if (updateControllerCandidates(delta)) {
infoPanelController.resetSuggestedControllerClasses(fxmlLocation);
}
if (controllerClassName != null && affectedClass.getElementName()
.equals(controllerClassName) && delta.getKind() != IJavaElementDelta.REMOVED) {
fxIds = getFxIds(affectedClass);
eventHandlers = getEventHandlers(affectedClass);
}
} }
} }
} }
private Collection<ICompilationUnit> getCandidateControllers(IPackageFragment pkg, private boolean updateControllerCandidates(IJavaElementDelta delta) {
URL fxmlLocation) { boolean updated = false;
try { ICompilationUnit compUnit = (ICompilationUnit) delta.getElement();
List<ICompilationUnit> allUnits = new ArrayList<>(Arrays.asList(pkg. if (delta.getKind() == IJavaElementDelta.REMOVED) {
getCompilationUnits())); if (candidateControllers.contains(compUnit)) {
Stream<ICompilationUnit> matches = allUnits.stream().filter(u candidateControllers.remove(compUnit);
-> isCandidateControllerClass(u, fxmlLocation)); updated = true;
return matches.collect(Collectors.toList());
} catch (JavaModelException e) {
e.printStackTrace();
return new ArrayList<>();
} }
} else {
CompilationUnit ast = delta.getCompilationUnitAST();
CompilationUnit clazz = ast != null ? ast : getAst(compUnit);
ControllerCandidateChecker checker = new ControllerCandidateChecker();
clazz.accept(checker);
boolean isCandidateController = checker.visitedCandidate();
if (candidateControllers.contains(compUnit)) {
if (!isCandidateController) {
candidateControllers.remove(compUnit);
updated = true;
}
} else if (isCandidateController) {
candidateControllers.add(compUnit);
updated = true;
}
}
return updated;
} }
private boolean isCandidateControllerClass(ICompilationUnit javaClass, URL fxmlLocation) { private CompilationUnit getAst(ICompilationUnit source) {
IType type = javaClass.findPrimaryType(); ASTParser parser = ASTParser.newParser(AST.JLS9);
return hasNoOrEmptyConstructor(type) && hasFxmlAnnotatedMembers(type); parser.setSource(source);
return (CompilationUnit) parser.createAST(null);
} }
private boolean hasFxmlAnnotatedMembers(IType type) { private IJavaElementDelta getClassChangedDelta(IJavaElementDelta rootDelta) {
try { IJavaElementDelta delta = rootDelta;
List<IMethod> methods = new ArrayList<>(Arrays.asList(type.getMethods())); IJavaElement element = delta.getElement();
List<IField> fields = new ArrayList<>(Arrays.asList(type.getFields())); while (delta.getAffectedChildren().length > 0 &&
boolean anyMethodsAnnotated = methods.stream().anyMatch(m -> !(element.getElementType() == IJavaElement.COMPILATION_UNIT)) {
m.getAnnotation(FXML_ANNOTATION).exists()); delta = delta.getAffectedChildren()[0];
boolean anyFieldsAnnotated = fields.stream().anyMatch(f -> element = delta.getElement();
f.getAnnotation(FXML_ANNOTATION).exists());
return anyFieldsAnnotated || anyMethodsAnnotated;
} catch (JavaModelException e) {
e.printStackTrace();
return false;
} }
return element.getElementType() == IJavaElement.COMPILATION_UNIT ? delta : null;
} }
private boolean hasNoOrEmptyConstructor(IType type) { private boolean inSameProject(ICompilationUnit clazz) {
try { return clazz.getJavaProject().equals(JavaModelUtils.getJavaProjectFromUrl(fxmlLocation));
for (IMethod method : type.getMethods()) {
if (method.isConstructor()) {
return method.getParameters().length == 0;
}
} }
private List<ICompilationUnit> getCandidateControllers(IPackageFragment pkg) {
ASTParser parser = ASTParser.newParser(AST.JLS9);
Requestor requestor = new Requestor();
try {
parser.createASTs(pkg.getCompilationUnits(), null, requestor, null);
return requestor.getCandidates();
} catch (JavaModelException e) { } catch (JavaModelException e) {
e.printStackTrace(); e.printStackTrace();
return new ArrayList<>();
} }
return true;
} }
private List<String> getEventHandlers(ICompilationUnit controller) { private List<String> getEventHandlers(ICompilationUnit controller) {
...@@ -206,22 +236,4 @@ public class JavaProjectGlossary extends Glossary implements IElementChangedList ...@@ -206,22 +236,4 @@ public class JavaProjectGlossary extends Glossary implements IElementChangedList
} }
} }
private ICompilationUnit getControllerClass(ElementChangedEvent event) {
IJavaElementDelta rootDelta = event.getDelta();
Stack<IJavaElementDelta> queue = new Stack<>();
queue.push(rootDelta);
while (!queue.isEmpty()) {
IJavaElementDelta delta = queue.pop();
IJavaElement element = delta.getElement();
if (element instanceof ICompilationUnit &&
((ICompilationUnit) element).getElementName().equals(controllerClassName)) {
return (ICompilationUnit) element;
}
for (IJavaElementDelta childDelta : delta.getAffectedChildren()) {
queue.push(childDelta);
}
}
return null;
}
} }
package no.tobask.sb4e;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.dom.ASTRequestor;
import org.eclipse.jdt.core.dom.CompilationUnit;
public class Requestor extends ASTRequestor {
private List<ICompilationUnit> candidates = new ArrayList<>();
@Override
public void acceptAST(ICompilationUnit source, CompilationUnit ast) {
ControllerCandidateChecker checker = new ControllerCandidateChecker();
ast.accept(checker);
if (checker.visitedCandidate()) {
candidates.add(source);
}
}
public List<ICompilationUnit> getCandidates() {
return new ArrayList<>(candidates);
}
}
...@@ -155,7 +155,8 @@ public class FXMLEditor extends EditorPart { ...@@ -155,7 +155,8 @@ public class FXMLEditor extends EditorPart {
e.printStackTrace(); e.printStackTrace();
} }
String controllerName = editorController.getFxomDocument().getFxomRoot().getFxController(); String controllerName = editorController.getFxomDocument().getFxomRoot().getFxController();
editorController.setGlossary(new JavaProjectGlossary(controllerName)); editorController.setGlossary(new JavaProjectGlossary(controllerName, fxmlUrl,
editorWindowController.infoPanelController));
canvas.setScene(editorWindowController.getScene()); canvas.setScene(editorWindowController.getScene());
editorController.getSelection().revisionProperty().addListener(editorSelectionListener); editorController.getSelection().revisionProperty().addListener(editorSelectionListener);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment