Merge branch 'master' of git.labs.intellij.net:idea/community

This commit is contained in:
irengrig
2010-06-11 12:56:56 +04:00
345 changed files with 56838 additions and 1202 deletions
@@ -24,15 +24,16 @@ import com.intellij.openapi.projectRoots.SdkAdditionalData;
import com.intellij.openapi.projectRoots.SdkModificator;
import com.intellij.openapi.projectRoots.SdkType;
import com.intellij.openapi.roots.RootProvider;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import org.jetbrains.annotations.NotNull;
/**
* used to override JdkHome location in order to provide correct paths
*/
@@ -70,6 +71,14 @@ public final class MockJdkWrapper implements Sdk {
return myDelegate.getRootProvider();
}
public <T> T getUserData(@NotNull Key<T> key) {
return myDelegate.getUserData(key);
}
public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
myDelegate.putUserData(key, value);
}
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
@@ -146,6 +146,11 @@ private static void setEnabled(JComponent c, boolean enabled) {
return StdFileTypes.JAVA;
}
@Override
protected void onLanguageChange(Language language) {
// Ignore, JavaDoc does not support languages other than Java
}
public void setPanelLanguage(Language language) {
super.setPanelLanguage(Language.findInstance(JavaLanguage.class));
}
@@ -49,7 +49,7 @@ public class RefCountHolder {
private enum State {
VIRGIN, // just created or cleared
BEING_WRITTEN_BY_GHP, // general highlighting pass is storing references during analysis
READY, // may be used for higlighting unused stuff
READY, // may be used for highlighting unused stuff
BEING_USED_BY_PHP, // post highlighting pass is retrieving info
}
@@ -780,6 +780,7 @@ public class HighlightMethodUtil {
int methodCount = 0;
final PsiMethod[] methodsByName = aClass.findMethodsByName(method.getName(), false);
for (PsiMethod other : methodsByName) {
if (other instanceof ExternallyDefinedPsiElement) continue;
if (other == method ||
other.isConstructor() == method.isConstructor() && other.getSignature(PsiSubstitutor.EMPTY).equals(methodSignature)) {
methodCount++;
@@ -557,10 +557,6 @@ public class PsiClassImplUtil {
if (!processor.execute(field, state)) return false;
}
}
for (PsiField field : PsiAugmentProvider.collectAugments(aClass, PsiField.class)) {
if (!processor.execute(field, state)) return false;
}
}
if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.METHOD)) {
@@ -573,10 +569,6 @@ public class PsiClassImplUtil {
}
if (!processor.execute(method, state)) return false;
}
for (PsiMethod method : PsiAugmentProvider.collectAugments(aClass, PsiMethod.class)) {
if (!processor.execute(method, state)) return false;
}
}
if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.CLASS)) {
@@ -66,11 +66,6 @@ public class PsiImplUtil {
if (method.isConstructor()) constructorsList.add(method);
}
final List<PsiMethod> augments = PsiAugmentProvider.collectAugments(aClass, PsiMethod.class);
for (final PsiMethod method : augments) {
if (method.isConstructor()) constructorsList.add(method);
}
return constructorsList.toArray(new PsiMethod[constructorsList.size()]);
}
@@ -15,122 +15,201 @@
*/
package com.intellij.psi.impl.source;
import com.intellij.openapi.util.ModificationTracker;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMember;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.impl.PsiClassImplUtil;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.ArrayUtil;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class ClassInnerStuffCache {
private volatile Map<String, PsiField> myCachedFieldsMap = null;
private volatile Map<String, PsiMethod[]> myCachedMethodsMap = null;
private volatile Map<String, PsiClass> myCachedInnersMap = null;
private volatile PsiMethod[] myCachedConstructors = null;
private final PsiClass myClass;
private final MyModificationTracker myTreeChangeTracker;
private CachedValue<PsiMethod[]> myConstructorsCache;
private CachedValue<PsiField[]> myFieldsCache;
private CachedValue<PsiMethod[]> myMethodsCache;
private CachedValue<Map<String, List<PsiField>>> myFieldsMapCache;
private CachedValue<Map<String, List<PsiMethod>>> myMethodsMapCache;
private CachedValue<Map<String, PsiClass>> myInnerClassesMapCache;
public ClassInnerStuffCache(final PsiClass aClass) {
myClass = aClass;
}
public void dropCaches() {
myCachedConstructors = null;
myCachedFieldsMap = null;
myCachedMethodsMap = null;
myCachedInnersMap = null;
myTreeChangeTracker = new MyModificationTracker();
buildCaches();
}
@NotNull
public PsiMethod[] getConstructors() {
PsiMethod[] constructors = myCachedConstructors;
if (constructors == null) {
myCachedConstructors = constructors = PsiImplUtil.getConstructors(myClass);
}
return constructors;
final PsiMethod[] constructors = myConstructorsCache.getValue();
return constructors != null ? constructors : PsiMethod.EMPTY_ARRAY;
}
public PsiField findFieldByName(String name, boolean checkBases) {
@NotNull
public PsiField[] getFields() {
final PsiField[] fields = myFieldsCache.getValue();
return fields != null ? fields : PsiField.EMPTY_ARRAY;
}
@NotNull
public PsiMethod[] getMethods() {
final PsiMethod[] methods = myMethodsCache.getValue();
return methods != null ? methods : PsiMethod.EMPTY_ARRAY;
}
@Nullable
public PsiField findFieldByName(final String name, final boolean checkBases) {
if (!checkBases) {
Map<String, PsiField> cachedFields = myCachedFieldsMap;
if (cachedFields == null) {
final PsiField[] fields = myClass.getFields();
if (fields.length > 0) {
cachedFields = new THashMap<String, PsiField>();
for (final PsiField field : fields) {
cachedFields.put(field.getName(), field);
}
myCachedFieldsMap = cachedFields;
}
else {
myCachedFieldsMap = Collections.emptyMap();
return null;
}
final Map<String, List<PsiField>> cachedFields = myFieldsMapCache.getValue();
if (cachedFields != null) {
final List<PsiField> fields = cachedFields.get(name);
return fields != null ? fields.get(0) : null;
}
return cachedFields.get(name);
return null;
}
return PsiClassImplUtil.findFieldByName(myClass, name, checkBases);
}
@NotNull
public PsiMethod[] findMethodsByName(String name, boolean checkBases) {
if(!checkBases){
Map<String, PsiMethod[]> cachedMethods = myCachedMethodsMap;
if(cachedMethods == null){
cachedMethods = new THashMap<String,PsiMethod[]>();
Map<String, List<PsiMethod>> cachedMethodsMap = new THashMap<String,List<PsiMethod>>();
final PsiMethod[] methods = myClass.getMethods();
for (final PsiMethod method : methods) {
List<PsiMethod> list = cachedMethodsMap.get(method.getName());
if (list == null) {
list = new ArrayList<PsiMethod>(1);
cachedMethodsMap.put(method.getName(), list);
}
list.add(method);
public PsiMethod[] findMethodsByName(final String name, final boolean checkBases) {
if (!checkBases) {
final Map<String, List<PsiMethod>> cachedMethods = myMethodsMapCache.getValue();
if (cachedMethods != null) {
final List<PsiMethod> methods = cachedMethods.get(name);
if (methods != null && methods.size() > 0) {
return methods.toArray(new PsiMethod[methods.size()]);
}
for (Map.Entry<String, List<PsiMethod>> entry : cachedMethodsMap.entrySet()) {
List<PsiMethod> cached = entry.getValue();
String methodName = entry.getKey();
cachedMethods.put(methodName, cached.toArray(new PsiMethod[cached.size()]));
}
myCachedMethodsMap = cachedMethods;
}
final PsiMethod[] psiMethods = cachedMethods.get(name);
return psiMethods != null ? psiMethods : PsiMethod.EMPTY_ARRAY;
return PsiMethod.EMPTY_ARRAY;
}
return PsiClassImplUtil.findMethodsByName(myClass, name, checkBases);
}
public PsiClass findInnerClassByName(String name, boolean checkBases) {
@Nullable
public PsiClass findInnerClassByName(final String name, final boolean checkBases) {
if (!checkBases) {
Map<String, PsiClass> inners = myCachedInnersMap;
if (inners == null) {
final PsiClass[] classes = myClass.getInnerClasses();
if (classes.length > 0) {
inners = new THashMap<String, PsiClass>();
for (final PsiClass psiClass : classes) {
inners.put(psiClass.getName(), psiClass);
}
myCachedInnersMap = inners;
}
else {
myCachedInnersMap = Collections.emptyMap();
return null;
}
}
return inners.get(name);
final Map<String, PsiClass> inners = myInnerClassesMapCache.getValue();
return inners != null ? inners.get(name) : null;
}
return PsiClassImplUtil.findInnerByName(myClass, name, checkBases);
}
private void buildCaches() {
final CachedValuesManager manager = CachedValuesManager.getManager(myClass.getProject());
final Object[] dependencies = {PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, myTreeChangeTracker};
myConstructorsCache = manager.createCachedValue(new CachedValueProvider<PsiMethod[]>() {
public Result<PsiMethod[]> compute() {
return Result.create(PsiImplUtil.getConstructors(myClass), dependencies);
}
}, false);
myFieldsCache = manager.createCachedValue(new CachedValueProvider<PsiField[]>() {
public Result<PsiField[]> compute() {
return Result.create(getAllFields(), dependencies);
}
}, false);
myMethodsCache = manager.createCachedValue(new CachedValueProvider<PsiMethod[]>() {
public Result<PsiMethod[]> compute() {
return Result.create(getAllMethods(), dependencies);
}
}, false);
myFieldsMapCache = manager.createCachedValue(new CachedValueProvider<Map<String, List<PsiField>>>() {
public Result<Map<String, List<PsiField>>> compute() {
return Result.create(getFieldsMap(), dependencies);
}
}, false);
myMethodsMapCache = manager.createCachedValue(new CachedValueProvider<Map<String, List<PsiMethod>>>() {
public Result<Map<String, List<PsiMethod>>> compute() {
return Result.create(getMethodsMap(), dependencies);
}
}, false);
myInnerClassesMapCache = manager.createCachedValue(new CachedValueProvider<Map<String, PsiClass>>() {
public Result<Map<String, PsiClass>> compute() {
return Result.create(getInnerClassesMap(), dependencies);
}
}, false);
}
private PsiField[] getAllFields() {
if (!(myClass instanceof PsiClassImpl)) return myClass.getFields();
final PsiField[] own = ((PsiClassImpl)myClass).getStubOrPsiChildren(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY);
final List<PsiField> ext = PsiAugmentProvider.collectAugments(myClass, PsiField.class);
return ArrayUtil.mergeArrayAndCollection(own, ext, PsiField.ARRAY_FACTORY);
}
private PsiMethod[] getAllMethods() {
if (!(myClass instanceof PsiClassImpl)) return myClass.getMethods();
final PsiMethod[] own = ((PsiClassImpl)myClass).getStubOrPsiChildren(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY);
final List<PsiMethod> ext = PsiAugmentProvider.collectAugments(myClass, PsiMethod.class);
return ArrayUtil.mergeArrayAndCollection(own, ext, PsiMethod.ARRAY_FACTORY);
}
@Nullable
private Map<String, List<PsiField>> getFieldsMap() {
return getMembersMap(getFields());
}
@Nullable
private Map<String, List<PsiMethod>> getMethodsMap() {
return getMembersMap(getMethods());
}
@Nullable
private static <T extends PsiMember> Map<String, List<T>> getMembersMap(final T[] members) {
if (members.length == 0) return null;
final Map<String, List<T>> cachedMembers = new THashMap<String, List<T>>();
for (final T member : members) {
List<T> list = cachedMembers.get(member.getName());
if (list == null) {
cachedMembers.put(member.getName(), (list = new ArrayList<T>(1)));
}
list.add(member);
}
return cachedMembers;
}
@Nullable
private Map<String, PsiClass> getInnerClassesMap() {
final PsiClass[] classes = myClass.getInnerClasses();
if (classes.length == 0) return null;
final Map<String, PsiClass> cachedInners = new THashMap<String, PsiClass>();
for (final PsiClass psiClass : classes) {
cachedInners.put(psiClass.getName(), psiClass);
}
return cachedInners;
}
public void dropCaches() {
myTreeChangeTracker.myCount++;
}
private static class MyModificationTracker implements ModificationTracker {
private long myCount = 0;
public long getModificationCount() {
return myCount;
}
}
}
@@ -64,7 +64,7 @@ import java.util.Map;
public class PsiClassImpl extends JavaStubPsiElement<PsiClassStub<?>> implements PsiClass, PsiQualifiedNamedElement, Queryable {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.PsiClassImpl");
private final ClassInnerStuffCache innersCache = new ClassInnerStuffCache(this);
private final ClassInnerStuffCache myInnersCache = new ClassInnerStuffCache(this);
private PsiMethod myValuesMethod = null;
private PsiMethod myValueOfMethod = null;
@@ -97,7 +97,7 @@ public class PsiClassImpl extends JavaStubPsiElement<PsiClassStub<?>> implements
}
private void dropCaches() {
innersCache.dropCaches();
myInnersCache.dropCaches();
myCachedForLongName = null;
}
@@ -306,17 +306,17 @@ public class PsiClassImpl extends JavaStubPsiElement<PsiClassStub<?>> implements
@NotNull
public PsiField[] getFields() {
return getStubOrPsiChildren(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY);
return myInnersCache.getFields();
}
@NotNull
public PsiMethod[] getMethods() {
return getStubOrPsiChildren(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY);
return myInnersCache.getMethods();
}
@NotNull
public PsiMethod[] getConstructors() {
return innersCache.getConstructors();
return myInnersCache.getConstructors();
}
@NotNull
@@ -350,7 +350,7 @@ public class PsiClassImpl extends JavaStubPsiElement<PsiClassStub<?>> implements
}
public PsiField findFieldByName(String name, boolean checkBases) {
return innersCache.findFieldByName(name, checkBases);
return myInnersCache.findFieldByName(name, checkBases);
}
public PsiMethod findMethodBySignature(PsiMethod patternMethod, boolean checkBases) {
@@ -364,7 +364,7 @@ public class PsiClassImpl extends JavaStubPsiElement<PsiClassStub<?>> implements
@NotNull
public PsiMethod[] findMethodsByName(String name, boolean checkBases) {
return innersCache.findMethodsByName(name, checkBases);
return myInnersCache.findMethodsByName(name, checkBases);
}
@NotNull
@@ -378,7 +378,7 @@ public class PsiClassImpl extends JavaStubPsiElement<PsiClassStub<?>> implements
}
public PsiClass findInnerClassByName(String name, boolean checkBases) {
return innersCache.findInnerClassByName(name, checkBases);
return myInnersCache.findInnerClassByName(name, checkBases);
}
public PsiTypeParameterList getTypeParameterList() {
@@ -644,6 +644,7 @@ public class ImportHelper{
while (!stack.isEmpty()) {
final PsiElement child = stack.removeFirst();
if (child instanceof PsiImportList) continue;
if (child instanceof PsiLiteralExpression) continue;
stack.addAll(Arrays.asList(child.getChildren()));
for(final PsiReference reference : child.getReferences()){
@@ -0,0 +1,81 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed 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.
*/
/*
* User: anna
* Date: 07-Jun-2010
*/
package com.intellij.refactoring.extractclass;
import com.intellij.psi.*;
import com.intellij.psi.search.PsiSearchScopeUtil;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase;
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
import com.intellij.refactoring.typeMigration.rules.TypeConversionRule;
import java.util.List;
public class EnumTypeConversionRule extends TypeConversionRule {
private final List<PsiField> myEnumConstants;
public EnumTypeConversionRule(List<PsiField> enumConstants) {
myEnumConstants = enumConstants;
}
@Override
public TypeConversionDescriptorBase findConversion(PsiType from,
PsiType to,
PsiMember member,
PsiExpression context,
TypeMigrationLabeler labeler) {
final PsiMethodCallExpression callExpression = PsiTreeUtil.getParentOfType(context, PsiMethodCallExpression.class, false);
if (callExpression != null) {
final PsiMethod resolved = callExpression.resolveMethod();
if (resolved != null) {
final SearchScope searchScope = labeler.getRules().getSearchScope();
if (!PsiSearchScopeUtil.isInScope(searchScope, resolved)) {
return null;
}
}
}
final PsiField field = PsiTreeUtil.getParentOfType(context, PsiField.class);
if (field != null &&
!myEnumConstants.contains(field) &&
field.hasModifierProperty(PsiModifier.STATIC) &&
field.hasModifierProperty(PsiModifier.FINAL) &&
field.hasInitializer()) {
return null;
}
final PsiClass toClass = PsiUtil.resolveClassInType(to);
if (toClass != null && toClass.isEnum()) {
final PsiMethod[] constructors = toClass.getConstructors();
if (constructors.length == 1) {
final PsiMethod constructor = constructors[0];
final PsiParameter[] parameters = constructor.getParameterList().getParameters();
if (parameters.length == 1) {
if (TypeConversionUtil.isAssignable(parameters[0].getType(), from)) {
return new TypeConversionDescriptorBase();
}
}
}
}
return null;
}
}
@@ -245,7 +245,7 @@ class ExtractClassDialog extends RefactoringDialog implements MemberInfoChangeLi
protected JComponent createCenterPanel() {
final JPanel panel = new JPanel(new BorderLayout());
final MemberSelectionPanel memberSelectionPanel =
new MemberSelectionPanel(RefactorJBundle.message("members.to.extract.label"), memberInfo, "as enum"){
new MemberSelectionPanel(RefactorJBundle.message("members.to.extract.label"), memberInfo, "As enum"){
@Override
protected MemberSelectionTable createMemberSelectionTable(final List<MemberInfo> memberInfo, String abstractColumnHeader) {
return new MemberSelectionTable(memberInfo, abstractColumnHeader) {
@@ -342,7 +342,7 @@ class ExtractClassDialog extends RefactoringDialog implements MemberInfoChangeLi
private static boolean isConstantField(PsiMember member) {
return member instanceof PsiField &&
member.hasModifierProperty(PsiModifier.STATIC) &&
member.hasModifierProperty(PsiModifier.FINAL) &&
// member.hasModifierProperty(PsiModifier.FINAL) &&
((PsiField)member).hasInitializer();
}
@@ -16,10 +16,12 @@
package com.intellij.refactoring.extractclass;
import com.intellij.ide.util.PackageUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
@@ -43,7 +45,6 @@ import com.intellij.refactoring.psi.MethodInheritanceUtils;
import com.intellij.refactoring.psi.TypeParametersVisitor;
import com.intellij.refactoring.util.FixableUsageInfo;
import com.intellij.refactoring.util.FixableUsagesRefactoringProcessor;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.refactoring.util.classMembers.MemberInfo;
import com.intellij.usageView.UsageInfo;
@@ -73,6 +74,8 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
private final String delegateFieldName;
private final boolean requiresBackpointer;
private boolean delegationRequired = false;
private ExtractEnumProcessor myExtractEnumProcessor;
private PsiClass myClass;
public ExtractClassProcessor(PsiClass sourceClass,
List<PsiField> fields,
@@ -122,11 +125,25 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
}
typeParams.addAll(typeParamSet);
}
myClass = ApplicationManager.getApplication().runWriteAction(
new Computable<PsiClass>() {
public PsiClass compute() {
return buildClass();
}
}
);
myExtractEnumProcessor = new ExtractEnumProcessor(myProject, this.enumConstants, fields, myClass);
}
@Override
protected boolean preprocessUsages(final Ref<UsageInfo[]> refUsages) {
final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
myExtractEnumProcessor.findEnumConstantConflicts(refUsages, conflicts);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
myClass.delete();
}
});
final Project project = sourceClass.getProject();
final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
final PsiClass existingClass =
@@ -153,21 +170,7 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
conflicts.putValue(field, "Field \'" + field.getName() + "\' needs setter");
}
}
for (final PsiField enumConstant : enumConstants) {
final PsiExpression initializer = enumConstant.getInitializer();
assert initializer != null;
initializer.accept(new JavaRecursiveElementWalkingVisitor(){
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
super.visitReferenceExpression(expression);
final PsiElement resolved = expression.resolve();
if (!enumConstants.contains(resolved) && fields.contains(resolved)) {
conflicts.putValue(initializer, "Enum constant " + RefactoringUIUtil.getDescription(enumConstant, false) +
" would forward reference on field " + RefactoringUIUtil.getDescription(resolved, false));
}
}
});
}
checkConflicts(refUsages, conflicts);
return showConflicts(conflicts, refUsages.get());
}
@@ -245,6 +248,7 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
if (delegationRequired) {
buildDelegate();
}
myExtractEnumProcessor.performEnumConstantTypeMigration(usageInfos);
final Set<PsiMember> members = new HashSet<PsiMember>();
for (PsiMethod method : methods) {
final PsiMethod member = psiClass.findMethodBySignature(method, false);
@@ -470,6 +474,7 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
findUsagesForField(field, usages);
usages.add(new RemoveField(field));
}
usages.addAll(myExtractEnumProcessor.findEnumConstantUsages(new ArrayList<FixableUsageInfo>(usages)));
for (PsiClass innerClass : innerClasses) {
findUsagesForInnerClass(innerClass, usages);
usages.add(new RemoveInnerClass(innerClass));
@@ -0,0 +1,218 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed 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.
*/
/*
* User: anna
* Date: 08-Jun-2010
*/
package com.intellij.refactoring.extractclass;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.extractclass.usageInfo.ReplaceStaticVariableAccess;
import com.intellij.refactoring.psi.MutationUtils;
import com.intellij.refactoring.typeMigration.TypeMigrationProcessor;
import com.intellij.refactoring.typeMigration.TypeMigrationRules;
import com.intellij.refactoring.util.EnumConstantsUtil;
import com.intellij.refactoring.util.FixableUsageInfo;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.MultiMap;
import java.util.*;
public class ExtractEnumProcessor {
private Project myProject;
private List<PsiField> myEnumConstants;
private PsiClass myClass;
private TypeMigrationProcessor myTypeMigrationProcessor;
private List<PsiField> myFields;
public ExtractEnumProcessor(Project project, List<PsiField> enumConstants, List<PsiField> fields, PsiClass aClass) {
myProject = project;
myEnumConstants = enumConstants;
myFields = fields;
myClass = aClass;
}
public void findEnumConstantConflicts(final Ref<UsageInfo[]> refUsages, final MultiMap<PsiElement, String> conflicts) {
if (hasUsages2Migrate()) {
final List<UsageInfo> resolvableConflicts = new ArrayList<UsageInfo>();
for (UsageInfo failedUsage : myTypeMigrationProcessor.getLabeler().getFailedUsages()) {
final PsiElement element = failedUsage.getElement();
if (element instanceof PsiReferenceExpression) {
resolvableConflicts.add(new FixableUsageInfo(element) {
@Override
public void fixUsage() throws IncorrectOperationException {
final PsiReferenceExpression expression = (PsiReferenceExpression)element;
final String link = PropertyUtil.suggestGetterName("value", expression.getType()) + "()";
MutationUtils.replaceExpression(expression.getReferenceName() + "." + link, expression);
}
});
} else if (element != null) {
resolvableConflicts.add(new ConflictUsageInfo(element, null));
}
}
if (!resolvableConflicts.isEmpty()) {
final List<UsageInfo> usageInfos = new ArrayList<UsageInfo>(Arrays.asList(refUsages.get()));
for (Iterator<UsageInfo> iterator = resolvableConflicts.iterator(); iterator.hasNext();) {
final UsageInfo conflict = iterator.next();
for (UsageInfo usageInfo : usageInfos) {
if (conflict.getElement() == usageInfo.getElement()) {
iterator.remove();
break;
}
}
}
resolvableConflicts.addAll(0, usageInfos);
refUsages.set(resolvableConflicts.toArray(new UsageInfo[resolvableConflicts.size()]));
}
}
}
private boolean hasUsages2Migrate() {
return myTypeMigrationProcessor != null;
}
public List<FixableUsageInfo> findEnumConstantUsages(List<FixableUsageInfo> fieldUsages) {
final List<FixableUsageInfo> result = new ArrayList<FixableUsageInfo>();
if (!myEnumConstants.isEmpty()) {
final Set<PsiSwitchStatement> switchStatements = new HashSet<PsiSwitchStatement>();
for (UsageInfo usage : fieldUsages) {
if (usage instanceof ReplaceStaticVariableAccess) {
final PsiElement element = usage.getElement();
final PsiSwitchStatement switchStatement = PsiTreeUtil.getParentOfType(element, PsiSwitchStatement.class);
if (switchStatement != null) {
switchStatements.add(switchStatement);
}
}
}
final PsiConstantEvaluationHelper evaluationHelper =
JavaPsiFacade.getInstance(myProject).getConstantEvaluationHelper();
final Set<Object> enumValues = new HashSet<Object>();
for (PsiField enumConstant : myEnumConstants) {
enumValues.add(evaluationHelper.computeConstantExpression(enumConstant.getInitializer()));
}
final PsiType enumValueType = myEnumConstants.get(0).getType();
for (PsiSwitchStatement switchStatement : switchStatements) {
final PsiStatement errStatement = EnumConstantsUtil.isEnumSwitch(switchStatement, enumValueType, enumValues);
if (errStatement != null) {
String description = null;
if (errStatement instanceof PsiSwitchLabelStatement) {
final PsiExpression caseValue = ((PsiSwitchLabelStatement)errStatement).getCaseValue();
if (caseValue != null) {
description = caseValue.getText() + " can not be replaced with enum";
}
}
result.add(new ConflictUsageInfo(errStatement, description));
}
else {
final PsiExpression expression = switchStatement.getExpression();
if (expression instanceof PsiReferenceExpression) {
final PsiElement element = ((PsiReferenceExpression)expression).resolve();
if (element != null) {
if (!element.getManager().isInProject(element)) {
result.add(new ConflictUsageInfo(expression, StringUtil.capitalize(RefactoringUIUtil.getDescription(element, false)) + " is out of project"));
}
}
}
else {
result.add(new ConflictUsageInfo(expression, null));
}
}
}
final TypeMigrationRules rules = new TypeMigrationRules(myEnumConstants.get(0).getType());
rules.addConversionDescriptor(new EnumTypeConversionRule(myEnumConstants));
rules.setMigrationRootType(
JavaPsiFacade.getElementFactory(myProject).createType(myClass));
rules.setBoundScope(GlobalSearchScope.projectScope(myProject));
myTypeMigrationProcessor = new TypeMigrationProcessor(myProject, myEnumConstants.toArray(new PsiElement[myEnumConstants.size()]), rules);
for (UsageInfo usageInfo : myTypeMigrationProcessor.findUsages()) {
final PsiElement migrateElement = usageInfo.getElement();
if (migrateElement instanceof PsiField) {
final PsiField enumConstantField = (PsiField)migrateElement;
if (enumConstantField.hasModifierProperty(PsiModifier.STATIC) &&
enumConstantField.hasModifierProperty(PsiModifier.FINAL) &&
enumConstantField.hasInitializer() &&
!myEnumConstants.contains(enumConstantField)) {
continue;
}
}
result.add(new EnumTypeMigrationUsageInfo(usageInfo));
}
}
return result;
}
public void performEnumConstantTypeMigration(UsageInfo[] usageInfos) {
if (hasUsages2Migrate()) {
final List<UsageInfo> migrationInfos = new ArrayList<UsageInfo>();
for (UsageInfo usageInfo : usageInfos) {
if (usageInfo instanceof EnumTypeMigrationUsageInfo) {
migrationInfos.add(((EnumTypeMigrationUsageInfo)usageInfo).getUsageInfo());
}
}
myTypeMigrationProcessor.performRefactoring(migrationInfos.toArray(new UsageInfo[migrationInfos.size()]));
}
}
private static class EnumTypeMigrationUsageInfo extends FixableUsageInfo {
private UsageInfo myUsageInfo;
public EnumTypeMigrationUsageInfo(UsageInfo usageInfo) {
super(usageInfo.getElement());
myUsageInfo = usageInfo;
}
@Override
public void fixUsage() throws IncorrectOperationException {
}
public UsageInfo getUsageInfo() {
return myUsageInfo;
}
}
private static class ConflictUsageInfo extends FixableUsageInfo {
private final String myDescription;
public ConflictUsageInfo(PsiElement expression, String description) {
super(expression);
myDescription = description;
}
@Override
public void fixUsage() throws IncorrectOperationException {
}
@Override
public String getConflictMessage() {
return "Unable to migrate statement to enum constant." + (myDescription != null ? " " + myDescription : "");
}
}
}
@@ -259,7 +259,7 @@ class ExtractedClassBuilder {
if (hasEnumConstants()) {
out.append(StringUtil.join(enumConstantFields, new Function<PsiField, String>() {
public String fun(PsiField field) {
final StringBuffer fieldStr = new StringBuffer(field.getName().toUpperCase() + "(");
final StringBuffer fieldStr = new StringBuffer(field.getName() + "(");
final PsiExpression initializer = field.getInitializer();
if (initializer != null) {
initializer.accept(new Mutator(fieldStr));
@@ -411,7 +411,7 @@ class ExtractedClassBuilder {
final String name = field.getName();
if (enumConstantFields.contains(field)) {
out.append(name.toUpperCase()).append(".").append(getterName()).append("()");
out.append(name).append(".").append(getterName()).append("()");
} else {
if (qualifier != null && name.equals(expression.getReferenceName())) {
out.append("this.");
@@ -15,26 +15,63 @@
*/
package com.intellij.refactoring.extractclass.usageInfo;
import com.intellij.psi.PsiReferenceExpression;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.psi.MutationUtils;
import com.intellij.refactoring.util.FixableUsageInfo;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
public class ReplaceStaticVariableAccess extends FixableUsageInfo {
private final PsiReferenceExpression expression;
private final String delegateClass;
private final PsiReferenceExpression expression;
private final String delegateClass;
private final boolean myEnumConstant;
private static final Logger LOGGER = Logger.getInstance("#" + ReplaceStaticVariableAccess.class.getName());
public ReplaceStaticVariableAccess(PsiReferenceExpression expression, String delegateClass, boolean enumConstant) {
super(expression);
this.expression = expression;
this.delegateClass = delegateClass;
super(expression);
this.expression = expression;
this.delegateClass = delegateClass;
myEnumConstant = enumConstant;
}
public void fixUsage() throws IncorrectOperationException {
MutationUtils.replaceExpression(delegateClass + '.' + expression.getReferenceName() + (myEnumConstant ? "." + PropertyUtil.suggestGetterName("value", expression.getType())+
"()" : ""), expression);
public void fixUsage() throws IncorrectOperationException {
if (myEnumConstant) {
final PsiSwitchLabelStatement switchStatement = PsiTreeUtil.getParentOfType(expression, PsiSwitchLabelStatement.class);
if (switchStatement != null) {
MutationUtils.replaceExpression(expression.getReferenceName(), expression);
return;
}
}
boolean replaceWithGetEnumValue = myEnumConstant;
if (replaceWithGetEnumValue) {
final PsiMethodCallExpression callExpression = PsiTreeUtil.getParentOfType(expression, PsiMethodCallExpression.class);
if (callExpression != null) {
final PsiElement resolved = callExpression.getMethodExpression().resolve();
if (resolved instanceof PsiMethod) {
final PsiParameter[] parameters = ((PsiMethod)resolved).getParameterList().getParameters();
final PsiExpression[] args = callExpression.getArgumentList().getExpressions();
final int idx = ArrayUtil.find(args, expression);
if (idx != -1 && parameters[idx].getType().getCanonicalText().equals(delegateClass)) {
replaceWithGetEnumValue = false;
}
}
}
else {
final PsiReturnStatement returnStatement = PsiTreeUtil.getParentOfType(expression, PsiReturnStatement.class);
if (returnStatement != null) {
final PsiMethod psiMethod = PsiTreeUtil.getParentOfType(expression, PsiMethod.class);
LOGGER.assertTrue(psiMethod != null);
final PsiType returnType = psiMethod.getReturnType();
if (returnType != null && returnType.getCanonicalText().equals(delegateClass)) {
replaceWithGetEnumValue = false;
}
}
}
}
final String link = replaceWithGetEnumValue ? "." + PropertyUtil.suggestGetterName("value", expression.getType()) + "()" : "";
MutationUtils.replaceExpression(delegateClass + '.' + expression.getReferenceName() + link, expression);
}
}
@@ -84,7 +84,7 @@ public class MoveJavaMemberHandler implements MoveMemberHandler {
PsiReferenceExpression refExpr = (PsiReferenceExpression)usage.reference;
PsiExpression qualifier = refExpr.getQualifierExpression();
if (qualifier != null) {
if (usage.qualifierClass != null) {
if (usage.qualifierClass != null && PsiTreeUtil.getParentOfType(refExpr, PsiSwitchLabelStatement.class) == null) {
changeQualifier(refExpr, usage.qualifierClass, usage.member);
}
else {
@@ -92,7 +92,7 @@ public class MoveJavaMemberHandler implements MoveMemberHandler {
}
}
else { // no qualifier
if (usage.qualifierClass != null) {
if (usage.qualifierClass != null && PsiTreeUtil.getParentOfType(refExpr, PsiSwitchLabelStatement.class) == null) {
changeQualifier(refExpr, usage.qualifierClass, usage.member);
}
}
@@ -0,0 +1,70 @@
package com.intellij.refactoring.typeMigration;
import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.RefactoringActionHandler;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.typeMigration.ui.TypeMigrationDialog;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import org.jetbrains.annotations.NotNull;
public class ChangeTypeSignatureHandler implements RefactoringActionHandler {
private static final Logger LOG = Logger.getInstance("#" + ChangeTypeSignatureHandler.class.getName());
public static final String REFACTORING_NAME = "Type Migration";
public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) {
editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
final int offset = TargetElementUtilBase.adjustOffset(editor.getDocument(), editor.getCaretModel().getOffset());
final PsiElement element = file.findElementAt(offset);
PsiTypeElement typeElement = PsiTreeUtil.getParentOfType(element, PsiTypeElement.class);
while (typeElement != null) {
final PsiElement parent = typeElement.getParent();
if (parent instanceof PsiVariable || parent instanceof PsiMember || (parent instanceof PsiReferenceParameterList && PsiTreeUtil.getParentOfType(parent, PsiMember.class) instanceof PsiClass)) {
invoke(project, parent, null, editor);
return;
}
typeElement = PsiTreeUtil.getParentOfType(parent, PsiTypeElement.class, false);
}
CommonRefactoringUtil.showErrorHint(project, editor,
"The caret should be positioned on type of field, variable, method or method parameter to be refactored",
REFACTORING_NAME, "refactoring.migrateType");
}
public void invoke(@NotNull final Project project, @NotNull final PsiElement[] elements, final DataContext dataContext) {
LOG.assertTrue(elements.length == 1);
final PsiElement element = elements[0];
invokeOnElement(project, element);
}
public static boolean invokeOnElement(final Project project, final PsiElement element) {
if (element instanceof PsiVariable || element instanceof PsiMember || element instanceof PsiFile) {
invoke(project, element, null, null);
return true;
}
if (element instanceof PsiReferenceParameterList && PsiTreeUtil.getParentOfType(element, PsiMember.class) instanceof PsiClass) {
invoke(project, element, null, null);
return true;
}
return false;
}
public static void invoke(final Project project, final PsiElement root, final TypeMigrationRules rules, final Editor editor) {
if (Util.canBeMigrated(root)) {
TypeMigrationDialog dialog = new TypeMigrationDialog(project, root, rules);
dialog.show();
return;
}
CommonRefactoringUtil.showErrorHint(project, editor, RefactoringBundle.message("only.fields.variables.of.methods.of.valid.type.can.be.considered"),
RefactoringBundle.message("unable.to.start.type.migration"), null);
}
}
@@ -0,0 +1,185 @@
/*
* User: anna
* Date: 19-Apr-2008
*/
package com.intellij.refactoring.typeMigration;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import com.intellij.util.Query;
import java.util.*;
public class ClassTypeArgumentMigrationProcessor {
private static final Logger LOG = Logger.getInstance("#" + ClassTypeArgumentMigrationProcessor.class.getName());
private final TypeMigrationLabeler myLabeler;
public ClassTypeArgumentMigrationProcessor(final TypeMigrationLabeler labeler) {
myLabeler = labeler;
}
public void migrateClassTypeParameter(final PsiReferenceParameterList referenceParameterList, final PsiType migrationType) {
final PsiClass psiClass = PsiTreeUtil.getParentOfType(referenceParameterList, PsiClass.class);
LOG.assertTrue(psiClass != null);
final PsiClass superClass = psiClass.getSuperClass();
LOG.assertTrue(superClass != null);
myLabeler.getTypeEvaluator().setType(new TypeMigrationUsageInfo(superClass), migrationType);
final Map<PsiElement, Pair<PsiReference[], PsiType>> roots = new HashMap<PsiElement, Pair<PsiReference[], PsiType>>();
markTypeParameterUsages(psiClass, migrationType, referenceParameterList, roots);
final Set<PsiElement> processed = new HashSet<PsiElement>();
for (Map.Entry<PsiElement, Pair<PsiReference[], PsiType>> entry : roots.entrySet()) {
final PsiElement member = entry.getKey();
final PsiType type = entry.getValue().second;
if (member instanceof PsiParameter && ((PsiParameter)member).getDeclarationScope() instanceof PsiMethod) {
myLabeler.migrateMethodCallExpressions(type, (PsiParameter)member, psiClass);
}
final PsiReference[] references = entry.getValue().first;
for (PsiReference usage : references) {
myLabeler.migrateRootUsageExpression(usage, processed);
}
}
}
private void markTypeParameterUsages(final PsiClass psiClass, PsiType migrationType, PsiReferenceParameterList referenceParameterList,
final Map<PsiElement, Pair<PsiReference[], PsiType>> roots) {
final Map<PsiClass, PsiTypeParameter[]> visibleTypeParams = getTypeParametersHierarchy(referenceParameterList);
final PsiSubstitutor substitutor = composeSubstitutor(psiClass.getProject(), migrationType, visibleTypeParams);
for (Map.Entry<PsiClass, PsiTypeParameter[]> entry : visibleTypeParams.entrySet()) {
final TypeParameterSearcher parameterSearcher = new TypeParameterSearcher(entry.getValue());
entry.getKey().accept(new JavaRecursiveElementVisitor(){
@Override
public void visitMethod(final PsiMethod method) {
super.visitMethod(method);
processMemberType(method, parameterSearcher, psiClass, substitutor, roots);
for (PsiParameter parameter : method.getParameterList().getParameters()) {
processMemberType(parameter, parameterSearcher, psiClass, substitutor, roots);
}
}
@Override
public void visitField(final PsiField field) {
super.visitField(field);
processMemberType(field, parameterSearcher, psiClass, substitutor, roots);
}
});
}
}
private void processMemberType(final PsiElement element,
final TypeParameterSearcher parameterSearcher,
final PsiClass psiClass,
final PsiSubstitutor substitutor,
final Map<PsiElement, Pair<PsiReference[], PsiType>> roots) {
final PsiType elementType = TypeMigrationLabeler.getElementType(element);
if (elementType != null && elementType.accept(parameterSearcher).booleanValue()) {
final PsiType memberType = substitutor.substitute(elementType);
prepareMethodsChangeSignature(psiClass, element, memberType);
final List<PsiReference> refs = TypeMigrationLabeler.filterReferences(psiClass, ReferencesSearch.search(element, psiClass.getUseScope()));
roots.put(element, Pair.create(myLabeler.markRootUsages(element, memberType, refs.toArray(new PsiReference[refs.size()])), memberType));
}
}
private static PsiSubstitutor composeSubstitutor(final Project project, final PsiType migrationType, final Map<PsiClass, PsiTypeParameter[]> visibleTypeParams) {
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
final PsiResolveHelper psiResolveHelper = JavaPsiFacade.getInstance(project).getResolveHelper();
for (Map.Entry<PsiClass,PsiTypeParameter[]> entry : visibleTypeParams.entrySet()) {
final PsiClassType clearedOriginalType = JavaPsiFacade.getElementFactory(project).createType(entry.getKey(), PsiSubstitutor.EMPTY);
for (PsiTypeParameter parameter : entry.getValue()) {
substitutor = substitutor.put(parameter,
psiResolveHelper.getSubstitutionForTypeParameter(parameter, clearedOriginalType, migrationType, true, clearedOriginalType.getLanguageLevel()));
}
}
return substitutor;
}
private static Map<PsiClass, PsiTypeParameter[]> getTypeParametersHierarchy(final PsiReferenceParameterList referenceParameterList) {
final PsiElement parent = referenceParameterList.getParent();
LOG.assertTrue(parent instanceof PsiJavaCodeReferenceElement);
final PsiClass superClass = (PsiClass)((PsiJavaCodeReferenceElement)parent).resolve();
LOG.assertTrue(superClass != null);
final Map<PsiClass, PsiTypeParameter[]> visibleTypeParams = new HashMap<PsiClass, PsiTypeParameter[]>();
visibleTypeParams.put(superClass, superClass.getTypeParameters());
final HashSet<PsiClass> superClasses = new HashSet<PsiClass>();
InheritanceUtil.getSuperClasses(superClass, superClasses, true);
for (PsiClass superSuperClass : superClasses) {
visibleTypeParams.put(superSuperClass, superSuperClass.getTypeParameters());
}
return visibleTypeParams;
}
/**
* signature should be changed for methods with type parameters
*/
private void prepareMethodsChangeSignature(final PsiClass currentClass, final PsiElement memberToChangeSignature, final PsiType memberType) {
if (memberToChangeSignature instanceof PsiMethod) {
final Query<PsiMethod> methodQuery = OverridingMethodsSearch.search(((PsiMethod)memberToChangeSignature), currentClass.getUseScope(), true);
for (PsiMethod method : methodQuery) {
myLabeler.addRoot(new TypeMigrationUsageInfo(method), memberType, method, false);
}
} else if (memberToChangeSignature instanceof PsiParameter && ((PsiParameter)memberToChangeSignature).getDeclarationScope() instanceof PsiMethod) {
final PsiMethod superMethod = (PsiMethod)((PsiParameter)memberToChangeSignature).getDeclarationScope();
final int parameterIndex = superMethod.getParameterList().getParameterIndex((PsiParameter)memberToChangeSignature);
final Query<PsiMethod> methodQuery = OverridingMethodsSearch.search(superMethod, currentClass.getUseScope(), true);
for (PsiMethod method : methodQuery) {
final PsiParameter parameter = method.getParameterList().getParameters()[parameterIndex];
myLabeler.addRoot(new TypeMigrationUsageInfo(parameter), memberType, parameter, false);
}
}
}
private static class TypeParameterSearcher extends PsiTypeVisitor<Boolean> {
private final Set<PsiTypeParameter> myTypeParams = new HashSet<PsiTypeParameter>();
private TypeParameterSearcher(final PsiTypeParameter[] set) {
myTypeParams.addAll(Arrays.asList(set));
}
public Boolean visitType(final PsiType type) {
return false;
}
public Boolean visitArrayType(final PsiArrayType arrayType) {
return arrayType.getComponentType().accept(this);
}
public Boolean visitClassType(final PsiClassType classType) {
final PsiClass aClass = classType.resolve();
if (aClass instanceof PsiTypeParameter && myTypeParams.contains((PsiTypeParameter)aClass)) return true;
final PsiType[] types = classType.getParameters();
for (final PsiType psiType : types) {
if (psiType.accept(this).booleanValue()) return true;
}
return false;
}
public Boolean visitWildcardType(final PsiWildcardType wildcardType) {
final PsiType bound = wildcardType.getBound();
return bound != null && bound.accept(this).booleanValue();
}
}
}
@@ -0,0 +1,27 @@
package com.intellij.refactoring.typeMigration;
import com.intellij.psi.PsiExpression;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
public class TypeConversionDescriptorBase {
private TypeMigrationUsageInfo myRoot;
public TypeConversionDescriptorBase() {
}
public TypeMigrationUsageInfo getRoot() {
return myRoot;
}
public void setRoot(final TypeMigrationUsageInfo root) {
myRoot = root;
}
public void replace(PsiExpression expression){}
@Override
public String toString() {
return "$";
}
}
@@ -0,0 +1,515 @@
package com.intellij.refactoring.typeMigration;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.source.tree.java.PsiBinaryExpressionImpl;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import com.intellij.util.Function;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Map;
/**
* Created by IntelliJ IDEA.
* User: db
* Date: 27.06.2003
* Time: 22:48:08
* To change this template use Options | File Templates.
*/
public class TypeEvaluator {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.typeMigration.TypeEvaluator");
private final HashMap<TypeMigrationUsageInfo, LinkedList<PsiType>> myTypeMap;
private final TypeMigrationRules myRules;
private final TypeMigrationLabeler myLabeler;
public TypeEvaluator(final LinkedList<Pair<TypeMigrationUsageInfo, PsiType>> types, final TypeMigrationLabeler labeler) {
myLabeler = labeler;
myRules = labeler.getRules();
myTypeMap = new HashMap<TypeMigrationUsageInfo, LinkedList<PsiType>>();
for (final Pair<TypeMigrationUsageInfo, PsiType> p : types) {
final LinkedList<PsiType> e = new LinkedList<PsiType>();
e.addFirst(p.getSecond());
myTypeMap.put(p.getFirst(), e);
}
}
public boolean setType(final TypeMigrationUsageInfo usageInfo, @NotNull PsiType type) {
final LinkedList<PsiType> t = myTypeMap.get(usageInfo);
final PsiElement element = usageInfo.getElement();
if (type instanceof PsiEllipsisType && !(element instanceof PsiParameter && ((PsiParameter)element).getDeclarationScope() instanceof PsiMethod)) {
type = ((PsiEllipsisType)type).toArrayType();
}
if (t != null) {
if (!t.getFirst().equals(type)) {
if (element instanceof PsiVariable || element instanceof PsiMethod) {
return false;
}
t.addFirst(type);
return true;
}
}
else {
final LinkedList<PsiType> e = new LinkedList<PsiType>();
e.addFirst(type);
myTypeMap.put(usageInfo, e);
return true;
}
return false;
}
@Nullable
public PsiType getType(PsiElement element) {
for (Map.Entry<TypeMigrationUsageInfo, LinkedList<PsiType>> entry : myTypeMap.entrySet()) {
if (Comparing.equal(element, entry.getKey().getElement())) {
return entry.getValue().getFirst();
}
}
if (element.getTextRange() == null) return null;
return getType(new TypeMigrationUsageInfo(element));
}
@Nullable
public PsiType getType(final TypeMigrationUsageInfo usageInfo) {
final LinkedList<PsiType> e = myTypeMap.get(usageInfo);
if (e != null) {
return e.getFirst();
}
return TypeMigrationLabeler.getElementType(usageInfo.getElement());
}
@Nullable
public PsiType evaluateType(final PsiExpression expr) {
if (expr == null) return null;
final LinkedList<PsiType> e = myTypeMap.get(new TypeMigrationUsageInfo(expr));
if (e != null) {
return e.getFirst();
}
if (expr instanceof PsiArrayAccessExpression) {
final PsiType at = evaluateType(((PsiArrayAccessExpression)expr).getArrayExpression());
if (at instanceof PsiArrayType) {
return ((PsiArrayType)at).getComponentType();
}
}
else if (expr instanceof PsiAssignmentExpression) {
return evaluateType(((PsiAssignmentExpression)expr).getLExpression());
}
else if (expr instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression call = (PsiMethodCallExpression)expr;
final JavaResolveResult resolveResult = call.resolveMethodGenerics();
final PsiMethod method = (PsiMethod)resolveResult.getElement();
if (method != null) {
final PsiParameter[] parameters = method.getParameterList().getParameters();
final PsiExpression[] actualParms = call.getArgumentList().getExpressions();
return PsiImplUtil.normalizeWildcardTypeByPosition(createMethodSubstitution(parameters, actualParms, method, call, resolveResult.getSubstitutor(), false).substitute(evaluateType(call.getMethodExpression())), expr);
}
}
else if (expr instanceof PsiBinaryExpression) {
final PsiExpression lOperand = ((PsiBinaryExpression)expr).getLOperand();
final PsiExpression rOperand = ((PsiBinaryExpression)expr).getROperand();
final PsiJavaToken operationSign = ((PsiBinaryExpression)expr).getOperationSign();
return PsiBinaryExpressionImpl.calcTypeForBinaryExpression(evaluateType(lOperand), evaluateType(rOperand), operationSign.getTokenType(),
true);
}
else if (expr instanceof PsiPostfixExpression) {
return evaluateType(((PsiPostfixExpression)expr).getOperand());
}
else if (expr instanceof PsiPrefixExpression) {
return evaluateType(((PsiPrefixExpression)expr).getOperand());
}
else if (expr instanceof PsiParenthesizedExpression) {
return evaluateType(((PsiParenthesizedExpression)expr).getExpression());
}
else if (expr instanceof PsiConditionalExpression) {
final PsiExpression thenExpression = ((PsiConditionalExpression)expr).getThenExpression();
final PsiExpression elseExpression = ((PsiConditionalExpression)expr).getElseExpression();
final PsiType thenType = evaluateType(thenExpression);
final PsiType elseType = evaluateType(elseExpression);
switch ((thenType == null ? 0 : 1) + (elseType == null ? 0 : 2)) {
case 0:
return expr.getType();
case 1:
return thenType;
case 2:
return elseType;
case 3:
if (TypeConversionUtil.areTypesConvertible(thenType, elseType)) {
return thenType;
}
else if (TypeConversionUtil.areTypesConvertible(elseType, thenType)) {
return elseType;
} else {
switch ((thenType.equals(thenExpression.getType()) ? 0 : 1) + (elseType.equals(elseExpression.getType()) ? 0 : 2)) {
case 0:
return expr.getType();
case 1:
return thenType;
case 2:
return elseType;
case 3:
LOG.error("Condition type conflict.");
return null;
default:
LOG.error("Must not happen.");
return null;
}
}
default:
LOG.error("Must not happen.");
}
}
else if (expr instanceof PsiNewExpression) {
final PsiExpression qualifier = ((PsiNewExpression)expr).getQualifier();
if (qualifier != null) {
final PsiClassType.ClassResolveResult qualifierResult = resolveType(evaluateType(qualifier));
if (qualifierResult.getElement() != null) {
final PsiSubstitutor qualifierSubs = qualifierResult.getSubstitutor();
final PsiClassType.ClassResolveResult result = resolveType(expr.getType());
if (result.getElement() != null) {
final PsiClass aClass = result.getElement();
return JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory()
.createType(aClass, result.getSubstitutor().putAll(qualifierSubs));
}
}
}
}
else if (expr instanceof PsiReferenceExpression) {
final PsiType type = evaluateReferenceExpressionType(expr);
if (type != null) {
return PsiImplUtil.normalizeWildcardTypeByPosition(type, expr);
}
} else if (expr instanceof PsiSuperExpression) {
final PsiClass psiClass = PsiTreeUtil.getParentOfType(expr, PsiClass.class);
if (psiClass != null) {
final PsiClass superClass = psiClass.getSuperClass();
if (superClass != null) {
return getType(new TypeMigrationUsageInfo(superClass));
}
}
}
return getType(expr);
}
@Nullable
private PsiType evaluateReferenceExpressionType(PsiExpression expr) {
final PsiReferenceExpression ref = (PsiReferenceExpression)expr;
final PsiExpression qualifier = ref.getQualifierExpression();
if (qualifier == null) {
final PsiElement resolvee = ref.resolve();
if (resolvee == null) {
return null;
}
return resolvee instanceof PsiClass ? JavaPsiFacade.getElementFactory(resolvee.getProject()).createType((PsiClass)resolvee, PsiSubstitutor.EMPTY) : getType(resolvee);
}
else {
final PsiType qualifierType = evaluateType(qualifier);
if (!(qualifierType instanceof PsiArrayType)) {
final PsiElement element = ref.resolve();
final PsiClassType.ClassResolveResult result = resolveType(qualifierType);
final PsiClass aClass = result.getElement();
if (aClass != null) {
final PsiSubstitutor aSubst = result.getSubstitutor();
if (element instanceof PsiField) {
final PsiField field = (PsiField)element;
PsiType aType = field.getType();
final PsiClass superClass = field.getContainingClass();
if (InheritanceUtil.isInheritorOrSelf(aClass, superClass, true)) {
aType = TypeConversionUtil.getSuperClassSubstitutor(superClass, aClass, PsiSubstitutor.EMPTY).substitute(aType);
}
return aSubst.substitute(aType);
} else if (element instanceof PsiMethod){
final PsiMethod method = (PsiMethod)element;
PsiType aType = method.getReturnType();
final PsiClass superClass = method.getContainingClass();
if (InheritanceUtil.isInheritorOrSelf(aClass, superClass, true)) {
aType = TypeConversionUtil.getSuperClassSubstitutor(superClass, aClass, PsiSubstitutor.EMPTY).substitute(aType);
} else if (InheritanceUtil.isInheritorOrSelf(superClass, aClass, true)) {
final PsiMethod[] methods = method.findSuperMethods(aClass);
if (methods.length > 0) {
aType = methods[0].getReturnType();
}
/*}
final Pair<PsiType, PsiType> pair = myRules.bindTypeParameters(qualifier.getType(), qualifierType, method, ref, myLabeler);
if (pair != null) {
final PsiClass psiClass = PsiUtil.resolveClassInType(aType);
if (psiClass instanceof PsiTypeParameter) {
aSubst = aSubst.put((PsiTypeParameter)psiClass, pair.getSecond());
}*/
}
return aSubst.substitute(aType);
}
}
}
}
return null;
}
public static PsiClassType.ClassResolveResult resolveType(PsiType type) {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(type);
final PsiClass aClass = resolveResult.getElement();
if (aClass instanceof PsiAnonymousClass) {
final PsiClassType baseClassType = ((PsiAnonymousClass)aClass).getBaseClassType();
return resolveType(resolveResult.getSubstitutor().substitute(baseClassType));
}
return resolveResult;
}
public PsiSubstitutor createMethodSubstitution(final PsiParameter[] parameters, final PsiExpression[] actualParms, final PsiMethod method,
final PsiExpression call) {
return createMethodSubstitution(parameters, actualParms, method, call, PsiSubstitutor.EMPTY, false);
}
public PsiSubstitutor createMethodSubstitution(final PsiParameter[] parameters,
final PsiExpression[] actualParms,
final PsiMethod method,
final PsiExpression call,
PsiSubstitutor subst,
boolean preferSubst) {
final SubstitutorBuilder substitutorBuilder = new SubstitutorBuilder(method, call, subst);
for (int i = 0; i < Math.min(parameters.length, actualParms.length); i++) {
substitutorBuilder.bindTypeParameters(getType(parameters[i]), evaluateType(actualParms[i]));
}
return substitutorBuilder.createSubstitutor(preferSubst);
}
public String getReport() {
final StringBuffer buffer = new StringBuffer();
final String[] t = new String[myTypeMap.size()];
int k = 0;
for (final TypeMigrationUsageInfo info : myTypeMap.keySet()) {
final LinkedList<PsiType> types = myTypeMap.get(info);
final StringBuffer b = new StringBuffer();
if (types != null) {
b.append(info.getElement()).append(" : ");
b.append(StringUtil.join(types, new Function<PsiType, String>() {
public String fun(final PsiType psiType) {
return psiType.getCanonicalText();
}
}, " "));
b.append("\n");
}
t[k++] = b.toString();
}
Arrays.sort(t);
for (String aT : t) {
buffer.append(aT);
}
return buffer.toString();
}
public LinkedList<Pair<TypeMigrationUsageInfo, PsiType>> getMigratedDeclarations() {
final LinkedList<Pair<TypeMigrationUsageInfo, PsiType>> list = new LinkedList<Pair<TypeMigrationUsageInfo, PsiType>>();
for (final TypeMigrationUsageInfo usageInfo : myTypeMap.keySet()) {
final LinkedList<PsiType> types = myTypeMap.get(usageInfo);
final PsiElement element = usageInfo.getElement();
if (element instanceof PsiVariable || element instanceof PsiMethod) {
list.addLast(new Pair<TypeMigrationUsageInfo, PsiType>(usageInfo, types.getFirst()));
}
}
return list;
}
@Nullable
static PsiType substituteType(final PsiType migrationType, final PsiType originalType, boolean captureWildcard, PsiClass originalClass, final PsiType rawTypeToReplace) {
if (originalClass != null) {
if (((PsiClassType)originalType).hasParameters() && ((PsiClassType)migrationType).hasParameters()) {
final PsiResolveHelper psiResolveHelper = JavaPsiFacade.getInstance(originalClass.getProject()).getResolveHelper();
final PsiType rawOriginalType = JavaPsiFacade.getElementFactory(originalClass.getProject()).createType(originalClass, PsiSubstitutor.EMPTY);
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
for (PsiTypeParameter parameter : originalClass.getTypeParameters()) {
final PsiType type = psiResolveHelper.getSubstitutionForTypeParameter(parameter, rawOriginalType, migrationType, false, PsiUtil.getLanguageLevel(originalClass));
if (type != null) {
substitutor = substitutor.put(parameter, captureWildcard && type instanceof PsiWildcardType ? ((PsiWildcardType)type).getExtendsBound() : type);
} else {
return null;
}
}
return substitutor.substitute(rawTypeToReplace);
} else {
return originalType;
}
}
return null;
}
public static PsiType substituteType(final PsiType migrationTtype, final PsiType originalType, final boolean isContraVariantPosition) {
if ( originalType instanceof PsiClassType && migrationTtype instanceof PsiClassType) {
final PsiClass originalClass = ((PsiClassType)originalType).resolve();
if (isContraVariantPosition && ((PsiClassType)originalType).rawType().isAssignableFrom(((PsiClassType)migrationTtype).rawType())) {
final PsiClass psiClass = ((PsiClassType)migrationTtype).resolve();
final PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(originalClass, psiClass, PsiSubstitutor.EMPTY);
if (substitutor != null) {
final PsiType psiType =
substituteType(migrationTtype, originalType, false, psiClass, JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(originalClass, substitutor));
if (psiType != null) {
return psiType;
}
}
}
else if (!isContraVariantPosition && ((PsiClassType)migrationTtype).rawType().isAssignableFrom(((PsiClassType)originalType).rawType())) {
final PsiType psiType = substituteType(migrationTtype, originalType, false, originalClass, JavaPsiFacade.getElementFactory(originalClass.getProject()).createType(originalClass, PsiSubstitutor.EMPTY));
if (psiType != null) {
return psiType;
}
}
}
return migrationTtype;
}
private class SubstitutorBuilder {
private final Map<PsiTypeParameter, PsiType> myMapping;
private final PsiMethod myMethod;
private final PsiExpression myCall;
private final PsiSubstitutor mySubst;
public SubstitutorBuilder(PsiMethod method, PsiExpression call, PsiSubstitutor subst) {
mySubst = subst;
myMapping = new HashMap<PsiTypeParameter, PsiType>();
myMethod = method;
myCall = call;
}
private void update(final PsiTypeParameter p, PsiType t) {
if (t instanceof PsiPrimitiveType) {
t = ((PsiPrimitiveType)t).getBoxedType(myMethod);
}
final PsiType binding = myMapping.get(p);
if (binding == null) {
myMapping.put(p, t);
}
else if (t != null) {
myMapping.put(p, PsiIntersectionType.createIntersection(binding, t));
}
}
void bindTypeParameters(PsiType formal, final PsiType actual) {
if (formal instanceof PsiWildcardType) formal = ((PsiWildcardType)formal).getBound();
if (formal instanceof PsiArrayType && actual instanceof PsiArrayType) {
bindTypeParameters(((PsiArrayType)formal).getComponentType(), ((PsiArrayType)actual).getComponentType());
return;
}
final Pair<PsiType, PsiType> typePair = myRules.bindTypeParameters(formal, actual, myMethod, myCall, myLabeler);
if (typePair != null) {
bindTypeParameters(typePair.getFirst(), typePair.getSecond());
return;
}
final PsiClassType.ClassResolveResult resultF = resolveType(formal);
final PsiClass classF = resultF.getElement();
if (classF != null) {
if (classF instanceof PsiTypeParameter) {
update((PsiTypeParameter)classF, actual);
return;
}
final PsiClassType.ClassResolveResult resultA = resolveType(actual);
final PsiClass classA = resultA.getElement();
if (classA == null) {
return;
}
if (!classA.equals(classF)) {
final PsiSubstitutor superClassSubstitutor =
TypeConversionUtil.getClassSubstitutor(classF, classA, resultA.getSubstitutor());
if (superClassSubstitutor != null) {
final PsiType aligned = JavaPsiFacade.getInstance(classF.getProject()).getElementFactory().createType(classF, superClassSubstitutor);
bindTypeParameters(formal, aligned);
}
}
final PsiTypeParameter[] typeParms = classA.getTypeParameters();
final PsiSubstitutor substA = resultA.getSubstitutor();
final PsiSubstitutor substF = resultF.getSubstitutor();
for (PsiTypeParameter typeParm : typeParms) {
bindTypeParameters(substF.substitute(typeParm), substA.substitute(typeParm));
}
}
}
public PsiSubstitutor createSubstitutor(boolean preferSubst) {
PsiSubstitutor theSubst = mySubst;
if (preferSubst) {
myMapping.keySet().removeAll(mySubst.getSubstitutionMap().keySet());
}
for (final PsiTypeParameter parm : myMapping.keySet()) {
theSubst = theSubst.put(parm, myMapping.get(parm));
}
return theSubst;
}
}
}
@@ -0,0 +1,830 @@
package com.intellij.refactoring.typeMigration;
import com.intellij.lang.StdLanguages;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.javadoc.PsiDocTagValue;
import com.intellij.psi.search.PsiSearchScopeUtil;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.typeMigration.usageInfo.OverridenUsageInfo;
import com.intellij.refactoring.typeMigration.usageInfo.OverriderUsageInfo;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Query;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
/**
* Created by IntelliJ IDEA.
* User: db
* Date: Sep 19, 2004
* Time: 6:13:23 PM
* To change this template use File | Settings | File Templates.
*/
public class TypeMigrationLabeler {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.typeMigration.TypeMigrationLabeler");
private boolean myShowWarning = true;
public TypeMigrationRules getRules() {
return myRules;
}
private final TypeMigrationRules myRules;
private TypeEvaluator myTypeEvaluator;
private final LinkedHashMap<PsiElement, Object> myConversions;
private final HashSet<Pair<PsiAnchor, PsiType>> myFailedConversions;
private LinkedList<Pair<TypeMigrationUsageInfo, PsiType>> myMigrationRoots;
private final LinkedHashMap<TypeMigrationUsageInfo, PsiType> myNewExpressionTypeChange;
private final LinkedHashMap<TypeMigrationUsageInfo, PsiClassType> myClassTypeArgumentsChange;
private TypeMigrationUsageInfo[] myMigratedUsages = null;
private TypeMigrationUsageInfo myCurrentRoot;
private final Map<TypeMigrationUsageInfo, HashSet<Pair<TypeMigrationUsageInfo, PsiType>>> myRootsTree =
new HashMap<TypeMigrationUsageInfo, HashSet<Pair<TypeMigrationUsageInfo, PsiType>>>();
private final Map<Pair<TypeMigrationUsageInfo, TypeMigrationUsageInfo>, Set<PsiElement>> myRootUsagesTree = new HashMap<Pair<TypeMigrationUsageInfo, TypeMigrationUsageInfo>, Set<PsiElement>>();
private final Set<TypeMigrationUsageInfo> myProcessedRoots = new HashSet<TypeMigrationUsageInfo>();
public TypeMigrationLabeler(final TypeMigrationRules rules) {
myRules = rules;
myConversions = new LinkedHashMap<PsiElement, Object>();
myFailedConversions = new HashSet<Pair<PsiAnchor, PsiType>>();
myNewExpressionTypeChange = new LinkedHashMap<TypeMigrationUsageInfo, PsiType>();
myClassTypeArgumentsChange = new LinkedHashMap<TypeMigrationUsageInfo, PsiClassType>();
}
public boolean hasFailedConversions() {
return myFailedConversions.size() > 0;
}
public String[] getFailedConversionsReport() {
final String[] report = new String[myFailedConversions.size()];
int j = 0;
for (final Pair<PsiAnchor, PsiType> p : myFailedConversions) {
final PsiElement element = p.getFirst().retrieve();
report[j++] = "Cannot convert type of expression <b>" +
StringUtil.escapeXml(element.getText()) +
"</b>" +
" from <b>" +
StringUtil.escapeXml(((PsiExpression)element).getType().getCanonicalText()) +
"</b> to <b>" + StringUtil.escapeXml(p.getSecond().getCanonicalText()) +
"</b><br>";
}
return report;
}
public UsageInfo[] getFailedUsages() {
final List<UsageInfo> usages = new ArrayList<UsageInfo>(myFailedConversions.size());
for (final Pair<PsiAnchor, PsiType> p : myFailedConversions) {
final PsiExpression expr = (PsiExpression)p.getFirst().retrieve();
if (expr != null) {
usages.add(new UsageInfo(expr) {
public String getTooltipText() {
return "Cannot convert type of the expression from " +
expr.getType().getCanonicalText() +
" to " +
p.getSecond().getCanonicalText();
}
});
}
}
return usages.toArray(new UsageInfo[usages.size()]);
}
public TypeMigrationUsageInfo[] getMigratedUsages() {
final LinkedList<Pair<TypeMigrationUsageInfo, PsiType>> declarations = getTypeEvaluator().getMigratedDeclarations();
final TypeMigrationUsageInfo[] usages = new TypeMigrationUsageInfo[declarations.size() + myConversions.size() + myNewExpressionTypeChange.size() + myClassTypeArgumentsChange.size()];
int j = 0;
List<PsiElement> conversionExprs = new ArrayList<PsiElement>(myConversions.keySet());
Collections.sort(conversionExprs, new Comparator<PsiElement>() {
public int compare(final PsiElement e1, final PsiElement e2) {
return e2.getTextRange().getStartOffset() - e1.getTextRange().getStartOffset();
}
});
for (final PsiElement element : conversionExprs) {
final Object conv = myConversions.get(element);
usages[j++] = new TypeMigrationUsageInfo(element) {
public String getTooltipText() {
if (conv instanceof String) { //todo
final String conversion = (String)conv;
return "Replaced with " + conversion.replaceAll("\\$", element.getText());
}
else {
return "Replaced with " + conv.toString();
}
}
@Override
public boolean isExcluded() {
if (conv instanceof TypeConversionDescriptorBase) return ((TypeConversionDescriptorBase)conv).getRoot().isExcluded();
return super.isExcluded();
}
};
}
for (final TypeMigrationUsageInfo expr : myNewExpressionTypeChange.keySet()) {
usages[j++] = expr;
}
for (final Pair<TypeMigrationUsageInfo, PsiType> p : declarations) {
final TypeMigrationUsageInfo element = p.getFirst();
usages[j++] = element;
}
for (TypeMigrationUsageInfo info : myClassTypeArgumentsChange.keySet()) {
usages[j++] = info;
}
return usages;
}
public void change(final TypeMigrationUsageInfo usageInfo) {
final PsiElement element = usageInfo.getElement();
if (element == null) return;
final Project project = element.getProject();
if (element instanceof PsiExpression) {
final PsiExpression expression = (PsiExpression)element;
if (element instanceof PsiNewExpression) {
for (Map.Entry<TypeMigrationUsageInfo, PsiType> info : myNewExpressionTypeChange.entrySet()) {
final PsiElement expressionToReplace = info.getKey().getElement();
if (expression.equals(expressionToReplace)) {
TypeMigrationReplacementUtil.replaceNewExpressionType(project, (PsiNewExpression)expressionToReplace, info);
}
}
}
final Object conversion = myConversions.get(element);
if (conversion != null) {
myConversions.remove(element);
TypeMigrationReplacementUtil.replaceExpression(expression, project, conversion);
}
} else if (element instanceof PsiReferenceParameterList) {
for (Map.Entry<TypeMigrationUsageInfo, PsiClassType> entry : myClassTypeArgumentsChange.entrySet()) {
if (element.equals(entry.getKey().getElement())) { //todo check null
final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
try {
element.getParent().replace(factory.createReferenceElementByType(entry.getValue()));
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
}
}
else {
TypeMigrationReplacementUtil.migratePsiMemeberType(element, project, getTypeEvaluator().getType(usageInfo));
}
}
@Nullable
Object getConversion(PsiElement element) {
return myConversions.get(element);
}
public TypeMigrationUsageInfo[] getMigratedUsages(boolean autoMigrate, final PsiElement... roots) {
if (myMigratedUsages == null) {
myShowWarning = autoMigrate;
migrate(autoMigrate, roots);
myMigratedUsages = getMigratedUsages();
}
return myMigratedUsages;
}
@Nullable
public Set<PsiElement> getTypeUsages(final TypeMigrationUsageInfo element, final TypeMigrationUsageInfo currentRoot) {
return myRootUsagesTree.get(Pair.create(element, currentRoot));
}
void convertExpression(final PsiExpression expr, final PsiType toType, final PsiType fromType, final boolean isCovariantPosition) {
final TypeConversionDescriptorBase conversion = myRules.findConversion(fromType, toType, expr instanceof PsiMethodCallExpression ? ((PsiMethodCallExpression)expr).resolveMethod() : null, expr,
isCovariantPosition, this);
if (conversion == null) {
markFailedConversion(new Pair<PsiType, PsiType>(fromType, toType), expr);
}
else {
setConversionMapping(expr, conversion);
}
}
public void migrateExpressionType(final PsiExpression expr, final PsiType migrationType, final PsiElement place, boolean alreadyProcessed, final boolean isCovariant) {
PsiType originalType = expr.getType();
LOG.assertTrue(originalType != null);
if (originalType.equals(migrationType)) return;
if (originalType.equals(PsiType.NULL)) {
if (migrationType instanceof PsiPrimitiveType) {
markFailedConversion(new Pair<PsiType, PsiType>(originalType, migrationType), expr);
}
return;
}
if (expr instanceof PsiConditionalExpression) {
} else if (expr instanceof PsiClassObjectAccessExpression) {
if (!TypeConversionUtil.isAssignable(migrationType, expr.getType())) {
markFailedConversion(new Pair<PsiType, PsiType>(expr.getType(), migrationType), expr);
return;
}
} else if (expr instanceof PsiArrayInitializerExpression && migrationType instanceof PsiArrayType) {
final PsiExpression[] initializers = ((PsiArrayInitializerExpression)expr).getInitializers();
for (PsiExpression initializer : initializers) {
migrateExpressionType(initializer, ((PsiArrayType)migrationType).getComponentType(), expr, alreadyProcessed, true);
}
getTypeEvaluator().setType(new TypeMigrationUsageInfo(expr), migrationType);
return;
} else if (expr instanceof PsiArrayAccessExpression) {
migrateExpressionType(((PsiArrayAccessExpression)expr).getArrayExpression(), migrationType.createArrayType(), place, alreadyProcessed, isCovariant);
return;
}
else if (expr instanceof PsiReferenceExpression) {
final PsiElement resolved = ((PsiReferenceExpression)expr).resolve();
if (resolved != null) {
if (!addMigrationRoot(resolved, migrationType, place, alreadyProcessed, !isCovariant)) {
convertExpression(expr, migrationType, getTypeEvaluator().evaluateType(expr), isCovariant);
}
}
return;
}
else if (expr instanceof PsiMethodCallExpression) {
final PsiMethod resolved = ((PsiMethodCallExpression)expr).resolveMethod();
if (resolved != null) {
if (!addMigrationRoot(resolved, migrationType, place, alreadyProcessed, !isCovariant)) {
convertExpression(expr, migrationType, getTypeEvaluator().evaluateType(expr), isCovariant);
}
}
return;
}
else if (expr instanceof PsiNewExpression) {
if (originalType.getArrayDimensions() == migrationType.getArrayDimensions()) {
if (migrationType.getArrayDimensions() > 0) {
final PsiType elemenType = ((PsiArrayType)migrationType).getComponentType();
final PsiArrayInitializerExpression arrayInitializer = ((PsiNewExpression)expr).getArrayInitializer();
if (arrayInitializer != null) {
final PsiExpression[] initializers = arrayInitializer.getInitializers();
for (int i = initializers.length - 1; i >= 0; i--) {
migrateExpressionType(initializers[i], elemenType, place, alreadyProcessed, true);
}
}
if (isGenericsArrayType(elemenType)){
markFailedConversion(new Pair<PsiType, PsiType>(originalType, migrationType), expr);
return;
}
myNewExpressionTypeChange.put(new TypeMigrationUsageInfo(expr), migrationType);
getTypeEvaluator().setType(new TypeMigrationUsageInfo(expr), migrationType);
return;
} else {
if (migrationType instanceof PsiClassType && originalType instanceof PsiClassType && ((PsiClassType)migrationType).rawType().isAssignableFrom(((PsiClassType)originalType).rawType())) {
final PsiClass originalClass = PsiUtil.resolveClassInType(originalType);
if (originalClass instanceof PsiAnonymousClass) {
originalType = ((PsiAnonymousClass)originalClass).getBaseClassType();
}
final PsiType type = TypeEvaluator.substituteType(migrationType, originalType, true, ((PsiClassType)originalType).resolveGenerics().getElement(),
JavaPsiFacade.getElementFactory(expr.getProject()).createType(((PsiClassType)originalType).resolve(), PsiSubstitutor.EMPTY));
if (type != null){
myNewExpressionTypeChange.put(new TypeMigrationUsageInfo(expr), type);
getTypeEvaluator().setType(new TypeMigrationUsageInfo(expr), type);
return;
}
}
}
}
}
convertExpression(expr, migrationType, originalType, isCovariant);
}
private static boolean isGenericsArrayType(final PsiType elemenType) {
if (elemenType instanceof PsiClassType && ((PsiClassType)elemenType).hasParameters()) {
return true;
} else if (elemenType instanceof PsiArrayType) {
final PsiType componentType = ((PsiArrayType)elemenType).getComponentType();
return isGenericsArrayType(componentType);
}
return false;
}
boolean addMigrationRoot(PsiElement element, PsiType type, final PsiElement place, boolean alreadyProcessed, final boolean isContraVariantPosition) {
return addMigrationRoot(element, type, place, alreadyProcessed, isContraVariantPosition, false);
}
boolean addMigrationRoot(PsiElement element,
PsiType type,
final PsiElement place,
boolean alreadyProcessed,
final boolean isContraVariantPosition,
final boolean userDefinedType) {
if (type.equals(PsiType.NULL)) {
return false;
}
final PsiElement resolved = Util.normalizeElement(element);
final SearchScope searchScope = myRules.getSearchScope();
if (!resolved.isPhysical() || !PsiSearchScopeUtil.isInScope(searchScope, resolved)) {
return false;
}
final PsiType originalType = getElementType(resolved);
LOG.assertTrue(originalType != null);
type = userDefinedType ? type : TypeEvaluator.substituteType(type, originalType, isContraVariantPosition);
if (!userDefinedType) {
if (originalType instanceof PsiClassType) {
final PsiClassType psiClassType = (PsiClassType)originalType;
if (psiClassType.resolve() instanceof PsiTypeParameter) {
return false;
}
for (PsiType paramType : psiClassType.getParameters()) {
if (paramType instanceof PsiClassType && ((PsiClassType)paramType).resolve() instanceof PsiTypeParameter) return false;
}
}
}
if (type instanceof PsiCapturedWildcardType) {
return false;
}
if (resolved instanceof PsiMethod) {
final PsiMethod method = ((PsiMethod)resolved);
final PsiMethod[] methods = OverridingMethodsSearch.search(method, method.getUseScope(), false).toArray(PsiMethod.EMPTY_ARRAY);
final OverridenUsageInfo overridenUsageInfo = new OverridenUsageInfo(method);
final OverriderUsageInfo[] overriders = new OverriderUsageInfo[methods.length];
for (int i = -1; i < methods.length; i++) {
final TypeMigrationUsageInfo m;
if (i < 0) {
m = overridenUsageInfo;
}
else {
overriders[i] = new OverriderUsageInfo(methods[i], method);
m = overriders[i];
}
alreadyProcessed = addRoot(m, type, place, alreadyProcessed);
}
overridenUsageInfo.setOverriders(overriders);
return !alreadyProcessed;
}
else if (resolved instanceof PsiParameter && ((PsiParameter)resolved).getDeclarationScope() instanceof PsiMethod) {
final PsiMethod method = PsiTreeUtil.getParentOfType(resolved, PsiMethod.class);
if (method == null) {
return false;
}
final int index = method.getParameterList().getParameterIndex(((PsiParameter)resolved));
final PsiMethod[] methods = OverridingMethodsSearch.search(method, method.getUseScope(), false).toArray(PsiMethod.EMPTY_ARRAY);
final OverriderUsageInfo[] overriders = new OverriderUsageInfo[methods.length];
final OverridenUsageInfo overridenUsageInfo = new OverridenUsageInfo(method.getParameterList().getParameters()[index]);
for (int i = -1; i < methods.length; i++) {
final PsiMethod m = i < 0 ? method : methods[i];
final PsiParameter p = m.getParameterList().getParameters()[index];
final TypeMigrationUsageInfo paramUsageInfo;
if (i < 0) {
paramUsageInfo = overridenUsageInfo;
}
else {
overriders[i] = new OverriderUsageInfo(p, method);
paramUsageInfo = overriders[i];
}
alreadyProcessed = addRoot(paramUsageInfo, type, place, alreadyProcessed);
}
overridenUsageInfo.setOverriders(overriders);
return !alreadyProcessed;
}
else {
return !addRoot(new TypeMigrationUsageInfo(resolved), type, place, alreadyProcessed);
}
}
@Nullable
public static PsiType getElementType(final PsiElement resolved) {
if (resolved instanceof PsiVariable) {
return ((PsiVariable)resolved).getType();
}
else {
if (resolved instanceof PsiMethod) {
return (((PsiMethod)resolved).getReturnType());
}
else if (resolved instanceof PsiExpression){
return (((PsiExpression)resolved).getType());
} else if (resolved instanceof PsiReferenceParameterList) {
final PsiElement parent = resolved.getParent();
LOG.assertTrue(parent instanceof PsiJavaCodeReferenceElement);
final PsiClass psiClass = (PsiClass)((PsiJavaCodeReferenceElement)parent).resolve();
return JavaPsiFacade.getElementFactory(parent.getProject()).createType(psiClass, TypeConversionUtil.getSuperClassSubstitutor(psiClass, PsiTreeUtil.getParentOfType(parent,
PsiClass.class),
PsiSubstitutor.EMPTY));
} else if (resolved instanceof PsiClass) {
return JavaPsiFacade.getElementFactory(resolved.getProject()).createType((PsiClass)resolved, PsiSubstitutor.EMPTY);
}
}
LOG.error("should not happen: " + resolved.getClass());
return null;
}
boolean addRoot(final TypeMigrationUsageInfo usageInfo, final PsiType type, final PsiElement place, boolean alreadyProcessed) {
if (myShowWarning && myMigrationRoots.size() > 10 && !ApplicationManager.getApplication().isUnitTestMode()) {
final MigrateException[] ex = new MigrateException[1];
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
if (Messages.showYesNoCancelDialog("Found more than 10 roots to migrate. Do you want to preview?", "Type Migration", Messages.getWarningIcon()) == DialogWrapper.OK_EXIT_CODE) {
ex[0] = new MigrateException();
}
}
});
}
catch (Exception e) {
//do nothing
}
if (ex[0] != null) throw ex[0];
myShowWarning = false;
}
rememberRootTrace(usageInfo, type, place, alreadyProcessed);
if (!alreadyProcessed && !getTypeEvaluator().setType(usageInfo, type)) {
alreadyProcessed = true;
}
if (!alreadyProcessed) myMigrationRoots.addFirst(new Pair<TypeMigrationUsageInfo, PsiType>(usageInfo, type));
return alreadyProcessed;
}
private void rememberRootTrace(final TypeMigrationUsageInfo usageInfo, final PsiType type, final PsiElement place, final boolean alreadyProcessed) {
if (myCurrentRoot != null) {
if (!alreadyProcessed) {
myProcessedRoots.add(usageInfo);
}
if (myProcessedRoots.contains(usageInfo)) {
HashSet<Pair<TypeMigrationUsageInfo, PsiType>> infos = myRootsTree.get(myCurrentRoot);
if (infos == null) {
infos = new HashSet<Pair<TypeMigrationUsageInfo, PsiType>>();
myRootsTree.put(myCurrentRoot, infos);
}
infos.add(Pair.create(usageInfo, type));
}
if (!(usageInfo instanceof OverriderUsageInfo)) { //hide the same usage for all overriders
setTypeUsage(usageInfo, place);
}
}
}
private void setTypeUsage(final TypeMigrationUsageInfo usageInfo, final PsiElement place) {
if (place != null) {
final Pair<TypeMigrationUsageInfo, TypeMigrationUsageInfo> rooted = Pair.create(usageInfo, myCurrentRoot);
Set<PsiElement> usages = myRootUsagesTree.get(rooted);
if (usages == null) {
usages = new HashSet<PsiElement>();
myRootUsagesTree.put(rooted, usages);
}
usages.add(place);
}
}
public void setTypeUsage(final PsiElement element, final PsiElement place) {
setTypeUsage(new TypeMigrationUsageInfo(element), place);
}
void markFailedConversion(final Pair<PsiType, PsiType> typePair, final PsiExpression expression) {
myFailedConversions.add(new Pair<PsiAnchor, PsiType>(PsiAnchor.create(expression), typePair.getSecond()));
}
void setConversionMapping(final PsiExpression expression, final Object obj) {
if (myConversions.get(expression) != null) {
return;
}
if (obj instanceof TypeConversionDescriptorBase) {
((TypeConversionDescriptorBase)obj).setRoot(myCurrentRoot);
}
myConversions.put(expression, obj);
}
public PsiReference[] markRootUsages(final PsiElement element, final PsiType migrationType) {
return markRootUsages(element, migrationType, ReferencesSearch.search(element, element.getUseScope(), false).toArray(new PsiReference[0]));
}
PsiReference[] markRootUsages(final PsiElement element, final PsiType migrationType, final PsiReference[] refs) {
final List<PsiReference> validReferences = new ArrayList<PsiReference>();
for (PsiReference ref1 : refs) {
final PsiElement ref = ref1.getElement();
if (ref != null) {
if (element instanceof PsiMethod) {
final PsiElement parent = Util.getEssentialParent(ref);
if (!(parent instanceof PsiMethodCallExpression)) {
continue;
}
getTypeEvaluator().setType(new TypeMigrationUsageInfo(parent), migrationType);
}
else if (element instanceof PsiVariable) {
if (ref instanceof PsiReferenceExpression) {
getTypeEvaluator().setType(new TypeMigrationUsageInfo(ref), PsiImplUtil.normalizeWildcardTypeByPosition(migrationType, (PsiReferenceExpression)ref));
}
}
else {
LOG.error("Method call expression or reference expression expected but found " + element.getClass().getName());
continue;
}
validReferences.add(ref1);
}
}
Collections.sort(validReferences, new Comparator<PsiReference>() {
public int compare(final PsiReference o1, final PsiReference o2) {
return o1.getElement().getTextOffset() - o2.getElement().getTextOffset();
}
});
return validReferences.toArray(new PsiReference[validReferences.size()]);
}
public void migrateRoot(final PsiElement root, final PsiType migrationType, final PsiReference[] usages) {
if (root instanceof PsiMethod) {
migrateMethodReturnExpression(migrationType, (PsiMethod)root);
}
else if (root instanceof PsiParameter && ((PsiParameter)root).getDeclarationScope() instanceof PsiMethod) {
migrateMethodCallExpressions(migrationType, (PsiParameter)root, null);
}
else if (root instanceof PsiVariable || root instanceof PsiExpression){
final PsiElement element = getContainingStatement(root);
element.accept(new TypeMigrationStatementProcessor(element, this));
} else if (root instanceof PsiReferenceParameterList) {
myClassTypeArgumentsChange.put(new TypeMigrationUsageInfo(root), (PsiClassType)migrationType);
new ClassTypeArgumentMigrationProcessor(this).migrateClassTypeParameter((PsiReferenceParameterList)root, migrationType);
}
final Set<PsiElement> processed = new HashSet<PsiElement>();
for (PsiReference usage : usages) {
migrateRootUsageExpression(usage, processed);
}
}
private static PsiElement getContainingStatement(final PsiElement root) {
final PsiStatement statement = PsiTreeUtil.getParentOfType(root, PsiStatement.class);
final PsiField field = PsiTreeUtil.getParentOfType(root, PsiField.class);
return statement != null ? statement : field != null ? field : root;
}
void migrateRootUsageExpression(final PsiReference usage, final Set<PsiElement> processed) {
final PsiElement ref = usage.getElement();
if (ref != null && ref.getLanguage() == StdLanguages.JAVA) {
final PsiElement element = getContainingStatement(ref);
if (element != null && !processed.contains(element)) {
processed.add(element);
element.accept(new TypeMigrationStatementProcessor(ref, this));
}
}
}
void migrateMethodCallExpressions(final PsiType migrationType, final PsiParameter param, final PsiClass psiClass) {
boolean checkNumberOfArguments = false;
if (param.getType() instanceof PsiEllipsisType && !(migrationType instanceof PsiEllipsisType)) {
checkNumberOfArguments = true;
}
final PsiType strippedType =
migrationType instanceof PsiEllipsisType ? ((PsiEllipsisType)migrationType).getComponentType() : migrationType;
final PsiMethod method = (PsiMethod)param.getDeclarationScope();
final PsiParameterList parameterList = method.getParameterList();
final int parametersCount = parameterList.getParametersCount();
final int index = parameterList.getParameterIndex(param);
final List<PsiReference> refs = filterReferences(psiClass, ReferencesSearch.search(method, method.getUseScope().intersectWith(myRules.getSearchScope()), false));
for (PsiReference ref1 : refs) {
final PsiElement ref = ref1.getElement();
final PsiElement parent = Util.getEssentialParent(ref);
if (parent instanceof PsiCallExpression) {
final PsiExpressionList argumentList = ((PsiCallExpression)parent).getArgumentList();
if (argumentList != null) {
final PsiExpression[] expressions = argumentList.getExpressions();
if (checkNumberOfArguments && parametersCount != expressions.length) {
markFailedConversion(new Pair<PsiType, PsiType>(param.getType(), migrationType), (PsiCallExpression)parent);
}
if (index > -1 && index < expressions.length) {
for (int idx = index; idx < (param.isVarArgs() ? expressions.length : index + 1); idx++) {
final PsiExpression actual = expressions[idx];
final PsiType type = getTypeEvaluator().evaluateType(actual);
if (type != null) {
migrateExpressionType(actual, strippedType, parent, TypeConversionUtil.isAssignable(strippedType, type), true);
}
}
}
}
} else if (ref instanceof PsiDocTagValue) {
myConversions.put(ref, method);
}
}
}
private void migrateMethodReturnExpression(final PsiType migrationType, final PsiMethod method) {
final PsiCodeBlock block = method.getBody();
if (block != null) {
block.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitReturnStatement(PsiReturnStatement statement) {
final PsiExpression value = statement.getReturnValue();
if (value != null) {
final PsiType type = getTypeEvaluator().evaluateType(value);
if (type != null && !type.equals(migrationType)) {
migrateExpressionType(value, migrationType, statement, TypeConversionUtil.isAssignable(migrationType, type), true);
}
}
}
});
}
}
private void iterate() {
final LinkedList<Pair<TypeMigrationUsageInfo, PsiType>> roots =
(LinkedList<Pair<TypeMigrationUsageInfo, PsiType>>)myMigrationRoots.clone();
myMigrationRoots = new LinkedList<Pair<TypeMigrationUsageInfo, PsiType>>();
final PsiReference[][] cachedUsages = new PsiReference[roots.size()][];
int j = 0;
for (final Pair<TypeMigrationUsageInfo, PsiType> p : roots) {
cachedUsages[j++] = markRootUsages(p.getFirst().getElement(), p.getSecond());
}
j = 0;
for (final Pair<TypeMigrationUsageInfo, PsiType> root : roots) {
myCurrentRoot = root.getFirst();
migrateRoot(root.getFirst().getElement(), root.getSecond(), cachedUsages[j++]);
}
}
private void migrate(boolean autoMigrate, final PsiElement... victims) {
myMigrationRoots = new LinkedList<Pair<TypeMigrationUsageInfo, PsiType>>();
myTypeEvaluator = new TypeEvaluator(myMigrationRoots, this);
final PsiType rootType = myRules.getMigrationRootType();
for (PsiElement victim : victims) {
addMigrationRoot(victim, rootType, null, false, true, true);
}
if (autoMigrate) {
while (myMigrationRoots.size() > 0) {
iterate();
}
}
}
public TypeEvaluator getTypeEvaluator() {
return myTypeEvaluator;
}
public Map<TypeMigrationUsageInfo, HashSet<Pair<TypeMigrationUsageInfo, PsiType>>> getRootsTree() {
return myRootsTree;
}
public void setCurrentRoot(final TypeMigrationUsageInfo currentRoot) {
myCurrentRoot = currentRoot;
}
public LinkedList<Pair<TypeMigrationUsageInfo, PsiType>> getMigrationRoots() {
return myMigrationRoots;
}
public static List<PsiReference> filterReferences(final PsiClass psiClass, final Query<PsiReference> memberReferences) {
final List<PsiReference> refs = new ArrayList<PsiReference>();
for (PsiReference memberReference : memberReferences) {
if (psiClass == null) {
refs.add(memberReference);
} else {
final PsiElement referencedElement = memberReference.getElement();
if (referencedElement instanceof PsiReferenceExpression) {
final PsiExpression qualifierExpression = ((PsiReferenceExpression)referencedElement).getQualifierExpression();
if (qualifierExpression != null) {
final PsiType qualifierType = qualifierExpression.getType();
if (qualifierType instanceof PsiClassType && psiClass == ((PsiClassType)qualifierType).resolve()) {
refs.add(memberReference);
}
} else {
if (psiClass == PsiTreeUtil.getParentOfType(referencedElement, PsiClass.class)) {
refs.add(memberReference);
}
}
}
}
}
return refs;
}
public String getMigrationReport() {
final StringBuffer buffer = new StringBuffer();
buffer.append("Types:\n" + getTypeEvaluator().getReport() + "\n");
buffer.append("Conversions:\n");
final String[] conversions = new String[myConversions.size()];
int k = 0;
for (final PsiElement expr : myConversions.keySet()) {
final Object conv = myConversions.get(expr);
if (conv instanceof Pair && ((Pair)conv).first == null) {
conversions[k++] = (expr.getText() + " -> " + ((Pair)conv).second + "\n");
} else {
conversions[k++] = (expr.getText() + " -> " + conv + "\n");
}
}
Arrays.sort(conversions, new Comparator() {
public int compare(Object x, Object y) {
return ((String)x).compareTo((String)y);
}
});
for (String conversion : conversions) {
buffer.append(conversion);
}
buffer.append("\nNew expression type changes:\n");
final String[] newchanges = new String[myNewExpressionTypeChange.size()];
k = 0;
for (final Map.Entry<TypeMigrationUsageInfo, PsiType> entry : myNewExpressionTypeChange.entrySet()) {
newchanges[k++] = entry.getKey().getElement().getText() + " -> " + entry.getValue().getCanonicalText() + "\n";
}
Arrays.sort(newchanges, new Comparator() {
public int compare(Object x, Object y) {
return ((String)x).compareTo((String)y);
}
});
for (String newchange : newchanges) {
buffer.append(newchange);
}
buffer.append("Fails:\n");
final ArrayList<Pair<PsiAnchor, PsiType>> failsList = new ArrayList<Pair<PsiAnchor, PsiType>>(myFailedConversions);
Collections.sort(failsList, new Comparator<Pair<PsiAnchor, PsiType>>() {
public int compare(final Pair<PsiAnchor, PsiType> o1, final Pair<PsiAnchor, PsiType> o2) {
final PsiElement element1 = o1.getFirst().retrieve();
final PsiElement element2 = o2.getFirst().retrieve();
if (element1 == null || element2 == null) return 0;
return element1.getText().compareTo(element2.getText());
}
});
for (final Pair<PsiAnchor, PsiType> p : failsList) {
final PsiElement element = p.getFirst().retrieve();
if (element != null) {
buffer.append(element.getText() + "->" + p.getSecond().getCanonicalText() + "\n");
}
}
return buffer.toString();
}
public static class MigrateException extends RuntimeException {
}
}
@@ -0,0 +1,153 @@
package com.intellij.refactoring.typeMigration;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.psi.*;
import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.refactoring.typeMigration.ui.FailedConversionsDialog;
import com.intellij.refactoring.typeMigration.ui.MigrationPanel;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import com.intellij.ui.content.Content;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewDescriptor;
import com.intellij.usageView.UsageViewManager;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class TypeMigrationProcessor extends BaseRefactoringProcessor {
private PsiElement[] myRoot;
private final TypeMigrationRules myRules;
private TypeMigrationLabeler myLabeler;
public TypeMigrationProcessor(final Project project, final PsiElement root, final TypeMigrationRules rules) {
this(project, new PsiElement[]{root}, rules);
}
public TypeMigrationProcessor(final Project project, final PsiElement[] roots, final TypeMigrationRules rules) {
super(project);
myRoot = roots;
myRules = rules;
}
protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) {
return new TypeMigrationViewDescriptor(myRoot[0]);
}
protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
if (hasFailedConversions()) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
throw new RuntimeException(StringUtil.join(myLabeler.getFailedConversionsReport(), "\n"));
}
FailedConversionsDialog dialog = new FailedConversionsDialog(myLabeler.getFailedConversionsReport(), myProject);
dialog.show();
if (!dialog.isOK()) {
final int exitCode = dialog.getExitCode();
prepareSuccessful();
if (exitCode == FailedConversionsDialog.VIEW_USAGES_EXIT_CODE) {
previewRefactoring(refUsages.get());
}
return false;
}
}
prepareSuccessful();
return true;
}
public boolean hasFailedConversions() {
return myLabeler.hasFailedConversions();
}
@Override
protected void previewRefactoring(final UsageInfo[] usages) {
MigrationPanel panel = new MigrationPanel(myRoot[0], myLabeler, myProject, isPreviewUsages());
String text;
if (myRoot[0] instanceof PsiField) {
text = "field \'" + ((PsiField)myRoot[0]).getName() + "\'";
} else if (myRoot[0] instanceof PsiParameter) {
text = "parameter \'" + ((PsiParameter)myRoot[0]).getName() + "\'";
} else if (myRoot[0] instanceof PsiLocalVariable) {
text = "variable \'" + ((PsiLocalVariable)myRoot[0]).getName() + "\'";
} else if (myRoot[0] instanceof PsiMethod) {
text = "method \'" + ((PsiMethod)myRoot[0]).getName() + "\' return";
} else {
text = myRoot.toString();
}
Content content = UsageViewManager.getInstance(myProject)
.addContent("Migrate Type of " +
text +
" from \'" +
TypeMigrationLabeler.getElementType(myRoot[0]).getPresentableText() +
"\' to \'" +
myRules.getMigrationRootType().getPresentableText() +
"\'", false, panel, true, true);
panel.setContent(content);
ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.FIND).activate(null);
}
@NotNull
public UsageInfo[] findUsages() {
myLabeler = new TypeMigrationLabeler(myRules);
try {
return myLabeler.getMigratedUsages(!isPreviewUsages(), myRoot);
}
catch (TypeMigrationLabeler.MigrateException e) {
setPreviewUsages(true);
return myLabeler.getMigratedUsages(false, myRoot);
}
}
protected void refreshElements(PsiElement[] elements) {
myRoot = elements;
}
public void performRefactoring(UsageInfo[] usages) {
change(myLabeler, usages);
}
public static void change(TypeMigrationLabeler labeler, UsageInfo[] usages) {
List<UsageInfo> nonCodeUsages = new ArrayList<UsageInfo>();
for (UsageInfo usage : usages) {
if (((TypeMigrationUsageInfo)usage).isExcluded()) continue;
final PsiElement element = usage.getElement();
if (element instanceof PsiVariable || element instanceof PsiMember || element instanceof PsiExpression || element instanceof PsiReferenceParameterList) {
labeler.change((TypeMigrationUsageInfo)usage);
} else {
nonCodeUsages.add(usage);
}
}
for (UsageInfo usageInfo : nonCodeUsages) {
final PsiElement element = usageInfo.getElement();
if (element != null) {
final PsiReference reference = element.getReference();
if (reference != null) {
final Object target = labeler.getConversion(element);
if (target instanceof PsiMember) {
try {
reference.bindToElement((PsiElement)target);
}
catch (IncorrectOperationException e) {
//skip
}
}
}
}
}
}
public TypeMigrationLabeler getLabeler() {
return myLabeler;
}
protected String getCommandName() {
return "TypeMigration";
}
}
@@ -0,0 +1,152 @@
/*
* User: anna
* Date: 04-Apr-2008
*/
package com.intellij.refactoring.typeMigration;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.ChildRole;
import com.intellij.psi.impl.source.tree.CompositeElement;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import com.intellij.util.IncorrectOperationException;
import java.util.Map;
public class TypeMigrationReplacementUtil {
public static final Logger LOG = Logger.getInstance("#" + TypeMigrationReplacementUtil.class.getName());
private TypeMigrationReplacementUtil() {
}
public static void replaceExpression(PsiExpression expression, final Project project, Object conversion) {
if (conversion instanceof TypeConversionDescriptorBase) {
((TypeConversionDescriptorBase)conversion).replace(expression);
} else if (conversion instanceof String) {
String replacement = (String)conversion;
try {
expression.replace(
JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(replacement, expression));
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
else if (expression instanceof PsiReferenceExpression) {
final PsiElement resolved = ((PsiReferenceExpression)expression).resolve();
final PsiMember replacer = ((PsiMember)conversion);
final String method = ((PsiMember)resolved).getName();
final String ref = expression.getText();
final String newref = ref.substring(0, ref.lastIndexOf(method)) + replacer.getName();
if (conversion instanceof PsiMethod) {
if (resolved instanceof PsiMethod) {
try {
expression.replace(
JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(newref, expression));
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
else {
try {
expression.replace(JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(
newref + "()", expression));
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
}
else if (conversion instanceof PsiField) {
if (resolved instanceof PsiField) {
try {
expression.replace(
JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(newref, expression));
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
else {
final PsiElement parent = Util.getEssentialParent(expression);
if (parent instanceof PsiMethodCallExpression) {
try {
parent.replace(
JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(newref, expression));
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
}
}
}
}
static void migratePsiMemeberType(final PsiElement element, final Project project, PsiType migratedType) {
try {
if (!migratedType.isValid()) {
migratedType = JavaPsiFacade.getElementFactory(project).createTypeByFQClassName(migratedType.getCanonicalText());
}
final PsiTypeElement typeElement =
JavaPsiFacade.getInstance(project).getElementFactory().createTypeElement(migratedType);
if (element instanceof PsiMethod) {
final PsiTypeElement returnTypeElement = ((PsiMethod)element).getReturnTypeElement();
if (returnTypeElement != null) {
returnTypeElement.replace(typeElement);
}
}
else if (element instanceof PsiVariable) {
final PsiTypeElement varTypeElement = ((PsiVariable)element).getTypeElement();
if (varTypeElement != null) {
varTypeElement.replace(typeElement);
}
}
else {
LOG.error("Must not happen: " + element.getClass().getName());
}
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
static void replaceNewExpressionType(final Project project, final PsiNewExpression expression, final Map.Entry<TypeMigrationUsageInfo, PsiType> info) {
final PsiType changeType = info.getValue();
if (changeType != null) {
try {
final PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference();
final PsiType componentType = changeType.getDeepComponentType();
if (classReference != null) {
replaceTypeWithClassReferenceOrKeyword(project, componentType, classReference);
}
else {
final PsiElement typeKeyword = getTypeKeyword(expression);
if (typeKeyword != null) {
replaceTypeWithClassReferenceOrKeyword(project, componentType, typeKeyword);
}
}
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
}
private static void replaceTypeWithClassReferenceOrKeyword(Project project, PsiType componentType, PsiElement typePlace) {
final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
if (componentType instanceof PsiClassType) {
typePlace.replace(factory.createReferenceElementByType((PsiClassType)componentType));
} else {
typePlace.replace(getTypeKeyword(((PsiNewExpression)factory.createExpressionFromText("new " + componentType.getPresentableText() + "[0]", typePlace))));
}
}
private static PsiElement getTypeKeyword(PsiNewExpression expression) {
return ((CompositeElement)expression).findChildByRoleAsPsiElement(ChildRole.TYPE_KEYWORD);
}
}
@@ -0,0 +1,93 @@
package com.intellij.refactoring.typeMigration;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.typeMigration.rules.*;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.LinkedList;
/**
* Created by IntelliJ IDEA.
* User: db
* Date: Oct 2, 2004
* Time: 9:24:23 PM
* To change this template use File | Settings | File Templates.
*/
public class TypeMigrationRules {
private final LinkedList<TypeConversionRule> myConversionRules = new LinkedList<TypeConversionRule>();
private final PsiType myRootType;
private PsiType myMigrationRootType;
private SearchScope mySearchScope;
public TypeMigrationRules(final PsiType root) {
myRootType = root;
myConversionRules.add(new RootTypeConversionRule());
myConversionRules.addAll(Arrays.asList(Extensions.getExtensions(TypeConversionRule.EP_NAME)));
}
public void setMigrationRootType(PsiType migrationRootType) {
myMigrationRootType = migrationRootType;
}
public PsiType getRootType() {
return myRootType;
}
public PsiType getMigrationRootType() {
return myMigrationRootType;
}
public void addConversionDescriptor(TypeConversionRule rule) {
myConversionRules.add(rule);
}
@NonNls
@Nullable
public TypeConversionDescriptorBase findConversion(final PsiType from, final PsiType to, PsiMember member, final PsiExpression context, final boolean isCovariantPosition,
final TypeMigrationLabeler labeler) {
final TypeConversionDescriptorBase conversion = findConversion(from, to, member, context, labeler);
if (conversion != null) return conversion;
if (isCovariantPosition) {
if (to instanceof PsiEllipsisType) {
if (TypeConversionUtil.isAssignable(((PsiEllipsisType)to).getComponentType(), from)) return new TypeConversionDescriptorBase();
}
if (TypeConversionUtil.isAssignable(to, from)) return new TypeConversionDescriptorBase();
}
if (!isCovariantPosition && TypeConversionUtil.isAssignable(from, to)) return new TypeConversionDescriptorBase();
return null;
}
@Nullable
public TypeConversionDescriptorBase findConversion(PsiType from, PsiType to, PsiMember member, PsiExpression context, TypeMigrationLabeler labeler) {
for (TypeConversionRule descriptor : myConversionRules) {
final TypeConversionDescriptorBase conversion = descriptor.findConversion(from, to, member, context, labeler);
if (conversion != null) return conversion;
}
return null;
}
public void setBoundScope(final SearchScope searchScope) {
mySearchScope = searchScope;
}
public SearchScope getSearchScope() {
return mySearchScope;
}
@Nullable
public Pair<PsiType, PsiType> bindTypeParameters(final PsiType from, final PsiType to, final PsiMethod method, final PsiExpression context, final TypeMigrationLabeler labeler) {
for (TypeConversionRule conversionRule : myConversionRules) {
final Pair<PsiType, PsiType> typePair = conversionRule.bindTypeParameters(from, to, method, context, labeler);
if (typePair != null) return typePair;
}
return null;
}
}
@@ -0,0 +1,505 @@
/*
* User: anna
* Date: 04-Apr-2008
*/
package com.intellij.refactoring.typeMigration;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.Map;
class TypeMigrationStatementProcessor extends JavaRecursiveElementVisitor {
private final PsiElement myStatement;
private final TypeMigrationLabeler myLabeler;
private static final Logger LOG = Logger.getInstance("#" + TypeMigrationStatementProcessor.class.getName());
private final TypeEvaluator myTypeEvaluator;
public TypeMigrationStatementProcessor(final PsiElement expression, TypeMigrationLabeler labeler) {
myStatement = expression;
myLabeler = labeler;
myTypeEvaluator = myLabeler.getTypeEvaluator();
}
@Override
public void visitAssignmentExpression(PsiAssignmentExpression expression) {
super.visitAssignmentExpression(expression);
final PsiExpression lExpression = expression.getLExpression();
final TypeView left = new TypeView(lExpression);
final PsiExpression rExpression = expression.getRExpression();
if (rExpression == null) return;
final TypeView right = new TypeView(rExpression);
final IElementType sign = expression.getOperationTokenType();
if (sign != JavaTokenType.EQ) {
final IElementType binaryOperator = TypeConversionUtil.convertEQtoOperation(sign);
if (!TypeConversionUtil.isBinaryOperatorApplicable(binaryOperator, left.getType(), right.getType(), false)) {
if (left.isChanged()) {
findConversionOrFail(expression, lExpression, left.getTypePair());
}
if (right.isChanged()) {
findConversionOrFail(expression, rExpression, right.getTypePair());
}
return;
}
}
switch (TypeInfection.getInfection(left, right)) {
case TypeInfection.NONE_INFECTED:
break;
case TypeInfection.LEFT_INFECTED:
myLabeler.migrateExpressionType(rExpression, left.getType(), myStatement, TypeConversionUtil.isAssignable(left.getType(), right.getType()), true);
break;
case TypeInfection.RIGHT_INFECTED:
myLabeler.migrateExpressionType(lExpression, right.getType(), myStatement, TypeConversionUtil.isAssignable(left.getType(), right.getType()), false);
break;
case TypeInfection.BOTH_INFECTED:
addTypeUsage(lExpression);
addTypeUsage(rExpression);
break;
default:
LOG.error("Must not happen.");
}
}
@Override
public void visitArrayAccessExpression(final PsiArrayAccessExpression expression) {
super.visitArrayAccessExpression(expression);
final PsiExpression indexExpression = expression.getIndexExpression();
if (indexExpression != null) {
checkIndexExpression(indexExpression);
}
final TypeView typeView = new TypeView(expression.getArrayExpression());
if (typeView.isChanged() && typeView.getType() instanceof PsiClassType) {
final TypeConversionDescriptorBase conversion =
myLabeler.getRules().findConversion(typeView.getTypePair().first, typeView.getType(), null, expression, false, myLabeler);
if (conversion == null) {
myLabeler.markFailedConversion(typeView.getTypePair(), expression);
}
else {
myLabeler.setConversionMapping(expression, conversion);
myTypeEvaluator.setType(new TypeMigrationUsageInfo(expression), myTypeEvaluator.evaluateType(expression));
}
}
}
@Override
public void visitSwitchLabelStatement(PsiSwitchLabelStatement statement) {
super.visitSwitchLabelStatement(statement);
final PsiExpression caseValue = statement.getCaseValue();
if (caseValue != null) {
final TypeView typeView = new TypeView(caseValue);
if (typeView.isChanged()) {
final PsiSwitchStatement switchStatement = statement.getEnclosingSwitchStatement();
if (switchStatement != null) {
final PsiExpression expression = switchStatement.getExpression();
myLabeler.migrateExpressionType(expression, typeView.getType(), myStatement, false, false);
}
}
}
}
@Override
public void visitInstanceOfExpression(final PsiInstanceOfExpression expression) {
super.visitInstanceOfExpression(expression);
final PsiTypeElement typeElement = expression.getCheckType();
if (typeElement != null) {
final PsiExpression consideredExpression = expression.getOperand();
final PsiType migrationType = myTypeEvaluator.evaluateType(consideredExpression);
final PsiType fixedType = typeElement.getType();
if (migrationType != null && !TypeConversionUtil.isAssignable(migrationType, fixedType)) {
myLabeler.markFailedConversion(new Pair<PsiType, PsiType>(fixedType, migrationType), consideredExpression);
}
}
}
@Override
public void visitTypeCastExpression(final PsiTypeCastExpression expression) {
super.visitTypeCastExpression(expression);
final PsiTypeElement typeElement = expression.getCastType();
if (typeElement != null) {
final PsiType fixedType = typeElement.getType();
final PsiType migrationType = myTypeEvaluator.evaluateType(expression.getOperand());
if (migrationType != null && !TypeConversionUtil.areTypesConvertible(migrationType, fixedType)) {
myLabeler.markFailedConversion(new Pair<PsiType, PsiType>(fixedType, migrationType), expression);
}
}
}
@Override
public void visitVariable(PsiVariable variable) {
super.visitVariable(variable);
final PsiExpression initializer = variable.getInitializer();
if (initializer != null && initializer.getType() != null) {
processVariable(variable, initializer, null, null, null, false);
}
}
@Override
public void visitReturnStatement(PsiReturnStatement statement) { // has to change method return type corresponding to new value type
super.visitReturnStatement(statement);
final PsiMethod method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class);
final PsiExpression value = statement.getReturnValue();
if (method != null && value != null) {
final PsiType returnType = method.getReturnType();
final PsiType valueType = myTypeEvaluator.evaluateType(value);
if (returnType != null && valueType != null) {
myLabeler.addMigrationRoot(method, valueType, myStatement, TypeConversionUtil.isAssignable(returnType, valueType), true);
}
}
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
final PsiExpression qualifierExpression = expression.getQualifierExpression();
if (qualifierExpression != null && qualifierExpression.isPhysical()) {
qualifierExpression.accept(this);
final TypeView qualifierView = new TypeView(qualifierExpression);
if (qualifierView.isChanged()) {
final PsiMember member = (PsiMember)expression.advancedResolve(false).getElement();
if (member == null) return;
final Pair<PsiType, PsiType> typePair = qualifierView.getTypePair();
final TypeConversionDescriptorBase conversion = myLabeler.getRules().findConversion(typePair.getFirst(), typePair.getSecond(), member, expression, false, myLabeler);
if (conversion == null) {
myLabeler.markFailedConversion(typePair, qualifierExpression);
} else {
final PsiElement parent = Util.getEssentialParent(expression);
if (parent instanceof PsiMethodCallExpression) {
myLabeler.setConversionMapping((PsiMethodCallExpression)parent, conversion);
myTypeEvaluator.setType(new TypeMigrationUsageInfo(parent), myTypeEvaluator.evaluateType((PsiExpression)parent));
} else {
myLabeler.setConversionMapping(expression, conversion);
myTypeEvaluator.setType(new TypeMigrationUsageInfo(expression), myTypeEvaluator.evaluateType(expression));
}
}
}
}
}
@Override
public void visitIfStatement(PsiIfStatement statement) {
super.visitIfStatement(statement);
final PsiExpression condition = statement.getCondition();
if (condition != null) {
final TypeView view = new TypeView(condition);
if (view.isChanged()) { //means that boolean condition becomes non-boolean
findConversionOrFail(condition, condition, view.getTypePair());
}
}
}
@Override
public void visitForeachStatement(final PsiForeachStatement statement) {
super.visitForeachStatement(statement);
final PsiExpression value = statement.getIteratedValue();
final PsiParameter psiParameter = statement.getIterationParameter();
if (value != null) {
final TypeView typeView = new TypeView(value);
PsiType psiType = typeView.getType();
if (psiType instanceof PsiArrayType) {
psiType = ((PsiArrayType)psiType).getComponentType();
}
else if (psiType instanceof PsiClassType) {
final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)psiType).resolveGenerics();
final PsiClass psiClass = resolveResult.getElement();
final Project project = statement.getProject();
final PsiClass iterableClass =
JavaPsiFacade.getInstance(project).findClass("java.lang.Iterable", GlobalSearchScope.allScope(project));
if (iterableClass == null) return;
if (!InheritanceUtil.isInheritorOrSelf(psiClass, iterableClass, true)) {
findConversionOrFail(value, value, typeView.getTypePair());
return;
}
final PsiSubstitutor iterableParamSubstitutor =
TypeConversionUtil.getClassSubstitutor(iterableClass, psiClass, PsiSubstitutor.EMPTY);
LOG.assertTrue(iterableParamSubstitutor != null);
final PsiTypeParameter[] typeParameters = iterableClass.getTypeParameters();
LOG.assertTrue(typeParameters.length == 1);
psiType = resolveResult.getSubstitutor().substitute(iterableParamSubstitutor.substitute(typeParameters[0]));
if (psiType instanceof PsiWildcardType) {
psiType = ((PsiWildcardType)psiType).getExtendsBound();
}
}
else {
return;
}
final TypeView left = new TypeView(psiParameter, null, null);
if (TypeInfection.getInfection(left, typeView) == TypeInfection.LEFT_INFECTED) {
PsiType iterableType;
final PsiType typeViewType = typeView.getType();
if (typeViewType instanceof PsiArrayType) {
iterableType = left.getType().createArrayType();
} else {
final PsiClass iterableClass = PsiUtil.resolveClassInType(typeViewType);
LOG.assertTrue(iterableClass != null);
final PsiTypeParameter[] typeParameters = iterableClass.getTypeParameters();
LOG.assertTrue(typeParameters.length == 1);
final Map<PsiTypeParameter, PsiType> substMap = Collections.singletonMap(typeParameters[0], left.getType());
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(iterableClass.getProject());
iterableType = factory.createType(iterableClass, factory.createSubstitutor(substMap));
}
myLabeler.migrateExpressionType(value, iterableType, myStatement, TypeConversionUtil.isAssignable(iterableType, typeViewType), true);
} else {
processVariable(psiParameter, value, psiType, null, null, false);
}
}
}
@Override
public void visitNewExpression(final PsiNewExpression expression) {
super.visitNewExpression(expression);
final PsiExpression[] dimensions = expression.getArrayDimensions();
for (PsiExpression dimension : dimensions) {
checkIndexExpression(dimension);
}
final PsiArrayInitializerExpression arrayInitializer = expression.getArrayInitializer();
if (arrayInitializer != null) {
processArrayInitializer(arrayInitializer, expression);
}
}
@Override
public void visitArrayInitializerExpression(final PsiArrayInitializerExpression expression) {
super.visitArrayInitializerExpression(expression);
processArrayInitializer(expression, expression);
}
@Override
public void visitPostfixExpression(final PsiPostfixExpression expression) {
super.visitPostfixExpression(expression);
processUnaryExpression(expression, expression.getOperationSign());
}
@Override
public void visitPrefixExpression(final PsiPrefixExpression expression) {
super.visitPrefixExpression(expression);
processUnaryExpression(expression, expression.getOperationSign());
}
private void processUnaryExpression(final PsiExpression expression, PsiJavaToken sign) {
final TypeView typeView = new TypeView(expression);
if (typeView.isChanged()) {
if (!TypeConversionUtil.isUnaryOperatorApplicable(sign, typeView.getType())) {
findConversionOrFail(expression, expression, typeView.getTypePair());
}
}
}
private void findConversionOrFail(PsiExpression expression, PsiExpression toFail, Pair<PsiType, PsiType> typePair) {
final TypeConversionDescriptorBase conversion = myLabeler.getRules().findConversion(typePair.getFirst(), typePair.getSecond(), null, expression, myLabeler);
if (conversion == null) {
myLabeler.markFailedConversion(typePair, toFail);
}
else {
myLabeler.setConversionMapping(expression, conversion);
myTypeEvaluator.setType(new TypeMigrationUsageInfo(expression), myTypeEvaluator.evaluateType(expression));
}
}
@Override
public void visitBinaryExpression(final PsiBinaryExpression expression) {
super.visitBinaryExpression(expression);
final PsiExpression lOperand = expression.getLOperand();
final TypeView left = new TypeView(lOperand);
final PsiExpression rOperand = expression.getROperand();
if (rOperand == null) return;
final TypeView right = new TypeView(rOperand);
if (!TypeConversionUtil.isBinaryOperatorApplicable(expression.getOperationSign().getTokenType(), left.getType(), right.getType(), false)) {
if (left.isChanged()) {
findConversionOrFail(expression, lOperand, left.getTypePair());
}
if (right.isChanged()) {
findConversionOrFail(expression, rOperand, right.getTypePair());
}
}
}
private void processArrayInitializer(final PsiArrayInitializerExpression expression, final PsiExpression parentExpression) {
final PsiExpression[] initializers = expression.getInitializers();
PsiType migrationType = null;
for (PsiExpression initializer : initializers) {
final TypeView typeView = new TypeView(initializer);
if (typeView.isChanged()) {
final PsiType type = typeView.getType();
if (migrationType == null || !TypeConversionUtil.isAssignable(migrationType, type)) {
if (migrationType != null && !TypeConversionUtil.isAssignable(type, migrationType)) {
myLabeler.markFailedConversion(new Pair<PsiType, PsiType>(parentExpression.getType(), type), parentExpression);
return;
}
migrationType = type;
}
}
}
final PsiType exprType = expression.getType();
if (migrationType != null && exprType instanceof PsiArrayType) {
final boolean alreadyProcessed = TypeConversionUtil.isAssignable(((PsiArrayType)exprType).getComponentType(), migrationType);
myLabeler.migrateExpressionType(parentExpression, alreadyProcessed ? exprType : migrationType.createArrayType(), expression, alreadyProcessed, true);
}
}
private void checkIndexExpression(final PsiExpression indexExpression) {
final PsiType indexType = myTypeEvaluator.evaluateType(indexExpression);
if (indexType != null && !TypeConversionUtil.isAssignable(PsiType.INT, indexType)) {
myLabeler.markFailedConversion(new Pair<PsiType, PsiType>(indexExpression.getType(), indexType), indexExpression);
}
}
@Override
public void visitMethodCallExpression(final PsiMethodCallExpression methodCallExpression) {
super.visitMethodCallExpression(methodCallExpression);
final JavaResolveResult resolveResult = methodCallExpression.resolveMethodGenerics();
final PsiElement method = resolveResult.getElement();
if (method instanceof PsiMethod) {
final PsiExpression[] psiExpressions = methodCallExpression.getArgumentList().getExpressions();
final PsiParameter[] originalParams = ((PsiMethod)method).getParameterList().getParameters();
final PsiSubstitutor evalSubstitutor = myTypeEvaluator.createMethodSubstitution(originalParams, psiExpressions, (PsiMethod)method, methodCallExpression);
for (int i = 0; i < psiExpressions.length; i++) {
PsiParameter originalParameter;
if (originalParams.length <= i) {
if (originalParams[originalParams.length - 1].isVarArgs()) {
originalParameter = originalParams[originalParams.length - 1];
} else {
continue;
}
}
else {
originalParameter = originalParams[i];
}
processVariable(originalParameter, psiExpressions[i], null, resolveResult.getSubstitutor(), evalSubstitutor, true);
}
final PsiExpression qualifier = methodCallExpression.getMethodExpression().getQualifierExpression();
if (qualifier != null && qualifier.isPhysical() && !new TypeView(qualifier).isChanged()) { //substitute property otherwise
final PsiType qualifierType = qualifier.getType();
if (qualifierType instanceof PsiClassType) {
final PsiClassType.ClassResolveResult classResolveResult = ((PsiClassType)qualifierType).resolveGenerics();
final PsiType migrationType =
classResolveResult.getSubstitutor().substitute(evalSubstitutor.substitute(JavaPsiFacade.getElementFactory(myStatement.getProject()).createType(classResolveResult.getElement(), PsiSubstitutor.EMPTY)));
myLabeler.migrateExpressionType(qualifier, migrationType, myStatement, migrationType.equals(qualifierType), true);
}
}
}
}
private void processVariable(final PsiVariable variable,
final PsiExpression value,
final PsiType migrationType,
final PsiSubstitutor varSubstitutor,
final PsiSubstitutor evalSubstitutor,
final boolean isCovariantPosition) {
final TypeView right = new TypeView(value);
final TypeView left = new TypeView(variable, varSubstitutor, evalSubstitutor);
switch (TypeInfection.getInfection(left, right)) {
case TypeInfection.NONE_INFECTED:
break;
case TypeInfection.LEFT_INFECTED:
myLabeler.migrateExpressionType(value, left.getType(), myStatement, TypeConversionUtil.isAssignable(left.getType(), right.getType()), true);
break;
case TypeInfection.RIGHT_INFECTED:
PsiType psiType = migrationType != null ? migrationType : right.getType();
if (!myLabeler.addMigrationRoot(variable, psiType, myStatement, TypeConversionUtil.isAssignable(left.getType(), psiType), true)) {
myLabeler.convertExpression(value, psiType, left.getType(), isCovariantPosition);
}
break;
case TypeInfection.BOTH_INFECTED:
addTypeUsage(variable);
break;
default:
LOG.error("Must not happen.");
}
}
private void addTypeUsage(final PsiElement typedElement) {
if (typedElement instanceof PsiReferenceExpression) {
myLabeler.setTypeUsage(((PsiReferenceExpression)typedElement).resolve(), myStatement);
}
else if (typedElement instanceof PsiMethodCallExpression) {
myLabeler.setTypeUsage(((PsiMethodCallExpression)typedElement).resolveMethod(), myStatement);
}
else {
myLabeler.setTypeUsage(typedElement, myStatement);
}
}
private class TypeView {
final PsiType myOriginType;
final PsiType myType;
final boolean myChanged;
public TypeView(@NotNull PsiExpression expr) {
PsiType exprType = expr.getType();
exprType = exprType instanceof PsiEllipsisType ? ((PsiEllipsisType)exprType).toArrayType() : exprType;
myOriginType = exprType != null ? GenericsUtil.getVariableTypeByExpressionType(exprType) : null;
PsiType type = myTypeEvaluator.evaluateType(expr);
type = type instanceof PsiEllipsisType ? ((PsiEllipsisType)type).toArrayType() : type;
myType = type != null ? GenericsUtil.getVariableTypeByExpressionType(type) : null;
myChanged = (myOriginType == null || myType == null) ? false : !myType.equals(myOriginType);
}
public TypeView(PsiVariable var, PsiSubstitutor varSubstitutor, PsiSubstitutor evalSubstitutor) {
myOriginType = varSubstitutor != null ? varSubstitutor.substitute(var.getType()) : var.getType();
myType = evalSubstitutor != null
? evalSubstitutor.substitute(myTypeEvaluator.getType(var))
: myTypeEvaluator.getType(var);
myChanged = (myOriginType == null || myType == null) ? false : !myType.equals(myOriginType);
}
public PsiType getType() {
return myType;
}
public boolean isChanged() {
return myChanged;
}
public Pair<PsiType, PsiType> getTypePair() {
return new Pair<PsiType, PsiType>(myOriginType, myType);
}
}
private static class TypeInfection {
static final int NONE_INFECTED = 0;
static final int LEFT_INFECTED = 1;
static final int RIGHT_INFECTED = 2;
static final int BOTH_INFECTED = 3;
static int getInfection(final TypeView left, final TypeView right) {
return (left.isChanged() ? 1 : 0) + (right.isChanged() ? 2 : 0);
}
}
}
@@ -0,0 +1,34 @@
package com.intellij.refactoring.typeMigration;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.usageView.UsageViewBundle;
import com.intellij.usageView.UsageViewDescriptor;
import org.jetbrains.annotations.NotNull;
class TypeMigrationViewDescriptor implements UsageViewDescriptor {
private final PsiElement myElement;
public TypeMigrationViewDescriptor(PsiElement elements) {
myElement = elements;
}
@NotNull
public PsiElement[] getElements() {
return new PsiElement[]{myElement};
}
public String getProcessedElementsHeader() {
return "Root for type migration";
}
public String getCodeReferencesText(int usagesCount, int filesCount) {
return RefactoringBundle.message("occurences.to.be.migrated", UsageViewBundle.getReferencesString(usagesCount, filesCount));
}
public String getCommentReferencesText(int usagesCount, int filesCount) {
return null;
}
}
@@ -0,0 +1,74 @@
package com.intellij.refactoring.typeMigration;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
/**
* Created by IntelliJ IDEA.
* User: db
* Date: Nov 4, 2004
* Time: 2:40:49 PM
* To change this template use File | Settings | File Templates.
*/
public class Util {
public static PsiElement getEssentialParent (final PsiElement element){
final PsiElement parent = element.getParent();
if (parent instanceof PsiParenthesizedExpression){
return getEssentialParent(parent);
}
return parent;
}
public static PsiElement normalizeElement(final PsiElement element) {
if (element instanceof PsiMethod) {
final PsiMethod superMethod = ((PsiMethod) element).findDeepestSuperMethod();
return superMethod == null ? element : superMethod;
}
else if (element instanceof PsiParameter && element.getParent() instanceof PsiParameterList) {
final PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
if (method != null) {
final int index = method.getParameterList().getParameterIndex(((PsiParameter)element));
final PsiMethod superMethod = method.findDeepestSuperMethod();
if (superMethod != null) {
return superMethod.getParameterList().getParameters()[index];
}
}
}
return element;
}
public static boolean canBeMigrated(final PsiElement e) {
if (e == null){
return false;
}
final PsiElement element = normalizeElement(e);
if (!element.getManager().isInProject(element)) {
return false;
}
final PsiType type = TypeMigrationLabeler.getElementType(element);
if (type != null) {
final PsiType elemenType = type instanceof PsiArrayType ? type.getDeepComponentType() : type;
if (elemenType instanceof PsiPrimitiveType) {
return !elemenType.equals(PsiType.VOID);
}
final PsiClass aClass = ((PsiClassType)elemenType).resolve();
return aClass != null/* && !aClass.hasTypeParameters()*/;
}
return false;
}
}
@@ -0,0 +1,54 @@
package com.intellij.refactoring.typeMigration.actions;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.RefactoringActionHandler;
import com.intellij.refactoring.actions.BaseRefactoringAction;
import com.intellij.refactoring.typeMigration.ChangeTypeSignatureHandler;
public class ChangeTypeSignatureAction extends BaseRefactoringAction {
public boolean isAvailableInEditorOnly() {
return false;
}
public boolean isEnabledOnElements(PsiElement[] elements) {
Project currProject = PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext());
if (currProject == null) {
return false;
}
if (elements.length > 1) return false;
for (PsiElement element : elements) {
if (!(element instanceof PsiMethod || element instanceof PsiVariable)) {
return false;
}
}
return true;
}
protected boolean isAvailableOnElementInEditor(final PsiElement element, final Editor editor) {
final Document document = editor.getDocument();
final PsiFile file = PsiDocumentManager.getInstance(element.getProject()).getPsiFile(document);
if (file != null) {
final PsiElement psiElement = file.findElementAt(editor.getCaretModel().getOffset());
final PsiReferenceParameterList referenceParameterList = PsiTreeUtil.getParentOfType(psiElement, PsiReferenceParameterList.class);
if (referenceParameterList != null) {
return referenceParameterList.getTypeArguments().length > 0;
}
}
return true;
}
public RefactoringActionHandler getHandler(DataContext dataContext) {
return new ChangeTypeSignatureHandler();
}
}
@@ -0,0 +1,81 @@
/*
* User: anna
* Date: 08-Aug-2008
*/
package com.intellij.refactoring.typeMigration.rules;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase;
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
public class RootTypeConversionRule extends TypeConversionRule {
public TypeConversionDescriptorBase findConversion(final PsiType from, final PsiType to, final PsiMember member, final PsiExpression context,
final TypeMigrationLabeler labeler) {
if (to instanceof PsiClassType && from instanceof PsiClassType) {
final PsiClass targetClass = ((PsiClassType)to).resolve();
if (targetClass != null && member instanceof PsiMethod && member.isPhysical()) {
PsiMethod method = (PsiMethod)member;
PsiMethod replacer = targetClass.findMethodBySignature(method, true);
if (replacer == null) {
for (PsiMethod superMethod : method.findDeepestSuperMethods()) {
replacer = targetClass.findMethodBySignature(superMethod, true);
if (replacer != null) {
method = superMethod;
break;
}
}
}
if (replacer != null && TypeConversionUtil.areTypesConvertible(method.getReturnType(), replacer.getReturnType())) {
final PsiElement parent = context.getParent();
if (context instanceof PsiReferenceExpression && parent instanceof PsiMethodCallExpression) {
final JavaResolveResult resolveResult = ((PsiReferenceExpression)context).advancedResolve(false);
final PsiSubstitutor aSubst;
final PsiReferenceExpression methodExpression = ((PsiMethodCallExpression)parent).getMethodExpression();
final PsiExpression qualifier = methodExpression.getQualifierExpression();
final PsiClass substitutionClass = method.getContainingClass();
if (qualifier != null) {
final PsiType evaluatedQualifierType = labeler.getTypeEvaluator().evaluateType(qualifier);
if (evaluatedQualifierType instanceof PsiClassType) {
aSubst = ((PsiClassType)evaluatedQualifierType).resolveGenerics().getSubstitutor();
}
else {
aSubst = PsiSubstitutor.EMPTY;
}
}
else {
aSubst = TypeConversionUtil.getClassSubstitutor(member.getContainingClass(), substitutionClass, PsiSubstitutor.EMPTY);
}
final PsiParameter[] originalParams = ((PsiMethod)member).getParameterList().getParameters();
final PsiParameter[] migrationParams = replacer.getParameterList().getParameters();
final PsiExpression[] actualParams = ((PsiMethodCallExpression)parent).getArgumentList().getExpressions();
assert originalParams.length == migrationParams.length;
final PsiSubstitutor methodTypeParamsSubstitutor =
labeler.getTypeEvaluator().createMethodSubstitution(originalParams, actualParams, method, (PsiReferenceExpression)context, aSubst != null ? aSubst : PsiSubstitutor.EMPTY, true);
for (int i = 0; i < originalParams.length; i++) {
final PsiType originalType = resolveResult.getSubstitutor().substitute(originalParams[i].getType());
PsiType type = migrationParams[i].getType();
if (InheritanceUtil.isInheritorOrSelf(targetClass, substitutionClass, true)) {
final PsiSubstitutor superClassSubstitutor =
TypeConversionUtil.getClassSubstitutor(substitutionClass, targetClass, PsiSubstitutor.EMPTY);
assert (superClassSubstitutor != null);
type = superClassSubstitutor.substitute(type);
}
if (!originalType.equals(type)) {
labeler.migrateExpressionType(actualParams[i], methodTypeParamsSubstitutor.substitute(type), context, originalType.equals(type), true);
}
}
}
return new TypeConversionDescriptorBase();
}
}
}
return null;
}
}
@@ -0,0 +1,32 @@
/*
* User: anna
* Date: 08-Aug-2008
*/
package com.intellij.refactoring.typeMigration.rules;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiMember;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiType;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase;
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
import org.jetbrains.annotations.Nullable;
public abstract class TypeConversionRule {
public static final ExtensionPointName<TypeConversionRule> EP_NAME = ExtensionPointName.create("com.intellij.conversion.rule");
@Nullable
public abstract TypeConversionDescriptorBase findConversion(final PsiType from,
final PsiType to,
final PsiMember member,
final PsiExpression context,
final TypeMigrationLabeler labeler);
@Nullable
public Pair<PsiType, PsiType> bindTypeParameters(PsiType from, PsiType to, final PsiMethod method, final PsiExpression context,
final TypeMigrationLabeler labeler) {
return null;
}
}
@@ -0,0 +1,78 @@
/**
* created at Sep 12, 2001
* @author Jeka
*/
package com.intellij.refactoring.typeMigration.ui;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.refactoring.RefactoringBundle;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import org.jetbrains.annotations.NonNls;
public class FailedConversionsDialog extends DialogWrapper {
private final String[] myConflictDescriptions;
public static final int VIEW_USAGES_EXIT_CODE = NEXT_USER_EXIT_CODE;
public FailedConversionsDialog(String[] conflictDescriptions, Project project) {
super(project, true);
myConflictDescriptions = conflictDescriptions;
setTitle(RefactoringBundle.message("usages.detected.title"));
setOKButtonText(RefactoringBundle.message("ignore.button"));
getOKAction().putValue(Action.MNEMONIC_KEY, new Integer('I'));
init();
}
protected Action[] createActions() {
return new Action[]{getOKAction(), new ViewUsagesAction(), new CancelAction()};
}
protected JComponent createCenterPanel() {
JPanel panel = new JPanel(new BorderLayout());
@NonNls final String contentType = "text/html";
final JEditorPane messagePane = new JEditorPane(contentType, "");
messagePane.setEditable(false);
JScrollPane scrollPane = new JScrollPane(messagePane);
scrollPane.setPreferredSize(new Dimension(500, 400));
panel.add(new JLabel(RefactoringBundle.message("the.following.problems.were.found")), BorderLayout.NORTH);
panel.add(scrollPane, BorderLayout.CENTER);
@NonNls StringBuffer buf = new StringBuffer();
for (String description : myConflictDescriptions) {
buf.append(description);
buf.append("<br><br>");
}
messagePane.setText(buf.toString());
return panel;
}
protected String getDimensionServiceKey() {
return "#com.intellij.refactoring.typeMigration.ui.FailedConversionsDialog";
}
private class CancelAction extends AbstractAction {
public CancelAction() {
super(RefactoringBundle.message("cancel.button"));
}
public void actionPerformed(ActionEvent e) {
doCancelAction();
}
}
private class ViewUsagesAction extends AbstractAction {
public ViewUsagesAction() {
super(RefactoringBundle.message("view.usages"));
putValue(Action.MNEMONIC_KEY, new Integer('V'));
putValue(DialogWrapper.DEFAULT_ACTION, Boolean.TRUE);
}
public void actionPerformed(ActionEvent e) {
close(VIEW_USAGES_EXIT_CODE);
}
}
}
@@ -0,0 +1,29 @@
/*
* User: anna
* Date: 26-Mar-2008
*/
package com.intellij.refactoring.typeMigration.ui;
import com.intellij.openapi.project.Project;
import com.intellij.packageDependencies.ui.UsagesPanel;
import com.intellij.usageView.UsageInfo;
import com.intellij.usages.UsageInfoToUsageConverter;
public class MigrationConflictsPanel extends UsagesPanel{
public MigrationConflictsPanel(Project project) {
super(project);
}
public String getInitialPositionText() {
return "No migration conflicts found";
}
public String getCodeUsagesString() {
return "Found migration conflicts";
}
@Override
public void showUsages(final UsageInfoToUsageConverter.TargetElementsDescriptor descriptor, final UsageInfo[] usageInfos) {
super.showUsages(descriptor, usageInfos);
}
}
@@ -0,0 +1,113 @@
/*
* User: anna
* Date: 25-Mar-2008
*/
package com.intellij.refactoring.typeMigration.ui;
import com.intellij.ide.projectView.PresentationData;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiType;
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import com.intellij.ui.DuplicateNodeRenderer;
import org.jetbrains.annotations.NotNull;
import java.util.*;
public class MigrationNode extends AbstractTreeNode<TypeMigrationUsageInfo> implements DuplicateNodeRenderer.DuplicatableNode<MigrationNode> {
private final TypeMigrationUsageInfo myInfo;
private MigrationNode myDuplicatedNode;
private List<MigrationNode> myCachedChildren;
private final TypeMigrationLabeler myLabeler;
private final PsiType myMigrationType;
private final TypeMigrationTreeBuilder myBuilder;
private final HashMap<TypeMigrationUsageInfo, Set<MigrationNode>> myProcessed;
private final HashSet<TypeMigrationUsageInfo> myParents;
public MigrationNode(final Project project,
final TypeMigrationUsageInfo info,
final PsiType migrationType,
final TypeMigrationLabeler labeler,
final TypeMigrationTreeBuilder builder,
final HashSet<TypeMigrationUsageInfo> parents,
final HashMap<TypeMigrationUsageInfo, Set<MigrationNode>> processed) {
super(project, info);
myLabeler = labeler;
myMigrationType = migrationType;
myBuilder = builder;
myProcessed = processed;
myParents = parents;
Set<MigrationNode> alreadyAdded = myProcessed.get(info);
if (alreadyAdded == null) {
alreadyAdded = new HashSet<MigrationNode>();
myProcessed.put(info, alreadyAdded);
myInfo = info;
}
else {
final MigrationNode duplicate = alreadyAdded.iterator().next();
myInfo = duplicate.getInfo();
myDuplicatedNode = duplicate;
}
alreadyAdded.add(this);
}
public TypeMigrationUsageInfo getInfo() {
return myInfo;
}
@NotNull
public Collection<? extends AbstractTreeNode> getChildren() {
if (myCachedChildren == null) {
myCachedChildren = new ArrayList<MigrationNode>();
final PsiElement element = myInfo.getElement();
if (element != null) {
myLabeler.setCurrentRoot(myInfo);
myLabeler.migrateRoot(element, myMigrationType, myLabeler.markRootUsages(element, myMigrationType));
final HashSet<Pair<TypeMigrationUsageInfo, PsiType>> roots = myLabeler.getRootsTree().get(myInfo);
if (roots != null) {
for (Pair<TypeMigrationUsageInfo, PsiType> root : roots) {
final TypeMigrationUsageInfo info = root.getFirst();
if (myParents.contains(info)) continue;
final HashSet<TypeMigrationUsageInfo> parents = new HashSet<TypeMigrationUsageInfo>(myParents);
parents.add(info);
final MigrationNode migrationNode =
new MigrationNode(getProject(), info, root.getSecond(), myLabeler, myBuilder, parents, myProcessed);
if (myInfo.isExcluded()) {
info.setExcluded(true);
}
myCachedChildren.add(migrationNode);
}
}
}
}
return myCachedChildren;
}
public boolean areChildrenInitialized() {
return myCachedChildren != null;
}
protected void update(final PresentationData presentation) {
}
public MigrationNode getDuplicate() {
return myDuplicatedNode;
}
public boolean hasDuplicate() {
return myDuplicatedNode != null;
}
}
@@ -0,0 +1,491 @@
/*
* User: anna
* Date: 24-Mar-2008
*/
package com.intellij.refactoring.typeMigration.ui;
import com.intellij.CommonBundle;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.help.HelpManager;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.presentation.java.SymbolPresentationUtil;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.typeMigration.ChangeTypeSignatureHandler;
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
import com.intellij.refactoring.typeMigration.TypeMigrationProcessor;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import com.intellij.ui.*;
import com.intellij.ui.content.Content;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewBundle;
import com.intellij.usageView.UsageViewManager;
import com.intellij.usages.TextChunk;
import com.intellij.usages.UsageInfoToUsageConverter;
import com.intellij.usages.UsagePresentation;
import com.intellij.util.EditSourceOnDoubleClickHandler;
import com.intellij.util.containers.HashSet;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
import java.util.Set;
public class MigrationPanel extends JPanel implements Disposable {
@NonNls private static final String MIGRATION_USAGES = "migration.usages";
private static final DataKey<TypeMigrationUsageInfo[]> MIGRATION_USAGES_KEYS = DataKey.create(MIGRATION_USAGES);
private final PsiElement myInitialRoot;
private final TypeMigrationLabeler myLabeler;
private final MyTree myRootsTree;
private static final Logger LOG = Logger.getInstance("#" + MigrationPanel.class.getName());
private final Project myProject;
private final boolean myPreviewUsages;
private Content myContent;
private final MigrationUsagesPanel myUsagesPanel;
private final MigrationConflictsPanel myConflictsPanel;
public MigrationPanel(final PsiElement root, TypeMigrationLabeler labeler, final Project project, final boolean previewUsages) {
super(new BorderLayout());
myInitialRoot = root;
myLabeler = labeler;
myProject = project;
myPreviewUsages = previewUsages;
myRootsTree = new MyTree(new DefaultTreeModel(new DefaultMutableTreeNode()));
final TypeMigrationTreeBuilder builder = new TypeMigrationTreeBuilder(myRootsTree, project);
final MigrationRootNode currentRoot = new MigrationRootNode(project, myLabeler, builder, root, myPreviewUsages);
builder.setRoot(currentRoot);
initTree(myRootsTree);
myRootsTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(final TreeSelectionEvent e) {
selectionChanged();
}
});
final Splitter treeSplitter = new Splitter();
Disposer.register(this, new Disposable() {
public void dispose() {
treeSplitter.dispose();
}
});
treeSplitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myRootsTree));
myUsagesPanel = new MigrationUsagesPanel(myProject);
treeSplitter.setSecondComponent(myUsagesPanel);
Disposer.register(this, myUsagesPanel);
add(createToolbar(), BorderLayout.SOUTH);
final Splitter conflictsSplitter = new Splitter(true, .8f);
Disposer.register(this, new Disposable() {
public void dispose() {
conflictsSplitter.dispose();
}
});
conflictsSplitter.setFirstComponent(treeSplitter);
myConflictsPanel = new MigrationConflictsPanel(myProject);
conflictsSplitter.setSecondComponent(myConflictsPanel);
add(conflictsSplitter, BorderLayout.CENTER);
Disposer.register(this, myConflictsPanel);
builder.addSubtreeToUpdate((DefaultMutableTreeNode)myRootsTree.getModel().getRoot(), new Runnable() {
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (builder.isDisposed()) return;
myRootsTree.expandPath(new TreePath(myRootsTree.getModel().getRoot()));
final Collection<? extends AbstractTreeNode> children = currentRoot.getChildren();
if (!children.isEmpty()) {
builder.select(children.iterator().next());
}
}
});
}
});
Disposer.register(this, builder);
}
private void selectionChanged() {
myConflictsPanel.setToInitialPosition();
myUsagesPanel.setToInitialPosition();
final DefaultMutableTreeNode[] migrationNodes = myRootsTree.getSelectedNodes(DefaultMutableTreeNode.class, null);
if (migrationNodes == null || migrationNodes.length == 0) return;
final Object userObject = migrationNodes[0].getUserObject();
if (userObject instanceof MigrationNode) {
final MigrationNode migrationNode = (MigrationNode)userObject;
final UsageInfo[] failedUsages = myLabeler.getFailedUsages();
if (failedUsages.length > 0) {
myConflictsPanel.showUsages(new UsageInfoToUsageConverter.TargetElementsDescriptor(new PsiElement[0]), failedUsages);
}
final AbstractTreeNode rootNode = migrationNode.getParent();
if (rootNode instanceof MigrationNode) {
myUsagesPanel.showRootUsages(((MigrationNode)rootNode).getInfo(), migrationNode.getInfo(), myLabeler);
}
}
}
private JComponent createToolbar() {
final JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints gc = new GridBagConstraints(GridBagConstraints.RELATIVE, 0, 1, 1, 0, 1, GridBagConstraints.NORTHWEST,
GridBagConstraints.NONE, new Insets(5, 10, 5, 0), 0, 0);
final JButton performButton = new JButton(RefactoringBundle.message("type.migration.migrate.button.text"));
performButton.addActionListener(new ActionListener() {
private void expandTree(MigrationNode migrationNode) {
if (!migrationNode.getInfo().isExcluded() || migrationNode.areChildrenInitialized()) { //do not walk into excluded collapsed nodes: nothing to migrate can be found
final Collection<? extends AbstractTreeNode> nodes = migrationNode.getChildren();
for (AbstractTreeNode node : nodes) {
expandTree((MigrationNode)node);
}
}
}
public void actionPerformed(final ActionEvent e) {
final Object root = myRootsTree.getModel().getRoot();
if (root instanceof DefaultMutableTreeNode) {
final Object userObject = ((DefaultMutableTreeNode)root).getUserObject();
if (userObject instanceof MigrationRootNode) {
ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
public void run() {
new WriteCommandAction(myProject) {
protected void run(Result result) throws Throwable {
final Collection<? extends AbstractTreeNode> children = ((MigrationRootNode)userObject).getChildren();
for (AbstractTreeNode child : children) {
expandTree((MigrationNode)child);
}
final TypeMigrationUsageInfo[] usages = myLabeler.getMigratedUsages();
final HashSet<VirtualFile> files = new HashSet<VirtualFile>();
for (TypeMigrationUsageInfo usage : usages) {
if (!usage.isExcluded()) {
final PsiElement element = usage.getElement();
if (element != null) {
files.add(element.getContainingFile().getVirtualFile());
}
}
}
if (ReadonlyStatusHandler.getInstance(myProject).
ensureFilesWritable(VfsUtil.toVirtualFileArray(files)).hasReadonlyFiles()) return;
TypeMigrationProcessor.change(myLabeler, usages);
}
}.execute();
}
}, "Type Migration", false, myProject);
}
}
UsageViewManager.getInstance(myProject).closeContent(myContent);
}
});
panel.add(performButton, gc);
final JButton closeButton = new JButton(CommonBundle.getCancelButtonText());
closeButton.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
UsageViewManager.getInstance(myProject).closeContent(myContent);
}
});
panel.add(closeButton, gc);
final JButton rerunButton = new JButton(RefactoringBundle.message("type.migration.rerun.button.text"));
rerunButton.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
UsageViewManager.getInstance(myProject).closeContent(myContent);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ChangeTypeSignatureHandler.invoke(myProject, myInitialRoot, myLabeler.getRules(), null);
}
});
}
});
panel.add(rerunButton, gc);
final JButton helpButton = new JButton(CommonBundle.getHelpButtonText());
helpButton.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
HelpManager.getInstance().invokeHelp("reference.typeMigrationPreview");
}
});
gc.weightx = 1;
panel.add(helpButton, gc);
return panel;
}
private void initTree(final Tree tree) {
final MigrationRootsTreeCellRenderer rootsTreeCellRenderer = new MigrationRootsTreeCellRenderer();
rootsTreeCellRenderer.setOpaque(false);
tree.setCellRenderer(rootsTreeCellRenderer);
tree.setRootVisible(false);
tree.setShowsRootHandles(true);
UIUtil.setLineStyleAngled(tree);
TreeToolTipHandler.install(tree);
TreeUtil.installActions(tree);
TreeUtil.expandAll(tree);
SmartExpander.installOn(tree);
EditSourceOnDoubleClickHandler.install(tree);
new TreeSpeedSearch(tree);
PopupHandler.installUnknownPopupHandler(tree, createTreePopupActions(), ActionManager.getInstance());
}
private ActionGroup createTreePopupActions() {
final DefaultActionGroup group = new DefaultActionGroup();
//group.add(new PerformRefactoringAction());
group.add(new ExcludeAction());
group.add(new IncludeAction());
group.addSeparator();
final ActionManager actionManager = ActionManager.getInstance();
group.add(actionManager.getAction(IdeActions.ACTION_EDIT_SOURCE));
group.add(actionManager.getAction(IdeActions.GROUP_VERSION_CONTROLS));
return group;
}
public void dispose() {
}
public void setContent(final Content content) {
myContent = content;
Disposer.register(content, this);
}
private static class MyTree extends Tree implements DataProvider {
private MyTree(final TreeModel treemodel) {
super(treemodel);
setOpaque(false);
}
@Override
protected void paintComponent(final Graphics g) {
DuplicateNodeRenderer.paintDuplicateNodesBackground(g, this);
super.paintComponent(g);
}
public Object getData(@NonNls final String dataId) {
if (DataConstants.PSI_ELEMENT.equals(dataId)) {
final DefaultMutableTreeNode[] selectedNodes = getSelectedNodes(DefaultMutableTreeNode.class, null);
return selectedNodes != null && selectedNodes.length == 1 && selectedNodes[0].getUserObject() instanceof MigrationNode
? ((MigrationNode)selectedNodes[0].getUserObject()).getInfo().getElement() : null;
}
if (MIGRATION_USAGES.equals(dataId)) {
DefaultMutableTreeNode[] selectedNodes = getSelectedNodes(DefaultMutableTreeNode.class, null);
if (selectedNodes == null) return null;
final Set<TypeMigrationUsageInfo> usageInfos = new HashSet<TypeMigrationUsageInfo>();
for (DefaultMutableTreeNode selectedNode : selectedNodes) {
final Object userObject = selectedNode.getUserObject();
if (userObject instanceof MigrationNode) {
collectInfos(usageInfos, (MigrationNode)userObject);
}
}
return usageInfos.toArray(new TypeMigrationUsageInfo[usageInfos.size()]);
}
return null;
}
private static void collectInfos(final Set<TypeMigrationUsageInfo> usageInfos, final MigrationNode currentNode) {
usageInfos.add(currentNode.getInfo());
if (!currentNode.areChildrenInitialized()) return;
final Collection<? extends AbstractTreeNode> nodes = currentNode.getChildren();
for (AbstractTreeNode node : nodes) {
collectInfos(usageInfos, (MigrationNode)node);
}
}
}
/*private class PerformRefactoringAction extends AnAction {
private PerformRefactoringAction() {
super(RefactoringBundle.message("type.migration.migrate.button.text"));
}
public void actionPerformed(final AnActionEvent e) {
final DefaultMutableTreeNode[] nodes = myRootsTree.getSelectedNodes(DefaultMutableTreeNode.class, null);
for (DefaultMutableTreeNode node : nodes) {
final Object userObject = node.getUserObject();
if (userObject instanceof MigrationNode) {
migrate(((MigrationNode)userObject));
}
}
}
@Override
public void update(final AnActionEvent e) {
super.update(e);
final MigrationNode[] selectedNodes = myRootsTree.getSelectedNodes(MigrationNode.class, null);
e.getPresentation().setEnabled(selectedNodes != null && selectedNodes.length == 1);
}
}*/
private class ExcludeAction extends ExcludeIncludeActionBase {
public ExcludeAction() {
super(RefactoringBundle.message("type.migration.exclude.action.text"));
registerCustomShortcutSet(CommonShortcuts.DELETE, myRootsTree);
}
protected void processUsage(final TypeMigrationUsageInfo usageInfo) {
usageInfo.setExcluded(true);
}
}
private class IncludeAction extends ExcludeIncludeActionBase {
public IncludeAction() {
super(RefactoringBundle.message("type.migration.include.action.text"));
registerCustomShortcutSet(CommonShortcuts.INSERT, myRootsTree);
}
protected void processUsage(final TypeMigrationUsageInfo usageInfo) {
usageInfo.setExcluded(false);
}
@Override
public void update(final AnActionEvent e) {
final Presentation presentation = e.getPresentation();
presentation.setEnabled(false);
final DefaultMutableTreeNode[] selectedNodes = myRootsTree.getSelectedNodes(DefaultMutableTreeNode.class, null);
if (selectedNodes == null) return;
for (DefaultMutableTreeNode node : selectedNodes) {
final Object userObject = node.getUserObject();
if (!(userObject instanceof MigrationNode)) return;
final AbstractTreeNode parent = ((MigrationNode)userObject).getParent(); //disable include if parent was excluded
if (parent instanceof MigrationNode && ((MigrationNode)parent).getInfo().isExcluded()) return;
}
presentation.setEnabled(true);
}
}
private abstract class ExcludeIncludeActionBase extends AnAction {
protected abstract void processUsage(TypeMigrationUsageInfo usageInfo);
protected ExcludeIncludeActionBase(final String text) {
super(text);
}
@Nullable
private TypeMigrationUsageInfo[] getUsages(AnActionEvent context) {
return MIGRATION_USAGES_KEYS.getData(context.getDataContext());
}
public void update(AnActionEvent e) {
final TreePath[] selectionPaths = myRootsTree.getSelectionPaths();
e.getPresentation().setEnabled(selectionPaths != null && selectionPaths.length > 0);
}
public void actionPerformed(AnActionEvent e) {
final TypeMigrationUsageInfo[] usages = getUsages(e);
assert usages != null;
for (TypeMigrationUsageInfo usageInfo : usages) {
processUsage(usageInfo);
}
myRootsTree.repaint();
}
}
private static class MigrationRootsTreeCellRenderer extends ColoredTreeCellRenderer {
public void customizeCellRenderer(final JTree tree,
final Object value,
final boolean selected,
final boolean expanded,
final boolean leaf,
final int row,
final boolean hasFocus) {
final Object userObject = ((DefaultMutableTreeNode)value).getUserObject();
if (!(userObject instanceof MigrationNode)) return;
final TypeMigrationUsageInfo usageInfo = ((MigrationNode)userObject).getInfo();
if (usageInfo != null) {
final PsiElement element = usageInfo.getElement();
if (element != null) {
PsiElement typeElement = null;
if (element instanceof PsiVariable) {
typeElement = ((PsiVariable)element).getTypeElement();
} else if (element instanceof PsiMethod) {
typeElement = ((PsiMethod)element).getReturnTypeElement();
}
if (typeElement == null) typeElement = element;
PsiDocumentManager.getInstance(element.getProject()).commitAllDocuments();
final UsagePresentation presentation = UsageInfoToUsageConverter.convert(new UsageInfoToUsageConverter.TargetElementsDescriptor(typeElement), new UsageInfo(typeElement)).getPresentation();
boolean isPrefix = true; //skip usage position
for (TextChunk chunk : presentation.getText()) {
if (!isPrefix) append(chunk.getText(), patchAttrs(usageInfo, SimpleTextAttributes.fromTextAttributes(chunk.getAttributes())));
isPrefix = false;
}
setIcon(presentation.getIcon());
String location;
if (element instanceof PsiMember) {
location = SymbolPresentationUtil.getSymbolContainerText(element);
}
else {
final PsiMember member = PsiTreeUtil.getParentOfType(element, PsiMember.class);
if (member instanceof PsiField) {
location = PsiFormatUtil.formatVariable((PsiVariable)member, PsiFormatUtil
.SHOW_NAME |
PsiFormatUtil
.SHOW_CONTAINING_CLASS |
PsiFormatUtil
.SHOW_FQ_NAME, PsiSubstitutor.EMPTY);
}
else if (member instanceof PsiMethod) {
location = PsiFormatUtil.formatMethod((PsiMethod)member, PsiSubstitutor.EMPTY, PsiFormatUtil
.SHOW_NAME |
PsiFormatUtil
.SHOW_CONTAINING_CLASS |
PsiFormatUtil
.SHOW_FQ_NAME, PsiFormatUtil.SHOW_TYPE);
}
else if (member instanceof PsiClass) {
location = PsiFormatUtil.formatClass((PsiClass)member, PsiFormatUtil
.SHOW_NAME |
PsiFormatUtil
.SHOW_CONTAINING_CLASS |
PsiFormatUtil
.SHOW_FQ_NAME);
}
else {
location = null;
}
if (location != null) location = PsiBundle.message("aux.context.display", location);
}
if (location != null) {
append(location, SimpleTextAttributes.GRAYED_ATTRIBUTES);
}
} else {
append(UsageViewBundle.message("node.invalid"), SimpleTextAttributes.ERROR_ATTRIBUTES);
}
}
}
private static SimpleTextAttributes patchAttrs(TypeMigrationUsageInfo usageInfo, SimpleTextAttributes original) {
if (usageInfo.isExcluded()) {
original = new SimpleTextAttributes(original.getStyle() | SimpleTextAttributes.STYLE_STRIKEOUT, original.getFgColor(), original.getWaveColor());
}
return original;
}
}
}
@@ -0,0 +1,73 @@
/*
* User: anna
* Date: 16-Apr-2008
*/
package com.intellij.refactoring.typeMigration.ui;
import com.intellij.ide.projectView.PresentationData;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiType;
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import com.intellij.ui.DuplicateNodeRenderer;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import javax.swing.tree.DefaultMutableTreeNode;
import java.util.*;
public class MigrationRootNode extends AbstractTreeNode<TypeMigrationLabeler> implements DuplicateNodeRenderer.DuplicatableNode {
private final TypeMigrationLabeler myLabeler;
private List<MigrationNode> myCachedChildren;
private final TypeMigrationTreeBuilder myBuilder;
private final PsiElement myRoot;
private final boolean myPreviewUsages;
protected MigrationRootNode(Project project,
TypeMigrationLabeler labeler,
final TypeMigrationTreeBuilder builder, final PsiElement root,
final boolean previewUsages) {
super(project, labeler);
myLabeler = labeler;
myBuilder = builder;
myRoot = root;
myPreviewUsages = previewUsages;
}
@NotNull
public Collection<? extends AbstractTreeNode> getChildren() {
if (myCachedChildren == null) {
myCachedChildren = new ArrayList<MigrationNode>();
if (myPreviewUsages) {
for (Pair<TypeMigrationUsageInfo, PsiType> root : myLabeler.getMigrationRoots()) {
addRoot(root.getFirst(), root.getSecond());
}
}
else {
addRoot(new TypeMigrationUsageInfo(myRoot), myLabeler.getRules().getMigrationRootType());
}
}
return myCachedChildren;
}
private void addRoot(TypeMigrationUsageInfo info, PsiType migrationType) {
final HashSet<TypeMigrationUsageInfo> parents = new HashSet<TypeMigrationUsageInfo>();
parents.add(info);
final MigrationNode migrationNode =
new MigrationNode(getProject(), info, migrationType, myLabeler, myBuilder, parents, new HashMap<TypeMigrationUsageInfo, Set<MigrationNode>>());
myCachedChildren.add(migrationNode);
}
protected void update(final PresentationData presentation) {
}
public DefaultMutableTreeNode getDuplicate() {
return null;
}
}
@@ -0,0 +1,56 @@
/*
* User: anna
* Date: 26-Mar-2008
*/
package com.intellij.refactoring.typeMigration.ui;
import com.intellij.openapi.project.Project;
import com.intellij.packageDependencies.ui.UsagesPanel;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import com.intellij.usageView.UsageInfo;
import com.intellij.usages.UsageInfoToUsageConverter;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class MigrationUsagesPanel extends UsagesPanel {
public MigrationUsagesPanel(Project project) {
super(project);
}
public String getInitialPositionText() {
return "Select root to find reasons to migrate";
}
public String getCodeUsagesString() {
return "Found reasons to migrate";
}
public void showRootUsages(UsageInfo root, UsageInfo migration, final TypeMigrationLabeler labeler) {
final PsiElement rootElement = root.getElement();
if (rootElement == null) return;
final UsageInfoToUsageConverter.TargetElementsDescriptor targetElementsDescriptor =
new UsageInfoToUsageConverter.TargetElementsDescriptor(rootElement);
final Set<PsiElement> usages = labeler.getTypeUsages((TypeMigrationUsageInfo)migration, ((TypeMigrationUsageInfo)root));
if (usages != null) {
final List<UsageInfo> infos = new ArrayList<UsageInfo>(usages.size());
for (PsiElement usage : usages) {
if (usage != null && usage.isValid()) {
infos.add(new UsageInfo(usage));
}
}
showUsages(targetElementsDescriptor, infos.toArray(new UsageInfo[infos.size()]));
} else {
showUsages(targetElementsDescriptor, new UsageInfo[] {migration});
}
}
@Override
public Dimension getMaximumSize() {
return new Dimension(-1, 300);
}
}
@@ -0,0 +1,230 @@
/*
* User: anna
* Date: 25-Mar-2008
*/
package com.intellij.refactoring.typeMigration.ui;
import com.intellij.find.FindSettings;
import com.intellij.ide.util.scopeChooser.ScopeChooserCombo;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.help.HelpManager;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.LabeledComponent;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
import com.intellij.refactoring.typeMigration.TypeMigrationProcessor;
import com.intellij.refactoring.typeMigration.TypeMigrationRules;
import com.intellij.refactoring.ui.RefactoringDialog;
import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
import com.intellij.ui.EditorComboBox;
import com.intellij.util.VisibilityUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
public class TypeMigrationDialog extends RefactoringDialog {
public static final String REFACTORING_NAME = "Type Migration";
private final EditorComboBox myToTypeEditor;
private final PsiElement myRoot;
private TypeMigrationRules myRules;
private final PsiTypeCodeFragment myTypeCodeFragment;
private final ScopeChooserCombo myScopeChooserCombo;
public PsiElement getRoot() {
return myRoot;
}
@Nullable
public PsiType getMigrationType() {
try {
return myTypeCodeFragment.getType();
}
catch (PsiTypeCodeFragment.TypeSyntaxException e) {
return null;
}
catch (PsiTypeCodeFragment.NoTypeException e) {
return null;
}
}
public TypeMigrationDialog(@NotNull Project project, PsiElement root, TypeMigrationRules rules) {
super(project, false);
myRoot = root;
myRules = rules;
final PsiType migrationRootType = rules != null ? rules.getMigrationRootType() : null;
myTypeCodeFragment = JavaPsiFacade.getInstance(project).getElementFactory().createTypeCodeFragment(
migrationRootType != null ? migrationRootType.getPresentableText() : getRootType().getPresentableText(), root, false, true,
root instanceof PsiParameter && ((PsiParameter)root).getDeclarationScope() instanceof PsiMethod);
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
final Document document = documentManager.getDocument(myTypeCodeFragment);
assert document != null;
myToTypeEditor = new EditorComboBox(document, project, StdFileTypes.JAVA);
final String[] types = getValidTypes(project, root);
if (types != null) {
myToTypeEditor.setHistory(types);
} else {
myToTypeEditor.setHistory(new String[]{document.getText()});
}
document.addDocumentListener(new DocumentAdapter() {
@Override
public void documentChanged(final DocumentEvent e) {
documentManager.commitDocument(document);
validateButtons();
}
});
myScopeChooserCombo = new ScopeChooserCombo(project, false, true, FindSettings.getInstance().getDefaultScopeName());
Disposer.register(myDisposable, myScopeChooserCombo);
myScopeChooserCombo.getChildComponent().addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
validateButtons();
}
});
init();
setTitle(REFACTORING_NAME);
}
@Nullable
private String[] getValidTypes(final Project project, final PsiElement root) {
final ArrayList<PsiExpression> expressions = new ArrayList<PsiExpression>();
if (root instanceof PsiField || root instanceof PsiMethod) {
if (VisibilityUtil.compare(VisibilityUtil.getVisibilityModifier(((PsiModifierListOwner)root).getModifierList()), PsiModifier.PRIVATE) < 0) return null;
}
for (PsiReference reference : ReferencesSearch.search(root, GlobalSearchScope.fileScope(root.getContainingFile()))) {
final PsiElement element = reference.getElement();
final PsiExpression expr = PsiTreeUtil.getParentOfType(element, PsiExpression.class, false);
if (expr != null) {
expressions.add(expr);
}
}
try {
final PsiType[] psiTypes = new TypeSelectorManagerImpl(project, myTypeCodeFragment.getType(), expressions.toArray(new PsiExpression[expressions.size()])).getTypesForAll();
if (psiTypes.length > 0) {
final String[] history = new String[psiTypes.length];
for (int i = 0; i < psiTypes.length; i++) {
PsiType psiType = psiTypes[i];
history[i] = psiType.getCanonicalText();
}
return history;
}
}
catch (PsiTypeCodeFragment.TypeSyntaxException e) {
return null;
}
catch (PsiTypeCodeFragment.NoTypeException e) {
return null;
}
return null;
}
@Override
protected void canRun() throws ConfigurationException {
if (!checkType(getMigrationType())) throw new ConfigurationException("\'" + StringUtil.first(myTypeCodeFragment.getText(), 10, true) + "\' is invalid type");
if (myScopeChooserCombo.getSelectedScope() == null) throw new ConfigurationException("Scope is not chosen");
}
private static boolean checkType(final PsiType type) {
if (type == null) return false;
if (!type.isValid()) return false;
if (type instanceof PsiClassType){
final PsiClassType psiClassType = (PsiClassType)type;
if (psiClassType.resolve() == null) return false;
final PsiType[] types = psiClassType.getParameters();
for (PsiType paramType : types) {
if (paramType instanceof PsiPrimitiveType || (paramType instanceof PsiWildcardType && ((PsiWildcardType)paramType).getBound() instanceof PsiPrimitiveType)) return false;
if (!checkType(paramType)) return false;
}
}
if (type instanceof PsiArrayType) {
return checkType(type.getDeepComponentType());
}
return true;
}
protected void doAction() {
FindSettings.getInstance().setDefaultScopeName(myScopeChooserCombo.getSelectedScopeName());
final PsiType rootType = getRootType();
final PsiType migrationType = getMigrationType();
if (Comparing.equal(rootType, migrationType)){
close(DialogWrapper.OK_EXIT_CODE);
return;
}
if (myRules == null) {
myRules = new TypeMigrationRules(rootType);
myRules.setMigrationRootType(migrationType);
myRules.setBoundScope(myScopeChooserCombo.getSelectedScope());
}
invokeRefactoring(new TypeMigrationProcessor(myProject, myRoot, myRules));
}
@Nullable
private PsiType getRootType() {
return TypeMigrationLabeler.getElementType(myRoot);
}
protected JComponent createCenterPanel() {
final JPanel panel = new JPanel(new GridBagLayout());
final GridBagConstraints gc = new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1, 0, GridBagConstraints.NORTHWEST,
GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0, 0), 0, 0);
panel.add(new JLabel("Migrate " + getElementPresentation(myRoot) + " \"" + getRootType().getCanonicalText() + "\" to"), gc);
panel.add(myToTypeEditor, gc);
LabeledComponent<ScopeChooserCombo> scopeChooserComponent = new LabeledComponent<ScopeChooserCombo>();
scopeChooserComponent.setComponent(myScopeChooserCombo);
scopeChooserComponent.setText("Choose scope where change signature may occur");
panel.add(scopeChooserComponent, gc);
return panel;
}
@Override
public JComponent getPreferredFocusedComponent() {
return myToTypeEditor;
}
private static String getElementPresentation(PsiElement element) {
if (element instanceof PsiMethod) {
return "return type of method " + ((PsiMethod)element).getName();
}
if (element instanceof PsiField) {
return "type of field " + ((PsiField)element).getName();
}
if (element instanceof PsiLocalVariable) {
return "type of variable " + ((PsiLocalVariable)element).getName();
}
if (element instanceof PsiParameter) {
final PsiParameter parm = (PsiParameter)element;
return "type of parameter " + parm.getName() + " of method " + PsiTreeUtil.getParentOfType(parm, PsiMethod.class).getName();
}
return element.toString();
}
protected void doHelpAction() {
HelpManager.getInstance().invokeHelp("reference.typeMigrationDialog");
}
}
@@ -0,0 +1,32 @@
/*
* User: anna
* Date: 11-Apr-2008
*/
package com.intellij.refactoring.typeMigration.ui;
import com.intellij.ide.util.treeView.AbstractTreeBuilder;
import com.intellij.ide.util.treeView.AlphaComparator;
import com.intellij.ide.util.treeView.NodeDescriptor;
import com.intellij.openapi.project.Project;
import javax.swing.*;
import javax.swing.tree.DefaultTreeModel;
public class TypeMigrationTreeBuilder extends AbstractTreeBuilder{
public TypeMigrationTreeBuilder(JTree tree, Project project) {
super(tree, (DefaultTreeModel)tree.getModel(), new TypeMigrationTreeStructure(project), AlphaComparator.INSTANCE, false);
initRootNode();
}
protected boolean isAlwaysShowPlus(final NodeDescriptor nodeDescriptor) {
return false;
}
protected boolean isAutoExpandNode(final NodeDescriptor nodeDescriptor) {
return false;
}
public void setRoot(MigrationRootNode root) {
((TypeMigrationTreeStructure)getTreeStructure()).setRoot(root);
}
}
@@ -0,0 +1,45 @@
/*
* User: anna
* Date: 11-Apr-2008
*/
package com.intellij.refactoring.typeMigration.ui;
import com.intellij.ide.projectView.TreeStructureProvider;
import com.intellij.ide.util.treeView.AbstractTreeStructureBase;
import com.intellij.openapi.project.Project;
import java.util.Collections;
import java.util.List;
public class TypeMigrationTreeStructure extends AbstractTreeStructureBase {
private MigrationRootNode myRoot;
public TypeMigrationTreeStructure(final Project project) {
super(project);
}
public void setRoot(final MigrationRootNode root) {
myRoot = root;
}
public List<TreeStructureProvider> getProviders() {
return Collections.emptyList();
}
public Object getRootElement() {
return myRoot;
}
public void commit() {
}
public boolean hasSomethingToCommit() {
return false;
}
@Override
public boolean isToBuildChildrenInBackground(final Object element) {
return true;
}
}
@@ -0,0 +1,24 @@
/*
* User: anna
* Date: 27-Mar-2008
*/
package com.intellij.refactoring.typeMigration.usageInfo;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
public class OverridenUsageInfo extends TypeMigrationUsageInfo {
private OverriderUsageInfo[] myOverriders;
public OverridenUsageInfo(@NotNull PsiElement element) {
super(element);
}
public OverriderUsageInfo[] getOverridingElements() {
return myOverriders;
}
public void setOverriders(final OverriderUsageInfo[] overriders) {
myOverriders = overriders;
}
}
@@ -0,0 +1,22 @@
/*
* User: anna
* Date: 27-Mar-2008
*/
package com.intellij.refactoring.typeMigration.usageInfo;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import org.jetbrains.annotations.NotNull;
public class OverriderUsageInfo extends TypeMigrationUsageInfo{
private final PsiMethod myBaseMethod;
public OverriderUsageInfo(@NotNull PsiElement element, PsiMethod baseMethod) {
super(element);
myBaseMethod = baseMethod;
}
public PsiMethod getBaseMethod() {
return myBaseMethod;
}
}
@@ -0,0 +1,41 @@
/*
* User: anna
* Date: 27-Mar-2008
*/
package com.intellij.refactoring.typeMigration.usageInfo;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.PsiElement;
import com.intellij.usageView.UsageInfo;
import org.jetbrains.annotations.NotNull;
public class TypeMigrationUsageInfo extends UsageInfo {
private boolean myExcluded;
public TypeMigrationUsageInfo(@NotNull PsiElement element) {
super(element);
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof TypeMigrationUsageInfo)) return false;
final UsageInfo usageInfo = (UsageInfo)o;
if (endOffset != usageInfo.endOffset) return false;
if (isNonCodeUsage != usageInfo.isNonCodeUsage) return false;
if (startOffset != usageInfo.startOffset) return false;
return Comparing.equal(getElement(), usageInfo.getElement());
}
public void setExcluded(final boolean excluded) {
myExcluded = excluded;
}
public boolean isExcluded() {
return myExcluded;
}
}
@@ -23,6 +23,9 @@ package com.intellij.refactoring.util;
import com.intellij.psi.*;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
public class EnumConstantsUtil {
private EnumConstantsUtil() {
@@ -48,4 +51,31 @@ public class EnumConstantsUtil {
public static PsiEnumConstant createEnumConstant(PsiClass enumClass, PsiLocalVariable local, final String fieldName) throws IncorrectOperationException {
return createEnumConstant(enumClass, fieldName, local.getInitializer());
}
@Nullable
public static PsiStatement isEnumSwitch(final PsiSwitchStatement switchStatement,
final PsiType enumValueType,
final Set<Object> enumValues) {
final PsiExpression expression = switchStatement.getExpression();
if (expression != null) {
final PsiType expressionType = expression.getType();
if (expressionType != null && !TypeConversionUtil.isAssignable(expressionType, enumValueType)) {
return switchStatement;
}
final PsiConstantEvaluationHelper evaluationHelper =
JavaPsiFacade.getInstance(expression.getProject()).getConstantEvaluationHelper();
final PsiCodeBlock body = switchStatement.getBody();
if (body != null) {
for (PsiStatement statement : body.getStatements()) {
if (statement instanceof PsiSwitchLabelStatement) {
final PsiSwitchLabelStatement labelStatement = (PsiSwitchLabelStatement)statement;
final Object caseValue = evaluationHelper.computeConstantExpression(labelStatement.getCaseValue());
if (caseValue != null && !enumValues.contains(caseValue)) return statement;
}
}
return null;
}
}
return switchStatement;
}
}
@@ -0,0 +1,18 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
}
void foobar() {
int max = Math.max(FOO, BAR);
foo(max);
}
}
@@ -0,0 +1,18 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
}
void foobar() {
int max = Math.max(FOO, BAR);
foo(max);
}
}
@@ -0,0 +1,20 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
int k = Math.max(i * i, i + i);
switch (k) {
case FOO:
break;
case BAR:
break;
}
}
}
@@ -0,0 +1,20 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
int k = Math.max(i * i, i + i);
switch (k) {
case FOO:
break;
case BAR:
break;
}
}
}
@@ -0,0 +1,13 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 2;
void foo(String[] args) {
switch (args.length) {
case FOO:
break;
case BAR:
break;
}
}
}
@@ -0,0 +1,13 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 2;
void foo(String[] args) {
switch (args.length) {
case FOO:
break;
case BAR:
break;
}
}
}
@@ -0,0 +1,17 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 2;
void foo(String[] args) {
switch (boo(args)) {
case FOO:
break;
case BAR:
break;
}
}
int boo(String[] args) {
return args.length;
}
}
@@ -0,0 +1,17 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 2;
void foo(String[] args) {
switch (boo(args)) {
case FOO:
break;
case BAR:
break;
}
}
int boo(String[] args) {
return args.length;
}
}
@@ -0,0 +1,12 @@
public enum EEnum {
FOO(0), BAR(1);
private int value;
public int getValue() {
return value;
}
EEnum(int value) {
this.value = value;
}
}
@@ -0,0 +1,16 @@
public class Test {
void foo(EEnum i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
}
void foobar() {
foo(EEnum.FOO);
foo(EEnum.BAR);
}
}
@@ -0,0 +1,18 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
}
void foobar() {
foo(FOO);
foo(BAR);
}
}
@@ -0,0 +1,12 @@
public enum EEnum {
FOO(0), BAR(1);
private int value;
public int getValue() {
return value;
}
EEnum(int value) {
this.value = value;
}
}
@@ -0,0 +1,12 @@
public class Test {
void foo(EEnum i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
int k = Math.max(i.getValue() * i.getValue(), i.getValue() + i.getValue());
}
}
@@ -0,0 +1,14 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
int k = Math.max(i * i, i + i);
}
}
@@ -0,0 +1,12 @@
public enum EEnum {
FOO(0), BAR(1);
private int value;
public int getValue() {
return value;
}
EEnum(int value) {
this.value = value;
}
}
@@ -0,0 +1,19 @@
public class Test {
void foo() {
EEnum i = foobar(false);
switch (i) {
case FOO:
break;
case BAR:
break;
}
}
EEnum foobar(boolean flag) {
if (flag) {
return EEnum.FOO;
}
return EEnum.BAR;
}
}
@@ -0,0 +1,21 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo() {
int i = foobar(false);
switch (i) {
case FOO:
break;
case BAR:
break;
}
}
int foobar(boolean flag) {
if (flag) {
return FOO;
}
return BAR;
}
}
@@ -0,0 +1,19 @@
public class Test {
public static final int OK = 0;
public static final int ERROR = 1;
void foo(int status) {
switch (status) {
case OK:
break;
case ERROR:
break;
case Node.WARNING:
break;
}
}
}
interface Node {
int WARNING = 2;
}
@@ -0,0 +1,19 @@
public class Test {
public static final int OK = 0;
public static final int ERROR = 1;
void foo(int status) {
switch (status) {
case OK:
break;
case ERROR:
break;
case Node.WARNING:
break;
}
}
}
interface Node {
int WARNING = 2;
}
@@ -0,0 +1,12 @@
public enum EEnum {
FOO(0), BAR(2);
private int value;
public int getValue() {
return value;
}
EEnum(int value) {
this.value = value;
}
}
@@ -0,0 +1,5 @@
class Test {
void foo() {
System.out.println(EEnum.FOO.getValue());
}
}
@@ -0,0 +1,15 @@
class Usage {
void foo(EEnum i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
}
void foobar() {
foo(EEnum.FOO);
foo(EEnum.BAR);
}
}
@@ -0,0 +1,7 @@
class Test {
public static final int FOO = 0;
public static final int BAR = 2;
void foo() {
System.out.println(FOO);
}
}
@@ -0,0 +1,15 @@
class Usage {
void foo(int i) {
switch (i) {
case Test.FOO:
break;
case Test.BAR:
break;
}
}
void foobar() {
foo(Test.FOO);
foo(Test.BAR);
}
}
@@ -0,0 +1,20 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
case 8:
break;
}
}
void foobar() {
foo(FOO);
foo(BAR);
}
}
@@ -0,0 +1,20 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
case 8:
break;
}
}
void foobar() {
foo(FOO);
foo(BAR);
}
}
@@ -1,4 +1,4 @@
import java.util.*;
import java.util.Collection;
class Test {
void foo() {
for (Number extension : getExtensions()) {
@@ -0,0 +1,4 @@
public enum A {
ONE(1);
A(int i){}
}
@@ -0,0 +1,17 @@
public class B {
void foo(A i) {
switch (i) {
case ONE :
break;
}
}
}
class Usage {
void foo(A i) {
switch (i) {
case ONE :
break;
}
}
}
@@ -0,0 +1,4 @@
public enum A {
;
A(int i){}
}
@@ -0,0 +1,18 @@
public class B {
public static final int ONE = 1;
void foo(A i) {
switch (i) {
case ONE :
break;
}
}
}
class Usage {
void foo(A i) {
switch (i) {
case B.ONE :
break;
}
}
}
@@ -15,7 +15,6 @@
*/
package com.intellij.projectView;
import com.intellij.JavaTestUtil;
import com.intellij.ide.SelectInTarget;
import com.intellij.ide.projectView.BaseProjectTreeBuilder;
import com.intellij.ide.projectView.ProjectView;
@@ -25,16 +24,17 @@ import com.intellij.ide.projectView.impl.nodes.PackageElementNode;
import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode;
import com.intellij.ide.util.treeView.*;
import com.intellij.openapi.application.ex.PathManagerEx;
import com.intellij.openapi.project.DumbAwareRunnable;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.impl.JavaSdkImpl;
import com.intellij.openapi.ui.Queryable;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.openapi.project.DumbAwareRunnable;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.ui.Queryable;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.ProjectViewTestUtil;
import com.intellij.testFramework.TestSourceBasedTestCase;
import com.intellij.util.Function;
@@ -47,6 +47,7 @@ import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
@@ -290,4 +291,12 @@ public abstract class BaseProjectViewTestCase extends TestSourceBasedTestCase {
protected Sdk getTestProjectJdk() {
return JavaSdkImpl.getMockJdkCE();
}
protected static void sortClasses(final PsiClass[] classes) {
Arrays.sort(classes, new Comparator<PsiClass>() {
public int compare(PsiClass o1, PsiClass o2) {
return o1.getName().compareTo(o2.getName());
}
});
}
}
@@ -41,12 +41,16 @@ import com.intellij.testFramework.IdeaTestUtil;
import javax.swing.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
@SuppressWarnings({"HardCodedStringLiteral"})
public class NavigateFromSourceTest extends BaseProjectViewTestCase {
public void testShowClassMembers() throws Exception {
useStandardProviders();
PsiClass psiClass = JavaDirectoryService.getInstance().getClasses(getPackageDirectory())[0];
final PsiClass[] classes = JavaDirectoryService.getInstance().getClasses(getPackageDirectory());
sortClasses(classes);
PsiClass psiClass = classes[0];
final AbstractProjectViewPSIPane pane = createPane();
final PsiFile containingFile = psiClass.getContainingFile();
@@ -60,7 +60,7 @@ public class ExtractEnumTest extends MultiFileTestCase {
}
public void testForwardReferenceConflict() throws Exception {
doTest("Enum constant field <b><code>BAR</code></b> would forward reference on field field <b><code>FOO</code></b>", false,
doTest("Unable to migrate statement to enum constant.", false,
new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, false),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
@@ -70,6 +70,62 @@ public class ExtractEnumTest extends MultiFileTestCase {
new RefactoringTestUtil.MemberDescriptor("value", PsiField.class, false));
}
public void testChangeMethodParameter() throws Exception {
doTest(new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testCantChangeMethodParameter() throws Exception {
doTest("Unable to migrate statement to enum constant.", false,
new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testDontChangeOtherConstants() throws Exception {
doTest("Unable to migrate statement to enum constant. Node.WARNING can not be replaced with enum", false,
new RefactoringTestUtil.MemberDescriptor("OK", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("ERROR", PsiField.class, true));
}
public void testCantChangeMethodParameter1() throws Exception {
doTest("Unable to migrate statement to enum constant.", false,
new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testChangeReturnType() throws Exception {
doTest(new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testCantChangeReturnType() throws Exception {
doTest("Unable to migrate statement to enum constant. Field &lt;b&gt;&lt;code&gt;length&lt;/code&gt;&lt;/b&gt; is out of project", false,
new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testCantChangeReturnType1() throws Exception {
doTest("Unable to migrate statement to enum constant.", false,
new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testChangeMethodParameterAndReplaceOtherUsages() throws Exception {
doTest(new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testReferencesOnEnumConstantElsewhere() throws Exception {
doTest(new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testUnknownSwitchLabel() throws Exception {
doTest("Unable to migrate statement to enum constant. 8 can not be replaced with enum", false,
new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
private void doTest(final RefactoringTestUtil.MemberDescriptor... memberDescriptors) throws Exception {
doTest(null, false, memberDescriptors);
}
@@ -84,6 +84,10 @@ public class MoveMembersTest extends MultiFileTestCase {
doTest("B", "A", 0);
}
public void testEnumConstantFromCaseStatement() throws Exception {
doTest("B", "A", 0);
}
public void testDependantFields() throws Exception {
doTest("B", "A", 0);
}
+33
View File
@@ -0,0 +1,33 @@
The following files can be redistributable under the license below:
yjpagent.dll
libyjpagent.so
libyjpagent.jnilib
yjp-controller-api-redist.jar
-------------------------------------------------------------------
Copyright (c) 2003-2009, YourKit
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of YourKit nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY YOURKIT "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL YOURKIT BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -124,7 +124,13 @@ public class ProblemsHolder {
if (reference instanceof LocalQuickFixProvider) {
fixes = ((LocalQuickFixProvider)reference).getQuickFixes();
}
registerProblemForReference(reference, highlightType, descriptionTemplate, fixes);
}
public void registerProblemForReference(@NotNull PsiReference reference,
ProblemHighlightType highlightType,
String descriptionTemplate,
LocalQuickFix... fixes) {
registerProblem(myManager.createProblemDescriptor(reference.getElement(), reference.getRangeInElement(), descriptionTemplate, highlightType,
myOnTheFly, fixes));
}
@@ -16,6 +16,7 @@
package com.intellij.openapi.projectRoots;
import com.intellij.openapi.roots.RootProvider;
import com.intellij.openapi.util.UserDataHolder;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nullable;
@@ -23,7 +24,7 @@ import org.jetbrains.annotations.Nullable;
* @author Eugene Zhuravlev
* Date: Sep 23, 2004
*/
public interface Sdk {
public interface Sdk extends UserDataHolder {
SdkType getSdkType();
@@ -77,8 +77,7 @@ public interface PsiFile extends PsiFileSystemItem {
*
* @return the array of PSI roots, or a single-element array containing <code>this</code>
* if the file has only a single language.
* @deprecated Use {@link com.intellij.psi.FileViewProvider#getLanguages()} and then
* {@link com.intellij.psi.FileViewProvider#getPsi(com.intellij.lang.Language)} instead.
* @deprecated Use {@link FileViewProvider#getAllFiles()} instead.
*/
@NotNull PsiFile[] getPsiRoots();
@@ -335,4 +335,8 @@ public abstract class CodeStyleAbstractPanel implements Disposable {
protected Editor getEditor() {
return myEditor;
}
protected CodeStyleSettings getSettings() {
return mySettings;
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.application.options.codeStyle;
import com.intellij.lang.Language;
import com.intellij.openapi.application.ApplicationBundle;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
@@ -68,6 +69,11 @@ public class CodeStyleBlankLinesPanel extends MultilanguageCodeStyleAbstractPane
return LanguageCodeStyleSettingsProvider.SettingsType.BLANK_LINE_SETTINGS;
}
@Override
protected void onLanguageChange(Language language) {
// TODO: IMPLEMENT
}
private JPanel createBlankLinesPanel() {
OptionGroup optionGroup = new OptionGroup(ApplicationBundle.message("title.blank.lines"));
@@ -15,6 +15,7 @@
*/
package com.intellij.application.options.codeStyle;
import com.intellij.lang.Language;
import com.intellij.openapi.application.ApplicationBundle;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
@@ -133,6 +134,11 @@ public class CodeStyleIndentAndBracesPanel extends MultilanguageCodeStyleAbstrac
return LanguageCodeStyleSettingsProvider.SettingsType.INDENT_AND_BRACES_SETTINGS;
}
@Override
protected void onLanguageChange(Language language) {
// TODO: IMPLEMENT
}
private Component createKeepWhenReformattingPanel() {
OptionGroup optionGroup = new OptionGroup(ApplicationBundle.message("title.keep.when.reformatting"));
@@ -15,6 +15,7 @@
*/
package com.intellij.application.options.codeStyle;
import com.intellij.lang.Language;
import com.intellij.openapi.application.ApplicationBundle;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.util.Trinity;
@@ -33,6 +34,7 @@ public class CodeStyleSpacesPanel extends OptionTreeWithPreviewPanel implements
private boolean myShowAllStandardOptions = false;
private Set<String> myAllowedOptions;
private MultiMap<String, Trinity<Class<? extends CustomCodeStyleSettings>, String, String>> myCustomOptions;
private boolean myUpdateOnly = false;
public CodeStyleSpacesPanel(CodeStyleSettings settings) {
super(settings);
@@ -43,6 +45,17 @@ public class CodeStyleSpacesPanel extends OptionTreeWithPreviewPanel implements
return LanguageCodeStyleSettingsProvider.SettingsType.SPACING_SETTINGS;
}
@Override
protected void onLanguageChange(Language language) {
myUpdateOnly = true;
for(LanguageCodeStyleSettingsProvider provider: Extensions.getExtensions(LanguageCodeStyleSettingsProvider.EP_NAME)) {
if (provider.getLanguage().is(language)) {
provider.customizeSpacingOptions(this);
}
}
updateOptionsTree();
}
protected void initTables() {
myAllowedOptions = new HashSet<String>();
myCustomOptions = new MultiMap<String, Trinity<Class<? extends CustomCodeStyleSettings>, String, String>>();
@@ -152,17 +165,25 @@ public class CodeStyleSpacesPanel extends OptionTreeWithPreviewPanel implements
public void showAllStandardOptions() {
myShowAllStandardOptions = true;
updateOptions(true);
}
public void showStandardOptions(String... optionNames) {
Collections.addAll(myAllowedOptions, optionNames);
if (!myUpdateOnly) {
Collections.addAll(myAllowedOptions, optionNames);
}
updateOptions(false, optionNames);
}
public void showCustomOption(Class<? extends CustomCodeStyleSettings> settingsClass,
String fieldName,
String optionName,
String groupName) {
myCustomOptions.putValue(groupName,
Trinity.<Class<? extends CustomCodeStyleSettings>, String, String>create(settingsClass, fieldName, optionName));
if (!myUpdateOnly) {
myCustomOptions.putValue(groupName,
Trinity.<Class<? extends CustomCodeStyleSettings>, String, String>create(settingsClass, fieldName,
optionName));
}
enableOption(fieldName);
}
}
@@ -71,6 +71,7 @@ public abstract class MultilanguageCodeStyleAbstractPanel extends CodeStyleAbstr
public void setPanelLanguage(Language language) {
myLanguage = language;
updatePreviewEditor();
onLanguageChange(language);
}
protected abstract LanguageCodeStyleSettingsProvider.SettingsType getSettingsType();
@@ -221,5 +222,6 @@ public abstract class MultilanguageCodeStyleAbstractPanel extends CodeStyleAbstr
return new JLabel("");
}
protected abstract void onLanguageChange(Language language);
}
@@ -66,6 +66,40 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA
}
protected void updateOptions(boolean showAllStandardOptions, String... allowedOptions) {
for (BooleanOptionKey key : myKeys) {
String fieldName = key.field.getName();
if (key instanceof CustomBooleanOptionKey) {
key.setEnabled(false);
}
else if (showAllStandardOptions) {
key.setEnabled(true);
}
else {
key.setEnabled(false);
for (String optionName: allowedOptions) {
if (fieldName.equals(optionName)) {
key.setEnabled(true);
break;
}
}
}
}
}
protected void enableOption(String optionName) {
for (BooleanOptionKey key : myKeys) {
if (key.field.getName().equals(optionName)) {
key.setEnabled(true);
}
}
}
protected void updateOptionsTree() {
resetImpl(getSettings());
myOptionsTree.repaint();
}
protected JTree createOptionsTree() {
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
String groupName = "";
@@ -159,6 +193,7 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA
try {
BooleanOptionKey key = (BooleanOptionKey)childNode.getKey();
childNode.setSelected(key.getValue(settings));
childNode.setEnabled(key.isEnabled());
}
catch (IllegalArgumentException e) {
LOG.error(e);
@@ -288,7 +323,7 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA
button.setBackground(UIUtil.getTreeTextBackground());
}
button.setEnabled(tree.isEnabled());
button.setEnabled(tree.isEnabled() && treeNode.isEnabled());
return button;
}
@@ -363,6 +398,7 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA
final String groupName;
final String cbName;
final Field field;
private boolean enabled = true;
public BooleanOptionKey(String groupName, String cbName, Field field) {
this.groupName = groupName;
@@ -392,6 +428,14 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA
public boolean getValue(CodeStyleSettings settings) throws IllegalAccessException {
return field.getBoolean(settings);
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabled() {
return this.enabled;
}
}
private static class CustomBooleanOptionKey<T extends CustomCodeStyleSettings> extends BooleanOptionKey {
@@ -424,6 +468,7 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA
private final Object myKey;
private final String myText;
private boolean isSelected;
private boolean isEnabled = true;
public MyToggleTreeNode(Object key, String text) {
myKey = key;
@@ -437,6 +482,10 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA
public void setSelected(boolean val) { isSelected = val; }
public boolean isSelected() { return isSelected; }
public void setEnabled(boolean val) { isEnabled = val; }
public boolean isEnabled() { return isEnabled; }
}
public JComponent getInternalPanel() {
@@ -15,6 +15,7 @@
*/
package com.intellij.application.options.codeStyle;
import com.intellij.lang.Language;
import com.intellij.openapi.application.ApplicationBundle;
import com.intellij.psi.codeStyle.CodeStyleSettings;
@@ -67,6 +68,11 @@ public class WrappingPanel extends OptionTableWithPreviewPanel {
return LanguageCodeStyleSettingsProvider.SettingsType.WRAPPING_SETTINGS;
}
@Override
protected void onLanguageChange(Language language) {
//TODO: IMPLEMENT
}
protected void initTables() {
initRadioGroupField("EXTENDS_LIST_WRAP", EXTENDS_LIST_WRAPPING, FULL_WRAP_OPTIONS, FULL_WRAP_VALUES);
initRadioGroupField("EXTENDS_KEYWORD_WRAP", EXTENDS_KEYWORD_WRAPPING, SINGLE_ITEM_WRAP_OPTIONS,
@@ -96,7 +96,6 @@ public class TemplateSettings implements PersistentStateComponent<Element>, Expo
private static final @NonNls String TEMPLATES_CONFIG_FOLDER = "templates";
private final List<TemplateImpl> myAllTemplates = new ArrayList<TemplateImpl>();
private final MultiMap<String,TemplateImpl> myTemplates = new MultiMap<String,TemplateImpl>();
private final Map<String,Template> myTemplatesById = new LinkedHashMap<String,Template>();
private final Map<String,TemplateImpl> myDefaultTemplates = new LinkedHashMap<String, TemplateImpl>();
@@ -303,7 +302,8 @@ public class TemplateSettings implements PersistentStateComponent<Element>, Expo
}
public TemplateImpl[] getTemplates() {
return myAllTemplates.toArray(new TemplateImpl[myAllTemplates.size()]);
final Collection<? extends TemplateImpl> all = myTemplates.values();
return all.toArray(new TemplateImpl[all.size()]);
}
public char getDefaultShortcutChar() {
@@ -369,7 +369,6 @@ public class TemplateSettings implements PersistentStateComponent<Element>, Expo
final TemplateImpl templateImpl = (TemplateImpl)template;
if (getTemplate(templateImpl.getKey(), templateImpl.getGroupName()) == null) {
myTemplates.putValue(template.getKey(), templateImpl);
myAllTemplates.add(templateImpl);
}
myMaxKeyLength = Math.max(myMaxKeyLength, template.getKey().length());
@@ -390,7 +389,6 @@ public class TemplateSettings implements PersistentStateComponent<Element>, Expo
myTemplates.removeValue(template.getKey(), (TemplateImpl )template);
TemplateImpl templateImpl = (TemplateImpl)template;
myAllTemplates.remove(templateImpl);
String groupName = templateImpl.getGroupName();
TemplateGroup group = mySchemesManager.findSchemeByName(groupName);
@@ -647,7 +645,6 @@ public class TemplateSettings implements PersistentStateComponent<Element>, Expo
public void setTemplates(List<TemplateGroup> newGroups) {
myTemplates.clear();
myAllTemplates.clear();
myDeletedTemplates.clear();
for (TemplateImpl template : myDefaultTemplates.values()) {
myDeletedTemplates.add(TemplateKey.keyOf(template));
@@ -23,6 +23,9 @@ import com.intellij.codeInspection.*;
import com.intellij.codeInspection.ex.*;
import com.intellij.codeInspection.reference.RefManagerImpl;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.impl.ProgressManagerImpl;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
@@ -59,12 +62,16 @@ public class CleanupInspectionIntention implements IntentionAction {
public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
if (!CodeInsightUtilBase.preparePsiElementForWrite(file)) return;
final InspectionManagerEx managerEx = ((InspectionManagerEx)InspectionManagerEx.getInstance(project));
final InspectionManagerEx managerEx = (InspectionManagerEx)InspectionManagerEx.getInstance(project);
final GlobalInspectionContextImpl context = managerEx.createNewGlobalContext(false);
final LocalInspectionToolWrapper tool = new LocalInspectionToolWrapper(myTool);
tool.initialize(context);
((RefManagerImpl)context.getRefManager()).inspectionReadActionStarted();
tool.processFile(file, true, managerEx, true);
((ProgressManagerImpl)ProgressManager.getInstance()).executeProcessUnderProgress(new Runnable() {
public void run() {
tool.processFile(file, true, managerEx, true);
}
}, new EmptyProgressIndicator());
final List<CommonProblemDescriptor> descriptions = new ArrayList<CommonProblemDescriptor>(tool.getProblemDescriptors());
Collections.sort(descriptions, new Comparator<CommonProblemDescriptor>() {
public int compare(final CommonProblemDescriptor o1, final CommonProblemDescriptor o2) {
@@ -76,7 +83,7 @@ public class CleanupInspectionIntention implements IntentionAction {
for (CommonProblemDescriptor descriptor : descriptions) {
final QuickFix[] fixes = descriptor.getFixes();
if (fixes != null && fixes.length > 0) {
for (QuickFix fix : fixes) {
for (QuickFix<CommonProblemDescriptor> fix : fixes) {
if (fix != null && fix.getClass().isAssignableFrom(myQuickfixClass)) {
final PsiElement element = ((ProblemDescriptor)descriptor).getPsiElement();
if (element != null && element.isValid()) {
@@ -275,7 +275,7 @@ public class ChooseRunConfigurationAction extends AnAction {
execute((ItemWrapper)item, executor);
}
});
listPopup.cancel();
listPopup.closeOk(null);
}
}
}
@@ -202,9 +202,7 @@ public class ShowUsagesAction extends AnAction {
final List<Usage> usages = new ArrayList<Usage>();
final Set<UsageNode> visibleNodes = new LinkedHashSet<UsageNode>();
Processor<Usage> collect = new Processor<Usage>() {
final UsageTarget[] myUsageTarget = new UsageTarget[] {
new PsiElement2UsageTargetAdapter(handler.getPsiElement())
};
final UsageTarget[] myUsageTarget = {new PsiElement2UsageTargetAdapter(handler.getPsiElement())};
public boolean process(@NotNull Usage usage) {
synchronized (usages) {
@@ -387,7 +385,9 @@ public class ShowUsagesAction extends AnAction {
if (!(element instanceof UsageNode)) return element.toString();
UsageNode node = (UsageNode)element;
GroupNode group = (GroupNode)node.getParent();
return node.getUsage().getPresentation().getPlainText() + group.toString();
Usage usage = node.getUsage();
if (usage == NullUsage.INSTANCE) return "";
return usage.getPresentation().getPlainText() + group.toString();
}
protected void selectElement(Object element, String selectedText) {
@@ -271,6 +271,7 @@ public class CommanderPanel extends JPanel {
add(myTitlePanel, BorderLayout.NORTH);
final JScrollPane scrollPane = new JScrollPane(myList);
scrollPane.setBorder(null);
scrollPane.getVerticalScrollBar().setFocusable(false); // otherwise the scrollbar steals focus and panel switching with tab is broken
scrollPane.getHorizontalScrollBar().setFocusable(false);
add(scrollPane, BorderLayout.CENTER);
@@ -17,6 +17,7 @@
package com.intellij.ide.util.gotoByName;
import com.intellij.Patches;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.actions.CopyReferenceAction;
import com.intellij.ide.ui.UISettings;
@@ -31,6 +32,8 @@ import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.JBPopupListener;
import com.intellij.openapi.ui.popup.LightweightWindowEvent;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Pair;
@@ -345,7 +348,17 @@ public abstract class ChooseByNameBase{
public void focusLost(final FocusEvent e) {
myHideAlarm.addRequest(new Runnable() {
public void run() {
if (!JBPopupFactory.getInstance().isChildPopupFocused(e.getComponent())) {
JBPopup popup = JBPopupFactory.getInstance().getChildFocusedPopup(e.getComponent());
if (popup != null) {
popup.addListener(new JBPopupListener.Adapter() {
@Override
public void onClosed(LightweightWindowEvent event) {
if (event.isOk()) {
hideHint();
}
}
});
} else {
hideHint();
}
}
@@ -25,10 +25,7 @@ import com.intellij.openapi.projectRoots.ex.ProjectRootContainer;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.RootProvider;
import com.intellij.openapi.roots.impl.RootProviderBaseImpl;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.JDOMExternalizable;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.*;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
@@ -40,7 +37,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.*;
public class ProjectJdkImpl implements JDOMExternalizable, Sdk, SdkModificator {
public class ProjectJdkImpl extends UserDataHolderBase implements JDOMExternalizable, Sdk, SdkModificator {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.projectRoots.impl.ProjectJdkImpl");
private final ProjectRootContainerImpl myRootContainer;
private String myName;
@@ -205,7 +202,7 @@ public class ProjectJdkImpl implements JDOMExternalizable, Sdk, SdkModificator {
}
}
public Object clone() throws CloneNotSupportedException {
public Object clone() {
ProjectJdkImpl newJdk = new ProjectJdkImpl("", mySdkType);
copyTo(newJdk);
return newJdk;
@@ -295,17 +292,11 @@ public class ProjectJdkImpl implements JDOMExternalizable, Sdk, SdkModificator {
// SdkModificator implementation
public SdkModificator getSdkModificator() {
try {
ProjectJdkImpl sdk = (ProjectJdkImpl)clone();
sdk.myOrigin = this;
sdk.myRootContainer.startChange();
sdk.update();
return sdk;
}
catch (CloneNotSupportedException e) {
LOG.error(e); // should not happen
return null;
}
ProjectJdkImpl sdk = (ProjectJdkImpl)clone();
sdk.myOrigin = this;
sdk.myRootContainer.startChange();
sdk.update();
return sdk;
}
public void commitChanges() {
@@ -19,11 +19,10 @@ package com.intellij.psi.impl.source.codeStyle;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.components.ExportableApplicationComponent;
import com.intellij.openapi.components.RoamingType;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.options.Scheme;
import com.intellij.openapi.options.SchemeProcessor;
import com.intellij.openapi.options.SchemesManager;
import com.intellij.openapi.options.SchemesManagerFactory;
import com.intellij.openapi.options.Scheme;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.DefaultJDOMExternalizer;
import com.intellij.openapi.util.InvalidDataException;
@@ -47,8 +46,6 @@ import java.util.Collection;
* Date: Jul 16, 2002
*/
public class CodeStyleSchemesImpl extends CodeStyleSchemes implements ExportableApplicationComponent,JDOMExternalizable {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.codeStyle.CodeStyleSchemesImpl");
@NonNls private static final String DEFAULT_SCHEME_NAME = "Default";
public String CURRENT_SCHEME_NAME = DEFAULT_SCHEME_NAME;
@@ -56,7 +53,7 @@ public class CodeStyleSchemesImpl extends CodeStyleSchemes implements Exportable
@NonNls private static final String CODESTYLES_DIRECTORY = "codestyles";
private final SchemesManager<CodeStyleScheme, CodeStyleSchemeImpl> mySchemesManager;
private static final String FILE_SPEC = "$ROOT_CONFIG$/" + CODESTYLES_DIRECTORY;
@NonNls private static final String FILE_SPEC = "$ROOT_CONFIG$/" + CODESTYLES_DIRECTORY;
public CodeStyleSchemesImpl(SchemesManagerFactory schemesManagerFactory) {
SchemeProcessor<CodeStyleSchemeImpl> processor = new SchemeProcessor<CodeStyleSchemeImpl>() {
@@ -0,0 +1,62 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed 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 com.intellij.psi.impl.source.resolve.reference.impl.providers;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import org.jetbrains.annotations.Nullable;
/**
* @author Konstantin Bulenkov
*/
public class FileReferenceUtil {
private FileReferenceUtil() {
}
/**
* Returns a PsiFile element referenced to
*
* @param element some PsiElement
* @return a PsiFile element referenced to
* @see FileReference
* @see com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet
*/
@Nullable
public static PsiFile findFile(@Nullable PsiElement element) {
return element == null ? null : findFile(element.getReferences());
}
/**
* Iterates all references starting from the end and looking for FileReference,
* when returns <code>resolve()</code> on it.
*
* @param references references, typically from PsiElement.getReferences()
* @return PsiFile if the last FileReference resolves into a real file.
* @see com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference
* @see com.intellij.psi.PsiElement#getReferences()
*/
@Nullable
public static PsiFile findFile(PsiReference...references) {
for (int i = references.length - 1; i >= 0; i--) {
if (references[i] instanceof FileReference) {
final PsiElement file = references[i].resolve();
return file instanceof PsiFile ? (PsiFile)file : null;
}
}
return null;
}
}
@@ -21,6 +21,7 @@ package com.intellij.psi.impl.source.tree;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.PsiLock;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.ILazyParseableElementType;
import com.intellij.util.text.CharArrayUtil;
@@ -28,6 +29,12 @@ import org.jetbrains.annotations.NotNull;
public class LazyParseableElement extends CompositeElement {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.LazyParseableElement");
private static final RecursiveTreeElementWalkingVisitor CREATE_PSI = new RecursiveTreeElementWalkingVisitor(false) {
@Override
protected void visitNode(TreeElement element) {
element.getPsi();
}
};
private static class ChameleonLock {
private ChameleonLock() {}
@@ -160,6 +167,16 @@ public class LazyParseableElement extends CompositeElement {
if (parsedNode != null) {
rawAddChildren((TreeElement)parsedNode);
//ensure PSI is created all at once, to reduce contention of PsiLock in CompositeElement.getPsi()
/*
synchronized (PsiLock.LOCK) {
while (parsedNode != null) {
((TreeElement)parsedNode).acceptTree(CREATE_PSI);
parsedNode = parsedNode.getTreeNext();
}
}
*/
}
}
@@ -105,9 +105,12 @@ public class RenameProcessor extends BaseRefactoringProcessor {
}
public void prepareRenaming() {
final RenamePsiElementProcessor processor = RenamePsiElementProcessor.forElement(myPrimaryElement);
processor.prepareRenaming(myPrimaryElement, myNewName, myAllRenames);
myForceShowPreview = processor.forcesShowPreview();
final List<RenamePsiElementProcessor> processors = RenamePsiElementProcessor.allForElement(myPrimaryElement);
myForceShowPreview = false;
for (RenamePsiElementProcessor processor : processors) {
processor.prepareRenaming(myPrimaryElement, myNewName, myAllRenames);
myForceShowPreview |= processor.forcesShowPreview();
}
}
@Nullable
@@ -286,8 +289,8 @@ public class RenameProcessor extends BaseRefactoringProcessor {
String newName = entry.getValue();
final RefactoringElementListener elementListener = getTransaction().getElementListener(element);
RenameUtil.doRename(element, newName, extractUsagesForElement(element, usages), myProject, elementListener);
Runnable postRenameCallback = RenamePsiElementProcessor.forElement(element).getPostRenameCallback(element, newName, elementListener);
RenameUtil.doRename(element, newName, extractUsagesForElement(element, usages), myProject, elementListener);
if (postRenameCallback != null) {
postRenameCallbacks.add(postRenameCallback);
}

Some files were not shown because too many files have changed in this diff Show More