JaroslavTulach commented on a change in pull request #3798:
URL: https://github.com/apache/netbeans/pull/3798#discussion_r829329110
##########
File path:
java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/MoveRefactoring.java
##########
@@ -116,101 +135,354 @@
@Override
@NbBundle.Messages({
"DN_DefaultPackage=<default package>",
- "DN_SelectTargetPackage=Select target package",
"DN_CreateNewClass=<create new class>",
- "DN_SelectTargetClass=Select target class",
})
public CompletableFuture<Object> processCommand(NbCodeLanguageClient
client, String command, List<Object> arguments) {
try {
if (arguments.size() > 0) {
String uri = gson.fromJson(gson.toJson(arguments.get(0)),
String.class);
- QuickPickItem elementItem = arguments.size() > 1 ?
gson.fromJson(gson.toJson(arguments.get(1)), QuickPickItem.class) : null;
FileObject file = Utils.fromUri(uri);
- Project project = FileOwnerQuery.getOwner(file);
- HashSet<QuickPickItem> items = new HashSet<>();
- if (project != null) {
- for(SourceGroup sourceGroup :
ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA))
{
- String name = sourceGroup.getDisplayName();
- FileObject rootFolder = sourceGroup.getRootFolder();
- if (elementItem == null) {
- items.add(new
QuickPickItem(Bundle.DN_DefaultPackage(), name, null, false,
Utils.toUri(rootFolder)));
- }
- for (String packageName :
ClasspathInfo.create(rootFolder).getClassIndex().getPackageNames("", false,
EnumSet.of(ClassIndex.SearchScope.SOURCE))) {
- if (elementItem == null) {
- String pkg = "";
- for (String part : packageName.split("\\.")) {
- if (!part.isEmpty()) {
- pkg += pkg.length() == 0 ? part : "."
+ part;
- items.add(new QuickPickItem(pkg, name,
null, false, Utils.toUri(rootFolder.getFileObject(pkg.replace('.', '/')))));
+ JavaSource js = JavaSource.forFileObject(file);
+ if (js != null) {
+ return CompletableFuture.supplyAsync(() -> {
+ try {
+ js.runUserActionTask(ci -> {
+ ci.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
+ if (arguments.size() > 1) {
+ Element element =
gson.fromJson(gson.toJson(arguments.get(1)), ElementData.class).resolve(ci);
+ if (element != null) {
+ if (element.getKind().isClass() ||
element.getKind().isInterface()) {
+ Pages.showMoveClassUI(ci, client,
file, element);
+ } else {
+ Pages.showMoveMembersUI(ci,
client, file, element);
+ }
}
+ } else {
+ Pages.showMoveClassUI(ci, client, file,
null);
}
- } else {
- items.add(new QuickPickItem(packageName, name,
null, false, Utils.toUri(rootFolder.getFileObject(packageName.replace('.',
'/')))));
- }
+ }, true);
+ return null;
+ } catch (IOException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }, RequestProcessor.getDefault());
+ }
+ } else {
+ throw new IllegalArgumentException(String.format("Illegal
number of arguments received for command: %s", command));
+ }
+ } catch (JsonSyntaxException | IllegalArgumentException |
MalformedURLException ex) {
+ client.showMessage(new MessageParams(MessageType.Error,
ex.getLocalizedMessage()));
+ }
+ return CompletableFuture.completedFuture(true);
+ }
+
+ @HTMLDialog(url = "ui/MoveClass.html")
+ static HTMLDialog.OnSubmit showMoveClassUI(
+ CompilationController ci,
+ NbCodeLanguageClient client,
+ FileObject file,
+ Element element
+ ) {
+ MoveElementUI model = new MoveElementUI();
+ model.withMoveClass(true)
+ .withFrom(file.getName())
+ .assignData(client, file, element != null ?
TreePathHandle.create(element, ci) : null);
+ model.applyBindings();
+ return (id) -> {
+ if ("accept".equals(id)) {
+ model.doRefactoring();
+ }
+ return true; // return false, if validation fails
+ };
+ }
+
+ @HTMLDialog(url = "ui/MoveMembers.html")
+ static HTMLDialog.OnSubmit showMoveMembersUI(
+ CompilationController ci,
+ NbCodeLanguageClient client,
+ FileObject file,
+ Element element
+ ) {
+ ElementUtilities eu = ci.getElementUtilities();
+ Element enclosingElement = element.getEnclosingElement();
+ String parentName = createLabel(ci, enclosingElement);
+ ElementUI[] members = enclosingElement.getEnclosedElements().stream()
+ .filter(memberElement -> (memberElement.getKind().isField() ||
memberElement.getKind() == ElementKind.METHOD) &&
!eu.isSynthetic(memberElement))
+ .map(memberElement -> {
+ String label = createLabel(ci, memberElement);
+ ElementData data = new ElementData(memberElement);
+ ElementUI memberElementUI = new ElementUI(memberElement ==
element, label, memberElement.getKind().name(), data.getSignature());
+ return memberElementUI;
+ }).toArray(ElementUI[]::new);
+ MoveElementUI model = new MoveElementUI();
+ model.withMoveClass(false)
+ .withFrom(parentName)
+ .withMembers(members)
+ .withKeepMethodSelected(false)
+ .withDeprecateMethodSelected(true)
+ .assignData(client, file, TreePathHandle.create(element, ci));
+ model.applyBindings();
+ return (id) -> {
+ if ("accept".equals(id)) {
+ model.doRefactoring();
+ }
+ return true; // return false, if validation fails
+ };
+ }
+
+ @Model(className = "MoveElementUI", targetId = "", instance = true,
builder = "with", properties = {
+ @Property(name = "moveClass", type = boolean.class),
+ @Property(name = "from", type = String.class),
+ @Property(name = "selectedProject", type = NamedPath.class),
+ @Property(name = "selectedRoot", type = NamedPath.class),
+ @Property(name = "selectedPackage", type = String.class),
+ @Property(name = "selectedClass", type = ElementUI.class),
+ @Property(name = "selectedVisibility", type = Visibility.class),
+ @Property(name = "selectedJavaDoc", type = JavaDoc.class),
+ @Property(name = "members", type = ElementUI.class, array = true),
+ @Property(name = "keepMethodSelected", type = boolean.class),
+ @Property(name = "deprecateMethodSelected", type = boolean.class)
+ })
+ static final class MoveElementControl {
+
+ private NbCodeLanguageClient client;
+ private FileObject file;
+ private TreePathHandle handle;
+
+ @ModelOperation
+ void assignData(MoveElementUI ui, NbCodeLanguageClient client,
FileObject file, TreePathHandle handle) {
+ this.client = client;
+ this.file = file;
+ this.handle = handle;
+ }
+
+ @ComputedProperty
+ static List<NamedPath> availableProjects() {
+ Project[] openProjects =
OpenProjects.getDefault().getOpenProjects();
+ List<NamedPath> projectNames = new
ArrayList<>(openProjects.length);
+ for (int i = 0; i < openProjects.length; i++) {
+ projectNames.add(new
NamedPath(ProjectUtils.getInformation(openProjects[i]).getDisplayName(),
Utils.toUri(openProjects[i].getProjectDirectory())));
+ }
+ return projectNames;
+ }
+
+ @ComputedProperty
+ static List<NamedPath> availableRoots(NamedPath selectedProject) {
+ Project project = getSelectedProject(selectedProject);
+ if (project != null) {
+ Sources sources = ProjectUtils.getSources(project);
+ SourceGroup[] groups =
sources.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
+ List<NamedPath> projectRoots = new ArrayList<>(groups.length);
+ for (int i = 0; i < groups.length; i++) {
+ projectRoots.add(new NamedPath(groups[i].getDisplayName(),
Utils.toUri(groups[i].getRootFolder())));
+ }
+ return projectRoots;
+ }
+ return Collections.emptyList();
+ }
+
+ @ComputedProperty
+ static List<String> availablePackages(boolean moveClass, NamedPath
selectedRoot) {
+ FileObject rootFolder = getSelectedRoot(selectedRoot);
+ if (rootFolder != null) {
+ List<String> packages;
+ if (moveClass) {
+ packages = new ArrayList<>();
+ packages.add(Bundle.DN_DefaultPackage());
+ Enumeration<? extends FileObject> children =
rootFolder.getChildren(true);
+ while (children.hasMoreElements()) {
+ FileObject child = children.nextElement();
+ if (child.isFolder()) {
+ packages.add(FileUtil.getRelativePath(rootFolder,
child).replace('/', '.'));
}
}
+ } else {
+ packages =
ClasspathInfo.create(rootFolder).getClassIndex().getPackageNames("", false,
EnumSet.of(ClassIndex.SearchScope.SOURCE)).stream().collect(Collectors.toList());
}
- ArrayList<QuickPickItem> packages = new ArrayList<>(items);
- Collections.sort(packages, (item1, item2) -> {
- int i =
item1.getDescription().compareTo(item2.getDescription());
- return i == 0 ?
item1.getLabel().compareTo(item2.getLabel()) : i;
+ packages.sort((s1, s2) -> s1.compareTo(s2));
+ return packages;
+ }
+ return Collections.emptyList();
+ }
+
+ @ComputedProperty
+ static List<ElementUI> availableClasses(boolean moveClass, NamedPath
selectedRoot, String selectedPackage) {
+ FileObject rootFolder = getSelectedRoot(selectedRoot);
+ if (rootFolder != null && selectedPackage != null) {
+ FileObject fo =
rootFolder.getFileObject(selectedPackage.replace('.', '/'));
+ ClassPath sourcePath = ClassPath.getClassPath(fo,
ClassPath.SOURCE);
+ final ClasspathInfo info = ClasspathInfo.create(EMPTY_PATH,
EMPTY_PATH, sourcePath);
+ Set<ClassIndex.SearchScopeType> searchScopeType = new
HashSet<>(1);
+ final Set<String> packageSet =
Collections.singleton(selectedPackage);
+ searchScopeType.add(new ClassIndex.SearchScopeType() {
+ @Override
+ public Set<? extends String> getPackages() {
+ return packageSet;
+ }
+
+ @Override
+ public boolean isSources() {
+ return true;
+ }
+
+ @Override
+ public boolean isDependencies() {
+ return false;
+ }
});
- Consumer<List<QuickPickItem>> f = selectedPackage -> {
- if (selectedPackage != null && !selectedPackage.isEmpty())
{
- ClasspathInfo info = ClasspathInfo.create(file);
- TreePathHandle tph = elementItem != null ?
TreePathHandle.from(gson.fromJson(gson.toJson(elementItem.getUserData()),
ElementData.class).toHandle(), info) : null;
- List<QuickPickItem> classes =
packageClasses(selectedPackage.get(0), tph == null || tph.getKind() ==
Tree.Kind.CLASS);
- if (classes.isEmpty()) {
- if (tph == null) {
- move(client, uri, selectedPackage.get(0),
ClasspathInfo.create(file));
- } else {
- throw new
IllegalArgumentException(String.format("No target class found in selected
package"));
- }
+ List<ElementUI> ret = new ArrayList<>();
+ if (moveClass) {
+ ret.add(new ElementUI(false, Bundle.DN_CreateNewClass(),
null));
+ }
+ for (ElementHandle<TypeElement> eh :
info.getClassIndex().getDeclaredTypes("", ClassIndex.NameKind.PREFIX,
searchScopeType)) {
+ ElementData data = new ElementData(eh);
+ String shortName =
eh.getQualifiedName().substring(selectedPackage.length() + 1);
+ int idx = shortName.indexOf('.');
+ if (fo.getFileObject(idx < 0 ? shortName :
shortName.substring(0, idx), "java") != null) {
+ ret.add(new ElementUI(false, shortName,
data.getKind(), data.getSignature()));
+ }
+ }
+ ret.sort((e1, e2) -> e1.getLabel().compareTo(e2.getLabel()));
+ return ret;
+ }
+ return Collections.emptyList();
+ }
+
+ @ComputedProperty
+ static List<Visibility> availableVisibilities() {
+ return Arrays.asList(Visibility.values());
+ }
+
+ @ComputedProperty
+ static List<JavaDoc> availableJavaDoc() {
+ return Arrays.asList(JavaDoc.values());
+ }
+
+ @ModelOperation
+ @Function
+ void doRefactoring(MoveElementUI ui) {
+ try {
+ org.netbeans.modules.refactoring.api.MoveRefactoring
refactoring;
+ if (handle == null) {
+ refactoring = new
org.netbeans.modules.refactoring.api.MoveRefactoring(Lookups.fixed(file));
+
refactoring.getContext().add(JavaRefactoringUtils.getClasspathInfoFor(file));
+ } else {
+ InstanceContent ic = new InstanceContent();
+ refactoring = new
org.netbeans.modules.refactoring.api.MoveRefactoring(new AbstractLookup(ic));
+ List<TreePathHandle> selectedElements =
ui.getMembers().stream().filter(member ->
member.isSelected()).map(selectedMember -> {
+ ElementHandle memberHandle =
ElementHandleAccessor.getInstance().create(ElementKind.valueOf(selectedMember.getKind()),
selectedMember.getSignature().toArray(new
String[selectedMember.getSignature().size()]));
+ return TreePathHandle.from(memberHandle,
ClasspathInfo.create(file));
+ }).collect(Collectors.toList());
+ ic.set(selectedElements, null);
+ if (handle.getKind() == Tree.Kind.CLASS) {
+
refactoring.getContext().add(JavaRefactoringUtils.getClasspathInfoFor(handle.getFileObject()));
+ } else {
+ JavaMoveMembersProperties properties = new
JavaMoveMembersProperties(selectedElements.toArray(new
TreePathHandle[selectedElements.size()]));
+
properties.setVisibility(JavaMoveMembersProperties.Visibility.valueOf(ui.getSelectedVisibility().name()));
+ properties.setDelegate(ui.isKeepMethodSelected());
+ properties.setUpdateJavaDoc(ui.getSelectedJavaDoc() ==
JavaDoc.UPDATE);
+
properties.setAddDeprecated(ui.isDeprecateMethodSelected());
+ refactoring.getContext().add(properties);
+ }
+ }
+ ElementUI selectedClass = ui.getSelectedClass();
+ if (selectedClass != null) {
+ if (selectedClass.getKind() != null &&
selectedClass.getSignature() != null) {
+ ElementHandle eh =
ElementHandleAccessor.getInstance().create(ElementKind.valueOf(selectedClass.getKind()),
selectedClass.getSignature().toArray(new
String[selectedClass.getSignature().size()]));
+
refactoring.setTarget(Lookups.singleton(TreePathHandle.from(eh,
ClasspathInfo.create(file))));
+ } else {
+ FileObject rootFolder =
getSelectedRoot(ui.getSelectedRoot());
+ if (rootFolder != null && ui.getSelectedPackage()!=
null) {
+
refactoring.setTarget(Lookups.singleton(rootFolder.getFileObject(ui.getSelectedPackage().replace('.',
'/')).toURL()));
} else {
- client.showQuickPick(new
ShowQuickPickParams(Bundle.DN_SelectTargetClass(), false,
classes)).thenAccept(selectedClass -> {
- if (selectedClass != null &&
!selectedClass.isEmpty()) {
- QuickPickItem selected =
Bundle.DN_CreateNewClass().equals(selectedClass.get(0).getLabel()) ?
selectedPackage.get(0) : selectedClass.get(0);
- move(client, tph != null ? tph : uri,
selected, info);
- }
- });
+ refactoring.setTarget(Lookup.EMPTY);
}
}
- };
- if (packages.size() == 1) {
- f.accept(packages);
} else {
- client.showQuickPick(new
ShowQuickPickParams(Bundle.DN_SelectTargetPackage(), false,
packages)).thenAccept(f);
+ refactoring.setTarget(Lookup.EMPTY);
+ }
+ client.applyEdit(new
ApplyWorkspaceEditParams(perform(refactoring, "Move")));
+ } catch (Exception ex) {
+ if (client == null) {
+ Exceptions.printStackTrace(
+ Exceptions.attachSeverity(ex, Level.SEVERE)
+ );
+ } else {
+ client.showMessage(new MessageParams(MessageType.Error,
ex.getLocalizedMessage()));
}
- } else {
- throw new IllegalArgumentException(String.format("Illegal
number of arguments received for command: %s", command));
}
- } catch (Exception ex) {
- client.showMessage(new MessageParams(MessageType.Error,
ex.getLocalizedMessage()));
}
- return CompletableFuture.completedFuture(true);
- }
- private void move(NbCodeLanguageClient client, Object source,
QuickPickItem target, ClasspathInfo info) {
- try {
- org.netbeans.modules.refactoring.api.MoveRefactoring refactoring;
- if (source instanceof String) {
- FileObject file = Utils.fromUri((String) source);
- refactoring = new
org.netbeans.modules.refactoring.api.MoveRefactoring(Lookups.fixed(file));
-
refactoring.getContext().add(JavaRefactoringUtils.getClasspathInfoFor(file));
- } else {
- TreePathHandle tph = (TreePathHandle) source;
- refactoring = new
org.netbeans.modules.refactoring.api.MoveRefactoring(Lookups.fixed(tph));
- refactoring.getContext().add(tph.getKind() == Tree.Kind.CLASS
? JavaRefactoringUtils.getClasspathInfoFor(tph.getFileObject()) : new
JavaMoveMembersProperties(tph));
+ private static Project getSelectedProject(NamedPath selectedProject) {
+ try {
+ String path = selectedProject.getPath();
+ return path != null ?
FileOwnerQuery.getOwner(Utils.fromUri(path)) : null;
+ } catch (MalformedURLException ex) {
+ return null;
}
- if (target.getDescription() != null) {
- refactoring.setTarget(Lookups.singleton(new URL((String)
target.getUserData())));
- } else {
- ElementHandle handle =
gson.fromJson(gson.toJson(target.getUserData()), ElementData.class).toHandle();
-
refactoring.setTarget(Lookups.singleton(TreePathHandle.from(handle, info)));
+ }
+
+ private static FileObject getSelectedRoot(NamedPath selectedRoot) {
+ try {
+ String path = selectedRoot.getPath();
+ return path != null ? Utils.fromUri(path) : null;
+ } catch (MalformedURLException ex) {
+ return null;
}
- client.applyEdit(new ApplyWorkspaceEditParams(perform(refactoring,
"Move")));
- } catch (Exception ex) {
- client.showMessage(new MessageParams(MessageType.Error,
ex.getLocalizedMessage()));
+ }
+ }
+
+ @Model(className = "ElementUI", instance = true, properties = {
+ @Property(name = "selected", type = boolean.class),
Review comment:
Safer in the presence of
[NETBEANS-6478](https://issues.apache.org/jira/browse/NETBEANS-6478).
##########
File path:
java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
##########
@@ -3648,9 +3649,14 @@ public void showMessage(MessageParams params) {
}
@Override
- public CompletableFuture<List<QuickPickItem>>
showQuickPick(ShowQuickPickParams params) {
- List<QuickPickItem> items = params.getItems();
- return CompletableFuture.completedFuture("Select target
package".equals(params.getPlaceHolder()) ? items.subList(2, 3) :
items.subList(0, 1));
+ public CompletableFuture<String> showHtmlPage(HtmlPageParams
params) {
+ MoveElementUI ui =
MockHtmlViewer.assertDialogShown(params.getUri(), MoveElementUI.class);
Review comment:
Great test.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists