blob: 106cd5e42032a37827e370ea33f986e07b3ab2e3 [file] [log] [blame]
/*
* 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);
}
}