| /* |
| * 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(); |
| } |
| } |