| /* |
| * 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.*; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.ElementKind; |
| import javax.lang.model.element.Modifier; |
| import javax.lang.model.element.TypeElement; |
| 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.api.ProgressEvent; |
| import org.netbeans.modules.refactoring.java.RefactoringUtils; |
| import org.netbeans.modules.refactoring.java.api.JavaRefactoringUtils; |
| import org.netbeans.modules.refactoring.java.api.MemberInfo; |
| import org.netbeans.modules.refactoring.java.api.PullUpRefactoring; |
| 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; |
| |
| |
| /** Plugin that implements the core functionality of Pull Up refactoring. |
| * |
| * @author Martin Matula |
| * @author Jan Becicka |
| */ |
| public final class PullUpRefactoringPlugin extends JavaRefactoringPlugin { |
| /** Reference to the parent refactoring instance */ |
| private final PullUpRefactoring refactoring; |
| private TreePathHandle treePathHandle; |
| |
| |
| /** Creates a new instance of PullUpRefactoringPlugin |
| * @param refactoring Parent refactoring instance. |
| */ |
| PullUpRefactoringPlugin(PullUpRefactoring refactoring) { |
| this.refactoring = refactoring; |
| this.treePathHandle = refactoring.getSourceType(); |
| } |
| |
| @Override |
| protected JavaSource getJavaSource(Phase p) { |
| switch (p) { |
| default: |
| return JavaSource.forFileObject(treePathHandle.getFileObject()); |
| } |
| } |
| |
| @Override |
| protected Problem preCheck(CompilationController cc) throws IOException { |
| fireProgressListenerStart(AbstractRefactoring.PRE_CHECK, 4); |
| try { |
| cc.toPhase(JavaSource.Phase.RESOLVED); |
| Problem problem = isElementAvail(treePathHandle, cc); |
| if (problem != null) { |
| // fatal error -> don't continue with further checks |
| return problem; |
| } |
| |
| // increase progress (step 1) |
| fireProgressListenerStep(); |
| final Element elm = treePathHandle.resolveElement(cc); |
| problem = JavaPluginUtils.isSourceElement(elm, cc); |
| if (problem != null) { |
| return problem; |
| } |
| if (!(elm instanceof TypeElement)) { |
| return new Problem(true, NbBundle.getMessage(PushDownRefactoringPlugin.class, "ERR_PushDown_InvalidSource", treePathHandle, elm)); // NOI18N |
| } |
| TypeElement e = (TypeElement) elm; |
| Collection<TypeElement> superTypes = JavaRefactoringUtils.getSuperTypes(e, cc, true); |
| List<MemberInfo> minfo = new LinkedList<MemberInfo>(); |
| for (TypeElement el: superTypes) { |
| MemberInfo<ElementHandle<TypeElement>> memberInfo = MemberInfo.create(el, cc); |
| if(memberInfo.getElementHandle().resolve(cc) != null) { // #200200 - Error in pulling up to a interface with cyclic inheritance error |
| minfo.add(memberInfo); |
| } |
| } |
| if (minfo.isEmpty()) { |
| return new Problem(true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_PullUp_NoSuperTypes")); // NOI18N |
| } |
| // increase progress (step 2) |
| fireProgressListenerStep(); |
| // #2 - check if there are any members to pull up |
| for (Element element : e.getEnclosedElements()) { |
| if (element.getKind() != ElementKind.CONSTRUCTOR) { |
| return null; |
| } |
| } |
| if (!e.getInterfaces().isEmpty()) { |
| return null; |
| } |
| problem = new Problem(true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_PullUp_NoMembers")); // NOI18N |
| // increase progress (step 3) |
| fireProgressListenerStep(); |
| return problem; |
| } finally { |
| fireProgressListenerStop(); |
| } |
| } |
| |
| |
| @Override |
| public Problem fastCheckParameters() { |
| MemberInfo<ElementHandle<? extends Element>>[] info = refactoring.getMembers(); |
| // #1 - check whether there are any members to pull up |
| if (info.length == 0) { |
| return new Problem(true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_PullUp_NoMembersSelected")); // NOI18N |
| } |
| |
| if (info.length > 1) { |
| for (int i=0; i<info.length - 1; i++) { |
| for (int j = i + 1; j < info.length; j++) { |
| if (info[i].equals(info[j])) { |
| return new Problem(true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_CannotPullupDuplicateMembers")); |
| } |
| } |
| } |
| } |
| |
| // #2 - check if the targed type is not null |
| if (refactoring.getTargetType() == null) { |
| return new Problem(true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_PullUp_NoTargetType")); // NOI18N |
| } |
| |
| Problem p=null; |
| if (refactoring.getTargetType().getKind().isInterface()) { |
| for (MemberInfo i:info) { |
| if (!i.getModifiers().contains(Modifier.PUBLIC)) { |
| p = createProblem(p, false, NbBundle.getMessage(PullUpRefactoringPlugin.class,"ERR_PullupNonPublicToInterface" ,i.getName())); |
| } |
| |
| if (i.getModifiers().contains(Modifier.STATIC)) { |
| p = createProblem(p, true, NbBundle.getMessage(PullUpRefactoringPlugin.class,"ERR_PullupStaticToInterface" ,i.getName())); |
| } |
| } |
| } |
| |
| for (MemberInfo<ElementHandle<? extends Element>> i : info) { |
| if(i.getElementHandle().signatureEquals(refactoring.getTargetType())) { |
| p = createProblem(p, true, NbBundle.getMessage(PullUpRefactoringPlugin.class,"ERR_PullUp_MemberTargetType" ,i.getName())); |
| } |
| } |
| |
| return p; |
| } |
| |
| @Override |
| protected Problem checkParameters(CompilationController cc) throws IOException { |
| fireProgressListenerStart(AbstractRefactoring.PRE_CHECK, 4); |
| try { |
| cc.toPhase(JavaSource.Phase.RESOLVED); |
| TypeElement sourceType = (TypeElement) refactoring.getSourceType().resolveElement(cc); |
| Collection<TypeElement> supers = JavaRefactoringUtils.getSuperTypes(sourceType, cc, false); |
| TypeElement targetType = refactoring.getTargetType().resolve(cc); |
| MemberInfo<ElementHandle<? extends Element>>[] members = refactoring.getMembers(); |
| |
| fireProgressListenerStart(AbstractRefactoring.PARAMETERS_CHECK, members.length + 1); |
| // #1 - check whether the target type is a legal super type |
| if (!supers.contains(targetType)) { |
| return new Problem(true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_PullUp_IllegalTargetType")); // NOI18N |
| } |
| |
| fireProgressListenerStep(); |
| |
| // TODO: what the hell is this check |
| // #2 - check whether all the members are legal members that can be pulled up |
| // HashSet visitedSources = new HashSet(); |
| // // HashSet allMembers = new HashSet(Arrays.asList(members)); |
| Problem problems = null; |
| // visitedSources.add(refactoring.getSourceType()); |
| for (int i = 0; i < members.length; i++) { |
| Element cls; |
| Element member = members[i].getElementHandle().resolve(cc); |
| if (members[i].getGroup()!=MemberInfo.Group.IMPLEMENTS) { |
| // // member is a feature (inner class, field or method) |
| // cls = member.getEnclosingElement(); |
| // } else { |
| // // member is an interface from implements clause |
| // MultipartId ifcName = (MultipartId) member; |
| // // get parent of the element (should be class if this is really |
| // // a name from implements clause |
| // Object parent = ifcName.refImmediateComposite(); |
| // // if parent is not a class, member is invalid |
| // if (!(parent instanceof JavaClass)) { |
| // cls = null; |
| // } else { |
| // // check if the parent class contains this MultipartId |
| // // in interfaceNames |
| // if (!((JavaClass) parent).getInterfaceNames().contains(ifcName)) { |
| // cls = null; |
| // } else { |
| // cls = (ClassDefinition) parent; |
| // } |
| // } |
| // } |
| // // if the declaring class has not been visited yet, perform checks on it |
| // if (visitedSources.add(cls)) { |
| // // if the declaring class of a feature is not a JavaClass, |
| // // or if it is not from the set of source type's supertypes |
| // // or if the declaring class is not a subtype of target class |
| // // then this member is illegal |
| // if (!(cls instanceof JavaClass) || !supers.contains(cls) || cls.equals(targetType) || !cls.isSubTypeOf(targetType)) { |
| // return createProblem(problems, true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_PullUp_IllegalMember", member.getName())); // NOI18N |
| // } |
| // } |
| // #3 - check if the member already exists in the target class |
| |
| if (RefactoringUtils.elementExistsIn(targetType, member, cc)) { |
| return createProblem(problems, true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_PullUp_MemberAlreadyExists", member.getSimpleName())); // NOI18N |
| } |
| |
| // #4 - check if the field does not use something that is not going to be pulled up |
| // Resource sourceResource = refactoring.getSourceType().getResource(); |
| // Resource targetResource = targetType.getResource(); |
| // if (!sourceResource.equals(targetResource)) { |
| // problems = checkUsedByElement(member, allMembers, problems, |
| // !sourceResource.equals(targetResource), |
| // !sourceResource.getPackageName().equals(targetResource.getPackageName())); |
| // } |
| |
| fireProgressListenerStep(); |
| } |
| |
| // TODO: implement non-fatal checks |
| } |
| } finally { |
| fireProgressListenerStop(); |
| } |
| return null; |
| } |
| |
| @Override |
| public Problem prepare(RefactoringElementsBag refactoringElements) { |
| ClasspathInfo cpInfo = getClasspathInfo(refactoring); |
| |
| Set<FileObject> a = new HashSet<FileObject>(); |
| a.addAll(getSuperTypesFiles(refactoring.getSourceType())); |
| a.add(RefactoringUtils.getFileObject(treePathHandle)); |
| fireProgressListenerStart(AbstractRefactoring.PREPARE, a.size()); |
| TransformTask task = new TransformTask(new PullUpTransformer(refactoring), treePathHandle); |
| Problem problem = createAndAddElements(a, task, refactoringElements, refactoring, cpInfo); |
| fireProgressListenerStop(); |
| return problem; |
| } |
| |
| protected FileObject getFileObject() { |
| return treePathHandle.getFileObject(); |
| } |
| |
| /** |
| * @param handle |
| * @return |
| */ |
| private static Collection<FileObject> getSuperTypesFiles(TreePathHandle handle) { |
| try { |
| SuperTypesTask ff; |
| JavaSource source = JavaSource.forFileObject(handle.getFileObject()); |
| source.runUserActionTask(ff = new SuperTypesTask(handle), true); |
| return ff.getFileObjects(); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| private static class SuperTypesTask implements CancellableTask<CompilationController> { |
| |
| private Collection<FileObject> files; |
| TreePathHandle handle; |
| |
| SuperTypesTask(TreePathHandle handle) { |
| this.handle = handle; |
| } |
| |
| @Override |
| public void cancel() { |
| } |
| |
| @Override |
| public void run(CompilationController cc) { |
| try { |
| cc.toPhase(JavaSource.Phase.RESOLVED); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| Element el = handle.resolveElement(cc); |
| files = RefactoringUtils.elementsToFile(JavaRefactoringUtils.getSuperTypes((TypeElement) el, cc, true), cc.getClasspathInfo()); |
| } |
| |
| public Collection<FileObject> getFileObjects() { |
| return files; |
| } |
| } |
| } |