blob: d1ac7f4f118e58b520bc5d1a3ddd9c60131b1650 [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 com.sun.source.tree.Tree;
import com.sun.source.util.TreePath;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.ResourceBundle;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.swing.event.ChangeListener;
import org.netbeans.api.fileinfo.NonRecursiveFolder;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.source.CompilationInfo;
import org.netbeans.api.java.source.ElementHandle;
import org.netbeans.api.java.source.SourceUtils;
import org.netbeans.api.java.source.TreePathHandle;
import org.netbeans.api.java.source.TreeUtilities;
import org.netbeans.api.queries.VisibilityQuery;
import org.netbeans.modules.refactoring.api.AbstractRefactoring;
import org.netbeans.modules.refactoring.api.Problem;
import org.netbeans.modules.refactoring.api.SafeDeleteRefactoring;
import org.netbeans.modules.refactoring.api.ui.ExplorerContext;
import org.netbeans.modules.refactoring.java.RefactoringUtils;
import org.netbeans.modules.refactoring.java.api.JavaRefactoringUtils;
import org.netbeans.modules.refactoring.spi.ui.CustomRefactoringPanel;
import org.netbeans.modules.refactoring.spi.ui.RefactoringUI;
import org.netbeans.modules.refactoring.spi.ui.RefactoringUIBypass;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileSystem;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataFolder;
import org.openide.loaders.DataObject;
import org.openide.util.*;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup;
/**
* A CustomRefactoringUI subclass that represents Safe Delete
* @author Bharath Ravikumar
*/
public class SafeDeleteUI implements RefactoringUI, RefactoringUIBypass, JavaRefactoringUIFactory {
private SafeDeleteRefactoring refactoring;
private Object[] elementsToDelete;
private SafeDeletePanel panel;
private ResourceBundle bundle;
private boolean regulardelete = false;
private Lookup lookup;
/**
* Creates a new instance of SafeDeleteUI
* @param selectedFiles An array of selected FileObjects that need to be
* safely deleted
* @param handles
*/
private SafeDeleteUI(FileObject[] selectedFiles, Collection<TreePathHandle> handles, boolean regulardelete) {
this.elementsToDelete = selectedFiles;
refactoring = new SafeDeleteRefactoring(new ProxyLookup(Lookups.fixed(elementsToDelete), Lookups.fixed(handles.toArray(new Object[handles.size()]))));
refactoring.getContext().add(JavaRefactoringUtils.getClasspathInfoFor(selectedFiles));
this.regulardelete = regulardelete;
}
/**
* Creates a new instance of SafeDeleteUI
* @param selectedElements An array of selected Elements that need to be
* safely deleted
*/
private SafeDeleteUI(TreePathHandle[] selectedElements) {
this.elementsToDelete = selectedElements;
refactoring = new SafeDeleteRefactoring(Lookups.fixed(elementsToDelete));
refactoring.getContext().add(RefactoringUtils.getClasspathInfoFor(selectedElements[0]));
}
private SafeDeleteUI(NonRecursiveFolder nonRecursiveFolder, boolean regulardelete) {
refactoring = new SafeDeleteRefactoring(Lookups.fixed(nonRecursiveFolder));
refactoring.getContext().add(JavaRefactoringUtils.getClasspathInfoFor(nonRecursiveFolder.getFolder()));
this.regulardelete = regulardelete;
}
private SafeDeleteUI(Lookup lookup) {
this.lookup = lookup;
}
/**
* Delegates to the fastCheckParameters of the underlying
* refactoring
* @return Returns the result of fastCheckParameters of the
* underlying refactoring
*/
@Override
public org.netbeans.modules.refactoring.api.Problem checkParameters() {
refactoring.setCheckInComments(panel.isSearchInComments());
return refactoring.fastCheckParameters();
}
@Override
public String getDescription() {
//TODO: Check bounds here. Might throw an OutofBoundsException otherwise.
// if (elementsToDelete[0] instanceof JavaClass) {
// return getString("DSC_SafeDelClasses", elementsToDelete);// NOI18N
// } else {
// if (elementsToDelete[0] instanceof ExecutableElement) {
// if (elementsToDelete.length > 1)
// return getString("DSC_SafeDelMethods");// NOI18N
// else
// return getString("DSC_SafeDelMethod", elementsToDelete[0]);// NOI18N
// }
//
// }
// if(elementsToDelete[0] instanceof Resource){
// return NbBundle.getMessage(SafeDeleteUI.class, "DSC_SafeDel",
// ((Resource)elementsToDelete[0]).getName()); // NOI18N
// }
NonRecursiveFolder folder = refactoring.getRefactoringSource().lookup(NonRecursiveFolder.class);
if (folder != null) {
return NbBundle.getMessage(SafeDeleteUI.class, "DSC_SafeDelPkg", folder.getFolder().getNameExt().replace('/', '.')); // NOI18N
}
return NbBundle.getMessage(SafeDeleteUI.class, "DSC_SafeDel", elementsToDelete); // NOI18N
}
@Override
public org.openide.util.HelpCtx getHelpCtx() {
return new HelpCtx("org.netbeans.modules.refactoring.java.ui.SafeDeleteUI"); // NOI18N
}
@Override
public String getName() {
return NbBundle.getMessage(SafeDeleteUI.class, "LBL_SafeDel"); // NOI18N
}
@Override
public CustomRefactoringPanel getPanel(ChangeListener parent) {
//TODO:Do you want to just use Arrays.asList?
if(panel == null) {
panel = new SafeDeletePanel(refactoring, regulardelete, parent);
}
return panel;
}
@Override
public AbstractRefactoring getRefactoring() {
return refactoring;
}
@Override
public boolean hasParameters() {
return true;
}
/**
* Returns false, since this refactoring is not a query.
* @return false
*/
@Override
public boolean isQuery() {
return false;
}
@Override
public Problem setParameters() {
refactoring.setCheckInComments(panel.isSearchInComments());
return refactoring.checkParameters();
}
//Helper methods------------------
@Override
public boolean isRefactoringBypassRequired() {
return panel.isRegularDelete();
}
@Override
public void doRefactoringBypass() throws IOException {
RequestProcessor.getDefault().post(new Runnable() {
@Override
public void run() {
try {
// #172199
FileUtil.runAtomicAction(new FileSystem.AtomicAction() {
@Override
public void run() throws IOException {
for (FileObject file : getRefactoring().getRefactoringSource().lookupAll(FileObject.class)) {
if(file.isValid()) {
DataObject.find(file).delete();
}
}
NonRecursiveFolder f = (NonRecursiveFolder) getRefactoring().getRefactoringSource().lookup(NonRecursiveFolder.class);
if (f != null) {
deletePackage(f.getFolder());
}
}
});
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
});
}
private void deletePackage(FileObject source) {
ClassPath classPath = ClassPath.getClassPath(source, ClassPath.SOURCE);
FileObject root = classPath != null ? classPath.findOwnerRoot(source) : null;
DataFolder dataFolder = DataFolder.findFolder(source);
FileObject parent = dataFolder.getPrimaryFile().getParent();
// First; delete all files except packages
try {
DataObject ch[] = dataFolder.getChildren();
boolean empty = true;
for (int i = 0; ch != null && i < ch.length; i++) {
if (!ch[i].getPrimaryFile().isFolder()) {
ch[i].delete();
}
else if (empty && VisibilityQuery.getDefault().isVisible(ch[i].getPrimaryFile())) {
// 156529: hidden folders should be considered as empty content
empty = false;
}
}
// If empty delete itself
if ( empty ) {
dataFolder.delete();
}
// Second; delete empty super packages, or empty folders when there is not root
while (!parent.equals(root) && parent.getChildren().length == 0) {
FileObject newParent = parent.getParent();
parent.delete();
parent = newParent;
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
@Override
public RefactoringUI create(CompilationInfo info, TreePathHandle[] handles, FileObject[] files, NonRecursiveFolder[] packages) {
final boolean b = lookup.lookup(ExplorerContext.class)!=null;
if (packages != null && packages.length == 1) {
return new SafeDeleteUI(packages[0], b);
}
if (handles != null && handles.length == 0 || (files!=null && files.length > 1)) {
return new SafeDeleteUI(files, Arrays.asList(handles), b);
}
if (b && files!=null && files.length == 1) {
return new SafeDeleteUI(files, Arrays.asList(handles), b);
}
if (info == null) {
return new SafeDeleteUI(handles);
}
TreePathHandle selectedElement = handles[0];
Element selected = selectedElement.resolveElement(info);
TreePath selectedTree = selectedElement.resolve(info);
if (selected == null || selectedTree == null) {
return null;
}
if (selected.getKind() == ElementKind.PACKAGE || selected.getEnclosingElement().getKind() == ElementKind.PACKAGE) {
ElementHandle<Element> handle = ElementHandle.create(selected);
FileObject file = SourceUtils.getFile(handle, info.getClasspathInfo());
if (file == null) {
return null;
}
if (file.getName().equals(selected.getSimpleName().toString())) {
return new SafeDeleteUI(new FileObject[]{file}, Collections.singleton(selectedElement), b);
}
}
if(!TreeUtilities.CLASS_TREE_KINDS.contains(selectedTree.getParentPath().getLeaf().getKind())
&& selectedTree.getParentPath().getLeaf().getKind() != Tree.Kind.COMPILATION_UNIT
&& selectedTree.getLeaf().getKind() == Tree.Kind.VARIABLE) {
switch (selectedTree.getParentPath().getLeaf().getKind()) {
case BLOCK:
case METHOD:
break;
default:
return null;
}
}
return new SafeDeleteUI(new TreePathHandle[]{selectedElement});
}
public static JavaRefactoringUIFactory factory(Lookup lookup) {
return new SafeDeleteUI(lookup);
}
}