| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.netbeans.modules.refactoring.java.ui; |
| |
| import java.awt.EventQueue; |
| import java.awt.datatransfer.Transferable; |
| import java.util.*; |
| import javax.swing.Action; |
| import javax.swing.JEditorPane; |
| import javax.swing.SwingUtilities; |
| import javax.swing.text.BadLocationException; |
| import javax.swing.text.Document; |
| import javax.swing.text.JTextComponent; |
| import org.netbeans.api.java.classpath.ClassPath; |
| import org.netbeans.api.java.source.JavaSource; |
| import org.netbeans.api.java.source.SourceUtils; |
| import org.netbeans.api.java.source.TreePathHandle; |
| import org.netbeans.editor.Utilities; |
| import org.netbeans.modules.refactoring.api.ui.ExplorerContext; |
| import org.netbeans.modules.refactoring.api.ui.RefactoringActionsFactory; |
| import org.netbeans.modules.refactoring.java.RefactoringUtils; |
| import org.netbeans.modules.refactoring.java.api.JavaRefactoringUtils; |
| import org.netbeans.modules.refactoring.java.plugins.InstantRefactoringPerformer; |
| import org.netbeans.modules.refactoring.spi.ui.ActionsImplementationProvider; |
| import org.openide.cookies.EditorCookie; |
| import org.openide.filesystems.FileObject; |
| import org.openide.filesystems.FileUtil; |
| import org.openide.loaders.DataFolder; |
| import org.openide.loaders.DataObject; |
| import org.openide.nodes.Node; |
| import org.openide.util.Exceptions; |
| import org.openide.util.Lookup; |
| import org.openide.util.NbBundle; |
| import org.openide.util.datatransfer.PasteType; |
| import static org.netbeans.modules.refactoring.java.ui.Bundle.*; |
| |
| |
| /** |
| * |
| * @author Jan Becicka |
| */ |
| @NbBundle.Messages({"WARN_CannotPerformHere=Cannot perform rename here."}) |
| @org.openide.util.lookup.ServiceProvider(service=org.netbeans.modules.refactoring.spi.ui.ActionsImplementationProvider.class, position=100) |
| public class RefactoringActionsProvider extends ActionsImplementationProvider{ |
| |
| private static boolean INSTANT = Boolean.getBoolean("org.netbeans.modules.java.refactoring.instantRename"); |
| |
| /** Creates a new instance of RefactoringActionsProvider */ |
| public RefactoringActionsProvider() { |
| } |
| |
| @Override |
| public void doRename(final Lookup lookup) { |
| final EditorCookie ec = lookup.lookup(EditorCookie.class); |
| if(INSTANT && RefactoringUtils.isFromEditor(ec)) { |
| invokeInstantRename(lookup, ec); |
| } else { |
| doFullRename(lookup); |
| } |
| } |
| |
| private void invokeInstantRename(final Lookup lookup, final EditorCookie ec) { |
| try { |
| JEditorPane target = ec.getOpenedPanes()[0]; |
| final int caret = target.getCaretPosition(); |
| String ident = Utilities.getIdentifier(Utilities.getDocument(target), caret); |
| |
| if (ident == null) { |
| Utilities.setStatusBoldText(target, WARN_CannotPerformHere()); |
| return; |
| } |
| |
| DataObject od = (DataObject) target.getDocument().getProperty(Document.StreamDescriptionProperty); |
| JavaSource js = od != null ? JavaSource.forFileObject(od.getPrimaryFile()) : null; |
| |
| if (js == null) { |
| Utilities.setStatusBoldText(target, WARN_CannotPerformHere()); |
| return; |
| } |
| InstantRefactoringUI ui = InstantRefactoringUIImpl.create(js, caret); |
| |
| if (ui != null) { |
| if (ui.getRegions().isEmpty() || ui.getKeyStroke() == null) { |
| doFullRename(lookup); |
| } else { |
| doInstantRename(target, js, caret, ui); |
| } |
| } else { |
| Utilities.setStatusBoldText(target, WARN_CannotPerformHere()); |
| } |
| } catch (BadLocationException e) { |
| Exceptions.printStackTrace(e); |
| } |
| } |
| |
| public static void doInstantRename(JTextComponent target, JavaSource js, int caretOffset, InstantRefactoringUI ui) throws BadLocationException { |
| new InstantRefactoringPerformer(target, caretOffset, ui); |
| } |
| |
| private void doFullRename(final Lookup lookup) { |
| final Runnable task = ContextAnalyzer.createTask(lookup, RenameRefactoringUI.factory(lookup)); |
| if(!EventQueue.isDispatchThread()) { |
| SwingUtilities.invokeLater(new Runnable() { |
| |
| @Override |
| public void run() { |
| UIUtilities.runWhenScanFinished(task, getActionName(RefactoringActionsFactory.renameAction())); |
| } |
| }); |
| } else { |
| UIUtilities.runWhenScanFinished(task, getActionName(RefactoringActionsFactory.renameAction())); |
| } |
| } |
| |
| static String getActionName(Action action) { |
| String arg = (String) action.getValue(Action.NAME); |
| arg = arg.replace("&", ""); // NOI18N |
| return arg.replace("...", ""); // NOI18N |
| } |
| |
| |
| /** |
| * returns true if exactly one refactorable file is selected |
| */ |
| @Override |
| public boolean canRename(Lookup lookup) { |
| Collection<? extends Node> nodes = new HashSet<Node>(lookup.lookupAll(Node.class)); |
| if (nodes.size() != 1) { |
| return false; |
| } |
| Node n = nodes.iterator().next(); |
| TreePathHandle tph = n.getLookup().lookup(TreePathHandle.class); |
| if (tph != null) { |
| return JavaRefactoringUtils.isRefactorable(tph.getFileObject()); |
| } |
| DataObject dob = n.getCookie(DataObject.class); |
| if (dob==null) { |
| return false; |
| } |
| FileObject fo = dob.getPrimaryFile(); |
| if (JavaRefactoringUtils.isRefactorable(fo)) { //NOI18N |
| return true; |
| } |
| if ((dob instanceof DataFolder) && |
| RefactoringUtils.isFileInOpenProject(fo) && |
| JavaRefactoringUtils.isOnSourceClasspath(fo) && |
| !RefactoringUtils.isClasspathRoot(fo)) { |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public void doCopy(final Lookup lookup) { |
| Runnable task = ContextAnalyzer.createTask(lookup, CopyClassRefactoringUI.factory(lookup)); |
| UIUtilities.runWhenScanFinished(task, getActionName(RefactoringActionsFactory.copyAction())); |
| } |
| |
| /** |
| * returns true if there is at least one java file in the selection |
| * and all java files are refactorable |
| */ |
| @Override |
| public boolean canCopy(Lookup lookup) { |
| Collection<? extends Node> nodes = new HashSet<Node>(lookup.lookupAll(Node.class)); |
| FileObject fo = getTarget(lookup); |
| if (fo != null) { |
| if (!fo.isFolder()) { |
| return false; |
| } |
| if (!JavaRefactoringUtils.isOnSourceClasspath(fo)) { |
| return false; |
| } |
| } |
| boolean result = true; |
| boolean hasJava = false; |
| for (Node n:nodes) { |
| DataObject dob = n.getLookup().lookup(DataObject.class); |
| if (dob == null || dob.getPrimaryFile().isFolder()) { |
| result = false; |
| break; |
| } |
| if (RefactoringUtils.isJavaFile(dob.getPrimaryFile())) { |
| hasJava = true; |
| if(ClassPath.getClassPath(dob.getPrimaryFile(), ClassPath.SOURCE) == null) { |
| result = false; |
| break; |
| } |
| } |
| } |
| return result && hasJava; |
| } |
| |
| @Override |
| public boolean canFindUsages(Lookup lookup) { |
| Collection<? extends Node> nodes = new HashSet<Node>(lookup.lookupAll(Node.class)); |
| if (nodes.size() != 1) { |
| return false; |
| } |
| Node n = nodes.iterator().next(); |
| TreePathHandle handle; |
| if ((handle = n.getLookup().lookup(TreePathHandle.class)) != null && handle.getFileObject() != null) { |
| return true; |
| } |
| DataObject dob = n.getLookup().lookup(DataObject.class); |
| if ((dob!=null) && RefactoringUtils.isJavaFile(dob.getPrimaryFile()) && !"package-info".equals(dob.getName())) { //NOI18N |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public void doFindUsages(Lookup lookup) { |
| Runnable task = ContextAnalyzer.createTask(lookup, WhereUsedQueryUI.factory()); |
| task.run(); |
| } |
| |
| /** |
| * Returns true if all selected file are refactorable java files |
| **/ |
| |
| @Override |
| public boolean canDelete(Lookup lookup) { |
| if (SourceUtils.isScanInProgress()) { |
| return false; |
| } |
| Collection<? extends Node> nodes = new HashSet<Node>(lookup.lookupAll(Node.class)); |
| //We live with a 2 pass validation of the selected nodes for now since |
| //the code will become unreadable if we attempt to implement all checks |
| //in one pass. |
| if (isSelectionHeterogeneous(nodes)) { |
| return false; |
| } |
| for (Node n:nodes) { |
| TreePathHandle tph = n.getLookup().lookup(TreePathHandle.class); |
| if (tph != null) { |
| return JavaRefactoringUtils.isRefactorable(tph.getFileObject()); |
| } |
| DataObject dataObject = n.getCookie(DataObject.class); |
| if (dataObject == null){ |
| return false; |
| } |
| FileObject fileObject = dataObject.getPrimaryFile(); |
| if (isRefactorableFolder(dataObject)){ |
| return true; |
| } |
| if (!JavaRefactoringUtils.isRefactorable(fileObject)) { |
| return false; |
| } |
| } |
| return !nodes.isEmpty(); |
| } |
| |
| @Override |
| public void doDelete(final Lookup lookup) { |
| Runnable task = ContextAnalyzer.createTask(lookup, SafeDeleteUI.factory(lookup)); |
| UIUtilities.runWhenScanFinished(task, getActionName(RefactoringActionsFactory.safeDeleteAction())); |
| } |
| |
| public static FileObject getTarget(Lookup look) { |
| ExplorerContext drop = look.lookup(ExplorerContext.class); |
| if (drop==null) { |
| return null; |
| } |
| Node n = drop.getTargetNode(); |
| if (n==null) { |
| return null; |
| } |
| DataObject dob = n.getCookie(DataObject.class); |
| if (dob!=null) { |
| return dob.getPrimaryFile(); |
| } |
| return null; |
| } |
| |
| public static PasteType getPaste(Lookup look) { |
| ExplorerContext drop = look.lookup(ExplorerContext.class); |
| if (drop==null) { |
| return null; |
| } |
| Transferable orig = drop.getTransferable(); |
| if (orig==null) { |
| return null; |
| } |
| Node n = drop.getTargetNode(); |
| if (n==null) { |
| return null; |
| } |
| PasteType[] pt = n.getPasteTypes(orig); |
| if (pt.length==1) { |
| return null; |
| } |
| return pt[1]; |
| } |
| |
| static String getName(Lookup look) { |
| ExplorerContext ren = look.lookup(ExplorerContext.class); |
| if (ren==null) { |
| return null; |
| } |
| return ren.getNewName(); //NOI18N |
| } |
| |
| /** |
| * returns true if there is at least one java file in the selection |
| * and all java files are refactorable |
| */ |
| @Override |
| public boolean canMove(Lookup lookup) { |
| Collection<? extends Node> nodes = new HashSet<Node>(lookup.lookupAll(Node.class)); |
| ExplorerContext drop = lookup.lookup(ExplorerContext.class); |
| FileObject fo = getTarget(lookup); |
| if (fo != null) { |
| if (!fo.isFolder()) { |
| return false; |
| } |
| if (!JavaRefactoringUtils.isOnSourceClasspath(fo)) { |
| return false; |
| } |
| |
| //it is drag and drop |
| Set<DataFolder> folders = new HashSet<DataFolder>(); |
| boolean jdoFound = false; |
| for (Node n:nodes) { |
| DataObject dob = n.getCookie(DataObject.class); |
| if (dob==null) { |
| return false; |
| } |
| if (!JavaRefactoringUtils.isOnSourceClasspath(dob.getPrimaryFile())) { |
| return false; |
| } |
| if (dob instanceof DataFolder) { |
| if (FileUtil.getRelativePath(dob.getPrimaryFile(), fo)!=null) { |
| return false; |
| } |
| folders.add((DataFolder)dob); |
| } else if (RefactoringUtils.isJavaFile(dob.getPrimaryFile())) { |
| jdoFound = true; |
| } |
| } |
| if (jdoFound) { |
| return true; |
| } |
| for (DataFolder fold:folders) { |
| for (Enumeration<DataObject> e = (fold).children(true); e.hasMoreElements();) { |
| if (RefactoringUtils.isJavaFile(e.nextElement().getPrimaryFile())) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } else { |
| //regular invokation |
| boolean result = false; |
| nodesloop: |
| for (Node n:nodes) { |
| DataObject dob = n.getCookie(DataObject.class); |
| if (dob==null) { |
| return false; |
| } |
| if (dob instanceof DataFolder) { |
| if (drop==null) { |
| return false; |
| } else { |
| //Ctrl-X |
| if (!JavaRefactoringUtils.isOnSourceClasspath(dob.getPrimaryFile()) || RefactoringUtils.isClasspathRoot(dob.getPrimaryFile())) { |
| return false; |
| } else { |
| LinkedList<DataFolder> folders = new LinkedList<DataFolder>(); |
| folders.add((DataFolder) dob); |
| while (!folders.isEmpty()) { |
| DataFolder fold = folders.remove(); |
| for (Enumeration<DataObject> e = fold.children(true); e.hasMoreElements();) { |
| if (RefactoringUtils.isJavaFile(e.nextElement().getPrimaryFile())) { |
| result = true; |
| continue nodesloop; |
| } else if (e instanceof DataFolder) { |
| folders.add((DataFolder) e); |
| } |
| } |
| } |
| } |
| } |
| } |
| if (!JavaRefactoringUtils.isOnSourceClasspath(dob.getPrimaryFile())) { |
| return false; |
| } |
| if (RefactoringUtils.isJavaFile(dob.getPrimaryFile())) { |
| result = true; |
| } |
| } |
| return result; |
| } |
| } |
| |
| @Override |
| public void doMove(final Lookup lookup) { |
| Runnable task = ContextAnalyzer.createTask(lookup, MoveClassUI.factory(lookup)); |
| UIUtilities.runWhenScanFinished(task, getActionName(RefactoringActionsFactory.moveAction())); |
| } |
| private static boolean isSelectionHeterogeneous(Collection<? extends Node> nodes) { |
| boolean folderSelected = false; |
| boolean nonFolderNodeSelected = false; |
| for (Node node : nodes) { |
| DataObject dataObject = node.getCookie(DataObject.class); |
| if (dataObject == null) { |
| continue; |
| } |
| if (isRefactorableFolder(dataObject)) { |
| if (folderSelected || nonFolderNodeSelected) { |
| return true; |
| } else { |
| folderSelected = true; |
| } |
| } else { |
| nonFolderNodeSelected = true; |
| } |
| } |
| |
| return false; |
| } |
| |
| private static boolean isRefactorableFolder(DataObject dataObject) { |
| FileObject fileObject = dataObject.getPrimaryFile(); |
| if (/* |
| * #159628 |
| */!Boolean.TRUE.equals(fileObject.getAttribute("isRemoteAndSlow"))) { // NOI18N |
| FileObject[] children = fileObject.getChildren(); |
| if (children == null || children.length <= 0) { |
| return false; |
| } |
| } |
| |
| return (dataObject instanceof DataFolder) |
| && RefactoringUtils.isFileInOpenProject(fileObject) |
| && JavaRefactoringUtils.isOnSourceClasspath(fileObject) |
| && !RefactoringUtils.isClasspathRoot(fileObject); |
| } |
| |
| |
| } |