blob: 5ff0c41133bd01bd6fc872abba2d6e9b007da160 [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.form.refactoring;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import org.netbeans.api.fileinfo.NonRecursiveFolder;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.source.CancellableTask;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.TreePathHandle;
import org.netbeans.modules.refactoring.api.AbstractRefactoring;
import org.netbeans.modules.refactoring.api.MoveRefactoring;
import org.netbeans.modules.refactoring.api.Problem;
import org.netbeans.modules.refactoring.api.RenameRefactoring;
import org.netbeans.modules.refactoring.api.SafeDeleteRefactoring;
import org.netbeans.modules.refactoring.api.SingleCopyRefactoring;
import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
import org.netbeans.modules.refactoring.spi.RefactoringPlugin;
import org.netbeans.modules.refactoring.spi.RefactoringPluginFactory;
import org.openide.filesystems.FileObject;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
/**
* Entry point for refactoring, registered in META-INF/services. Whenever
* a refactoring is about to start, createInstance is called where we analyze
* the type of refactoring and attach RefactoringInfo object to the refactoring,
* which can be later accessed from various places where we prepare or perform
* the additional changes in forms and resources.
*
* @author Tomas Pavek
*/
@org.openide.util.lookup.ServiceProvider(service=org.netbeans.modules.refactoring.spi.RefactoringPluginFactory.class)
public class RefactoringPluginFactoryImpl implements RefactoringPluginFactory {
@Override
public RefactoringPlugin createInstance(AbstractRefactoring refactoring) {
RefactoringInfo.ChangeType changeType = null;
List<FileObject> fileList = new LinkedList<FileObject>();
List<String> nameList = new LinkedList<String>();
// We must do some more analysis here, though it would be better to do
// it later in the plugin's prepare method, but we can't be sure the
// guarded handler is not called sooner from java plugin than our plugin.
if (refactoring instanceof RenameRefactoring) {
FileObject primaryFile = null;
String oldName = null;
Lookup sourceLookup = refactoring.getRefactoringSource();
FileObject file = sourceLookup.lookup(FileObject.class);
NonRecursiveFolder pkgFolder = sourceLookup.lookup(NonRecursiveFolder.class);
final TreePathHandle tpHandle = sourceLookup.lookup(TreePathHandle.class);
// assumption: if file is being renamed (even as result of renaming
// a class) then file != null, and if something inside the class
// is renamed then file == null
if (file != null && RefactoringInfo.isJavaFile(file)) {
// renaming a java file within the same package
// (can be a form, or a component used in a form, or both)
if (isOnSourceClasspath(file)) {
changeType = RefactoringInfo.ChangeType.CLASS_RENAME;
primaryFile = file;
oldName = file.getName();
}
} else if (file == null && tpHandle != null) {
// renaming an element inside a java file
primaryFile = tpHandle.getFileObject();
if (RefactoringInfo.isJavaFileOfForm(primaryFile)) {
JavaSource source = JavaSource.forFileObject(tpHandle.getFileObject());
final RefactoringInfo.ChangeType[] changeTypes = new RefactoringInfo.ChangeType[1];
final String[] oldNames = new String[1];
try {
source.runUserActionTask(new CancellableTask<CompilationController>() {
@Override
public void cancel() {
}
@Override
public void run(CompilationController controller) throws Exception {
controller.toPhase(JavaSource.Phase.RESOLVED);
Element el = tpHandle.resolveElement(controller);
if (el != null) {
switch(el.getKind()) {
case FIELD:
changeTypes[0] = RefactoringInfo.ChangeType.VARIABLE_RENAME;
break;
case LOCAL_VARIABLE:
Element parentEl = el.getEnclosingElement();
if (parentEl.getKind() == ElementKind.METHOD
&& "initComponents".equals(parentEl.getSimpleName().toString()) // NOI18N
&& ((ExecutableElement)parentEl).getParameters().isEmpty()) {
changeTypes[0] = RefactoringInfo.ChangeType.VARIABLE_RENAME;
}
break;
}
// [should we also check if it really matches an existing component in the form?]
oldNames[0] = el.getSimpleName().toString();
}
}
}, true);
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
changeType = changeTypes[0];
oldName = oldNames[0];
}
// TBD: changing a property method of a component
} else if (file != null && file.isFolder()) {
// renaming a folder (incl. subfolders)
if (isOnSourceClasspath(file)) {
changeType = RefactoringInfo.ChangeType.FOLDER_RENAME;
primaryFile = file;
oldName = file.getName();
}
} else if (pkgFolder != null) {
// renaming a package (without subfolders)
if (isOnSourceClasspath(pkgFolder.getFolder())) {
changeType = RefactoringInfo.ChangeType.PACKAGE_RENAME;
primaryFile = pkgFolder.getFolder();
ClassPath cp = ClassPath.getClassPath(primaryFile, ClassPath.SOURCE);
oldName = cp.getResourceName(primaryFile, '.', false);
}
}
if (changeType != null) {
fileList.add(primaryFile);
nameList.add(oldName);
}
} else {
if (refactoring instanceof MoveRefactoring) {
Collection<? extends FileObject> files = refactoring.getRefactoringSource().lookupAll(FileObject.class);
for (FileObject file : files) {
if (RefactoringInfo.isJavaFile(file) && isOnSourceClasspath(file)) {
// moving a java file (between packages)
changeType = RefactoringInfo.ChangeType.CLASS_MOVE;
fileList.add(file);
ClassPath cp = ClassPath.getClassPath(file, ClassPath.SOURCE);
nameList.add(cp.getResourceName(file, '.', false));
}
}
} else if (refactoring instanceof SingleCopyRefactoring) {
Collection<? extends FileObject> files = refactoring.getRefactoringSource().lookupAll(FileObject.class);
for (FileObject file : files) {
if (RefactoringInfo.isJavaFileOfForm(file) && isOnSourceClasspath(file)) {
// copying a java file
changeType = RefactoringInfo.ChangeType.CLASS_COPY;
fileList.add(file);
nameList.add(file.getName());
}
}
} else if (refactoring instanceof SafeDeleteRefactoring) {
Collection<? extends FileObject> files = refactoring.getRefactoringSource().lookupAll(FileObject.class);
for (FileObject file : files) {
if (file != null && RefactoringInfo.isJavaFileOfForm(file) && isOnSourceClasspath(file)) {
// deleting a form
changeType = RefactoringInfo.ChangeType.CLASS_DELETE;
fileList.add(file);
nameList.add(null);
}
}
}
}
if (changeType != null) {
FileObject[] originalFiles = fileList.toArray(new FileObject[fileList.size()]);
String[] oldNames = nameList.toArray(new String[nameList.size()]);
RefactoringInfo refInfo = new RefactoringInfo(refactoring, changeType, originalFiles, oldNames);
refactoring.getContext().add(refInfo); // to be accessible to the GuardedBlockHandlerFactoryImpl
return new RefactoringPluginImpl(refInfo);
}
return null;
}
private static boolean isOnSourceClasspath(FileObject fo) {
// TBD
return true;
}
// -----
private static class RefactoringPluginImpl implements RefactoringPlugin {
private RefactoringInfo refInfo;
RefactoringPluginImpl(RefactoringInfo refInfo) {
this.refInfo = refInfo;
}
@Override
public Problem preCheck() {
return null;
}
@Override
public Problem checkParameters() {
return null;
}
@Override
public Problem fastCheckParameters() {
return null;
}
@Override
public void cancelRequest() {
}
@Override
public Problem prepare(RefactoringElementsBag refactoringElements) {
// even if guarded blocks are not affected directly we might want some changes
for (FileObject file : refInfo.getOriginalFiles()) {
if (RefactoringInfo.isJavaFileOfForm(file)) {
FormRefactoringUpdate update = refInfo.getUpdateForFile(file);
switch (refInfo.getChangeType()) {
case CLASS_DELETE: // in case of delete we only backup the form file
refactoringElements.registerTransaction(update);
return null;
case CLASS_RENAME: // renaming form class, always needs to load - auto-i18n
if (!update.prepareForm(true)) {
return new Problem(true, "Error loading form. Cannot update generated code.");
}
break;
// for VARIABLE_RENAME and EVENT_HANDLER_RENAME we don't know yet
// if they affect the form - guarded block handler will take care
}
refactoringElements.add(refInfo.getRefactoring(), update.getPreviewElement());
refactoringElements.addFileChange(refInfo.getRefactoring(), update);
} else if (refInfo.getChangeType() == RefactoringInfo.ChangeType.PACKAGE_RENAME
|| refInfo.getChangeType() == RefactoringInfo.ChangeType.FOLDER_RENAME) {
boolean anyForm = false;
for (FileObject fo : file.getChildren()) {
if (RefactoringInfo.isJavaFileOfForm(fo)) {
anyForm = true;
FormRefactoringUpdate update = refInfo.getUpdateForFile(fo);
refactoringElements.addFileChange(refInfo.getRefactoring(), update);
}
}
if (anyForm) {
// TODO add refactoring element informing about updating references to resources in GUI forms in this package
}
}
}
return null;
}
}
}