mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'master' of git.labs.intellij.net:idea/community
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
+21
-16
@@ -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.");
|
||||
|
||||
+46
-9
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+70
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
+185
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -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 {
|
||||
}
|
||||
}
|
||||
+153
@@ -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";
|
||||
}
|
||||
}
|
||||
+152
@@ -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;
|
||||
}
|
||||
}
|
||||
+505
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -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;
|
||||
}
|
||||
}
|
||||
+54
@@ -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();
|
||||
}
|
||||
}
|
||||
+81
@@ -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;
|
||||
}
|
||||
}
|
||||
+32
@@ -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;
|
||||
}
|
||||
}
|
||||
+78
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -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);
|
||||
}
|
||||
}
|
||||
+230
@@ -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");
|
||||
}
|
||||
}
|
||||
+32
@@ -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);
|
||||
}
|
||||
}
|
||||
+45
@@ -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;
|
||||
}
|
||||
}
|
||||
+24
@@ -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;
|
||||
}
|
||||
}
|
||||
+22
@@ -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;
|
||||
}
|
||||
}
|
||||
+41
@@ -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;
|
||||
}
|
||||
}
|
||||
+18
@@ -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);
|
||||
}
|
||||
}
|
||||
+18
@@ -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);
|
||||
}
|
||||
}
|
||||
+20
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -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;
|
||||
}
|
||||
}
|
||||
+17
@@ -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;
|
||||
}
|
||||
}
|
||||
+12
@@ -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;
|
||||
}
|
||||
}
|
||||
+16
@@ -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);
|
||||
}
|
||||
}
|
||||
+18
@@ -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);
|
||||
}
|
||||
}
|
||||
+12
@@ -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;
|
||||
}
|
||||
}
|
||||
+12
@@ -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());
|
||||
}
|
||||
}
|
||||
+14
@@ -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;
|
||||
}
|
||||
}
|
||||
+19
@@ -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;
|
||||
}
|
||||
+19
@@ -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;
|
||||
}
|
||||
+12
@@ -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;
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class Test {
|
||||
void foo() {
|
||||
System.out.println(EEnum.FOO.getValue());
|
||||
}
|
||||
}
|
||||
+15
@@ -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);
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
public static final int FOO = 0;
|
||||
public static final int BAR = 2;
|
||||
void foo() {
|
||||
System.out.println(FOO);
|
||||
}
|
||||
}
|
||||
+15
@@ -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()) {
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
public enum A {
|
||||
ONE(1);
|
||||
A(int i){}
|
||||
}
|
||||
+17
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
public enum A {
|
||||
;
|
||||
A(int i){}
|
||||
}
|
||||
+18
@@ -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 <b><code>length</code></b> 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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -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"));
|
||||
|
||||
|
||||
+6
@@ -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"));
|
||||
|
||||
|
||||
+24
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
+50
-1
@@ -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));
|
||||
|
||||
+10
-3
@@ -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()) {
|
||||
|
||||
+1
-1
@@ -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() {
|
||||
|
||||
+2
-5
@@ -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>() {
|
||||
|
||||
+62
@@ -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
Reference in New Issue
Block a user