blob: 8184576c9faf6ea1c58f9d06bba92999e826f67e [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.plugins;
import java.io.IOException;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.NestingKind;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import org.netbeans.api.java.source.*;
import org.netbeans.modules.refactoring.api.AbstractRefactoring;
import org.netbeans.modules.refactoring.api.Problem;
import org.netbeans.modules.refactoring.java.api.InnerToOuterRefactoring;
import org.netbeans.modules.refactoring.java.spi.JavaRefactoringPlugin;
import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
import org.openide.filesystems.FileObject;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import static org.netbeans.modules.refactoring.java.plugins.Bundle.*;
/** Plugin that implements the core functionality of "inner to outer" refactoring.
*
* @author Martin Matula
* @author Jan Becicka
*/
public class InnerToOuterRefactoringPlugin extends JavaRefactoringPlugin {
/** Reference to the parent refactoring instance */
private final InnerToOuterRefactoring refactoring;
private TreePathHandle treePathHandle;
/** Creates a new instance of InnerToOuterRefactoringPlugin
* @param refactoring Parent refactoring instance.
*/
InnerToOuterRefactoringPlugin(InnerToOuterRefactoring refactoring) {
this.refactoring = refactoring;
this.treePathHandle = refactoring.getRefactoringSource().lookup(TreePathHandle.class);
}
@Override
protected JavaSource getJavaSource(Phase p) {
switch (p) {
case PRECHECK:
ClasspathInfo cpInfo = getClasspathInfo(refactoring);
return JavaSource.create(cpInfo, treePathHandle.getFileObject());
default:
return JavaSource.forFileObject(treePathHandle.getFileObject());
}
}
@Override
protected Problem preCheck(CompilationController info) throws IOException {
// fire operation start on the registered progress listeners (4 steps)
fireProgressListenerStart(refactoring.PRE_CHECK, 4);
Problem preCheckProblem = null;
info.toPhase(JavaSource.Phase.RESOLVED);
Element el = treePathHandle.resolveElement(info);
TreePathHandle sourceType = refactoring.getSourceType();
// check whether the element is valid
Problem result = isElementAvail(sourceType, info);
if (result != null) {
// fatal error -> don't continue with further checks
return result;
}
result = JavaPluginUtils.isSourceElement(el, info);
if (result != null) {
return result;
}
refactoring.setClassName(sourceType.resolveElement(info).getSimpleName().toString());
// increase progress (step 1)
fireProgressListenerStep();
// #1 - check if the class is an inner class
// RefObject declCls = (RefObject) sourceType.refImmediateComposite();
if (el instanceof TypeElement) {
if (((TypeElement)el).getNestingKind() == NestingKind.ANONYMOUS) {
// fatal error -> return
preCheckProblem = new Problem(true, NbBundle.getMessage(InnerToOuterRefactoringPlugin.class, "ERR_InnerToOuter_Anonymous")); // NOI18N
return preCheckProblem;
}
if (!((TypeElement)el).getNestingKind().isNested()) {
// fatal error -> return
preCheckProblem = new Problem(true, NbBundle.getMessage(InnerToOuterRefactoringPlugin.class, "ERR_InnerToOuter_MustBeInnerClass")); // NOI18N
return preCheckProblem;
}
} else {
preCheckProblem = new Problem(true, NbBundle.getMessage(InnerToOuterRefactoringPlugin.class, "ERR_InnerToOuter_MustBeInnerClass")); // NOI18N
return preCheckProblem;
}
// increase progress (step 2)
fireProgressListenerStep();
fireProgressListenerStop();
return preCheckProblem;
}
@Override
public Problem checkParameters() {
return null;
}
@Override
@NbBundle.Messages(
"ERR_InnerToOuter_ClassNameClash=Inner class named <b>{0}</b> already exists in the target class.")
protected Problem fastCheckParameters(final CompilationController javac) throws IOException {
Problem problem = null;
String name = refactoring.getReferenceName();
javac.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
Element resolved = refactoring.getSourceType().resolveElement(javac);
if(resolved != null && resolved.getKind().isClass()) {
if(name != null) {
List<VariableElement> fieldsIn = ElementFilter.fieldsIn(((TypeElement)resolved).getEnclosedElements());
for (VariableElement variableElement : fieldsIn) {
if(variableElement.getSimpleName().toString().equals(name)) {
problem = new Problem(true, NbBundle.getMessage(InnerToOuterRefactoringPlugin.class, "ERR_OuterNameAlreadyUsed", name, variableElement.getEnclosingElement().getSimpleName())); // NOI18N
return problem;
}
}
fieldsIn = ElementFilter.fieldsIn(javac.getElements().getAllMembers((TypeElement)resolved));
for (VariableElement variableElement : fieldsIn) {
if(variableElement.getSimpleName().toString().equals(name)) {
problem = new Problem(false, NbBundle.getMessage(InnerToOuterRefactoringPlugin.class, "WRN_OuterNameAlreadyUsed", name, variableElement.getEnclosingElement().getSimpleName())); // NOI18N
break;
}
}
List<ExecutableElement> constructors = ElementFilter.constructorsIn(((TypeElement)resolved).getEnclosedElements());
for( ExecutableElement execElement: constructors ) {
List<? extends VariableElement> parameters = execElement.getParameters();
for( VariableElement variableElement: parameters ) {
if(variableElement.getSimpleName().toString().equals(name)) {
return new Problem(true, NbBundle.getMessage(InnerToOuterRefactoringPlugin.class, "ERR_InnerToOuter_OuterNameClash", name, resolved.getSimpleName())); // NOI18N
}
}
}
}
String className = refactoring.getClassName();
if(className != null) {
TypeElement outer = javac.getElementUtilities().enclosingTypeElement(resolved);
Element outerouter = outer.getEnclosingElement();
if (outerouter.getKind() != ElementKind.PACKAGE) {
if(!outerouter.getKind().isClass()) {
outerouter = javac.getElementUtilities().enclosingTypeElement(outerouter);
}
List<TypeElement> types = ElementFilter.typesIn(javac.getElements().getAllMembers((TypeElement)outerouter));
for (TypeElement type : types) {
if (className.contentEquals(type.getSimpleName()) && type != resolved) {
return new Problem(true, ERR_InnerToOuter_ClassNameClash(className)); // NOI18N
}
}
}
}
}
return problem;
}
@Override
public Problem fastCheckParameters() {
Problem result = null;
String newName = refactoring.getClassName();
if (!Utilities.isJavaIdentifier(newName)) {
result = createProblem(result, true, NbBundle.getMessage(InnerToOuterRefactoringPlugin.class, "ERR_InvalidIdentifier", newName)); // NOI18N
return result;
}
String referenceName = refactoring.getReferenceName();
if(referenceName != null) {
if (referenceName.length() < 1) {
result = new Problem(true, NbBundle.getMessage(InnerToOuterRefactoringPlugin.class, "ERR_EmptyReferenceName")); // NOI18N
return result;
} else {
if (!Utilities.isJavaIdentifier(referenceName)) {
result = new Problem(true, NbBundle.getMessage(InnerToOuterRefactoringPlugin.class, "ERR_InvalidIdentifier", referenceName)); // NOI18N
return result;
}
}
}
FileObject primFile = refactoring.getSourceType().getFileObject();
FileObject folder = primFile.getParent();
FileObject[] children = folder.getChildren();
for (FileObject child: children) {
if (!child.isVirtual() && child.getName().equals(newName) && "java".equals(child.getExt())) { // NOI18N
result = createProblem(result, true, NbBundle.getMessage(InnerToOuterRefactoringPlugin.class, "ERR_ClassClash", newName, folder.getName())); // NOI18N
return result;
}
}
return super.fastCheckParameters();
}
private Set<FileObject> getRelevantFiles() {
ClasspathInfo cpInfo = getClasspathInfo(refactoring);
HashSet<FileObject> set = new LinkedHashSet<FileObject>();
set.add(refactoring.getSourceType().getFileObject());
ClassIndex idx = cpInfo.getClassIndex();
set.addAll(idx.getResources(refactoring.getSourceType().getElementHandle(), EnumSet.of(ClassIndex.SearchKind.TYPE_REFERENCES, ClassIndex.SearchKind.IMPLEMENTORS),EnumSet.of(ClassIndex.SearchScope.SOURCE)));
return set;
}
@Override
public Problem prepare(RefactoringElementsBag refactoringElements) {
Set<FileObject> a = getRelevantFiles();
fireProgressListenerStart(AbstractRefactoring.PREPARE, a.size());
final InnerToOuterTransformer innerToOuter = new InnerToOuterTransformer(refactoring);
TransformTask transform = new TransformTask(innerToOuter, refactoring.getSourceType());
Problem problem = createAndAddElements(a, transform, refactoringElements, refactoring);
fireProgressListenerStop();
return problem != null ? problem : innerToOuter.getProblem();
}
}