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

Reply via email to