/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.refactor;
import com.intellij.codeInsight.ExpectedTypeInfo;
import com.intellij.codeInsight.ExpectedTypesProvider;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.lang.ASTNode;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.GenericsUtil;
import com.intellij.psi.ImplicitVariable;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.JavaRecursiveElementVisitor;
import com.intellij.psi.JavaRecursiveElementWalkingVisitor;
import com.intellij.psi.JavaResolveResult;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiAnonymousClass;
import com.intellij.psi.PsiArrayInitializerExpression;
import com.intellij.psi.PsiArrayType;
import com.intellij.psi.PsiAssignmentExpression;
import com.intellij.psi.PsiCall;
import com.intellij.psi.PsiCallExpression;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassInitializer;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiCodeBlock;
import com.intellij.psi.PsiCodeFragment;
import com.intellij.psi.PsiDeclarationStatement;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiDoWhileStatement;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.PsiEnumConstant;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiExpressionList;
import com.intellij.psi.PsiExpressionListStatement;
import com.intellij.psi.PsiExpressionStatement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiForStatement;
import com.intellij.psi.PsiForeachStatement;
import com.intellij.psi.PsiIdentifier;
import com.intellij.psi.PsiIfStatement;
import com.intellij.psi.PsiImportList;
import com.intellij.psi.PsiImportStatement;
import com.intellij.psi.PsiImportStatementBase;
import com.intellij.psi.PsiImportStaticStatement;
import com.intellij.psi.PsiJavaCodeReferenceElement;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiLocalVariable;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiMember;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiMethodCallExpression;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiNewExpression;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiParameterList;
import com.intellij.psi.PsiParenthesizedExpression;
import com.intellij.psi.PsiPostfixExpression;
import com.intellij.psi.PsiPrefixExpression;
import com.intellij.psi.PsiPrimitiveType;
import com.intellij.psi.PsiQualifiedExpression;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceExpression;
import com.intellij.psi.PsiReferenceList;
import com.intellij.psi.PsiResolveHelper;
import com.intellij.psi.PsiReturnStatement;
import com.intellij.psi.PsiStatement;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.psi.PsiSuperExpression;
import com.intellij.psi.PsiSwitchLabelStatement;
import com.intellij.psi.PsiSwitchStatement;
import com.intellij.psi.PsiThisExpression;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiTypeElement;
import com.intellij.psi.PsiTypeParameter;
import com.intellij.psi.PsiTypeParameterList;
import com.intellij.psi.PsiTypeVisitor;
import com.intellij.psi.PsiVariable;
import com.intellij.psi.PsiWhileStatement;
import com.intellij.psi.PsiWildcardType;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
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.PackageWrapper;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
import gnu.trove.THashMap;
import gw.lang.parser.IStatement;
import gw.plugin.ij.lang.GosuElementType;
import gw.plugin.ij.lang.parser.GosuRawPsiElement;
import gw.plugin.ij.lang.psi.api.expressions.IGosuExpression;
import gw.plugin.ij.lang.psi.api.expressions.IGosuReferenceExpression;
import gw.plugin.ij.lang.psi.impl.GosuEnhancementFileImpl;
import gw.plugin.ij.lang.psi.impl.GosuFragmentFileImpl;
import gw.plugin.ij.lang.psi.impl.GosuProgramFileImpl;
import gw.plugin.ij.lang.psi.impl.GosuTemplateFileImpl;
import gw.plugin.ij.lang.psi.impl.expressions.GosuTypeLiteralImpl;
import gw.plugin.ij.lang.psi.impl.statements.GosuStatementListImpl;
import gw.plugin.ij.lang.psi.util.GosuPsiParseUtil;
import gw.plugin.ij.refactor.intoduceField.GosuElementToWorkOn;
import gw.plugin.ij.refactor.introduceVariable.GosuIntroduceVariableBase;
import gw.plugin.ij.util.GosuBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Copy of Intellij RefactoringUtil
* <p/>
* DO NOT CLEAN !!!
*/
public class GosuRefactoringUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.util.RefactoringUtil");
public static final int EXPR_COPY_SAFE = 0;
public static final int EXPR_COPY_UNSAFE = 1;
public static final int EXPR_COPY_PROHIBITED = 2;
private GosuRefactoringUtil() {
}
public static boolean isValidToIntroduceField(PsiElement element, @Nullable Editor editor, @NotNull String title, @Nullable String helpID) {
PsiFile containingFile = element.getContainingFile();
final String error = containingFile == null ? null :
containingFile instanceof GosuProgramFileImpl ? GosuBundle.message("error.introduce.field.in.program") :
containingFile instanceof GosuTemplateFileImpl ? GosuBundle.message("error.introduce.field.in.template") :
containingFile instanceof GosuEnhancementFileImpl ? GosuBundle.message("error.introduce.field.in.enhancement") :
null;
if (error != null) {
String message = RefactoringBundle.getCannotRefactorMessage(error);
CommonRefactoringUtil.showErrorHint(element.getProject(), editor, message, title, helpID);
return false;
}
return true;
}
public static boolean isSourceRoot(final PsiDirectory directory) {
if (directory.getManager() == null) {
return false;
}
final Project project = directory.getProject();
final VirtualFile virtualFile = directory.getVirtualFile();
final VirtualFile sourceRootForFile = ProjectRootManager.getInstance(project).getFileIndex().getSourceRootForFile(virtualFile);
return Comparing.equal(virtualFile, sourceRootForFile);
}
public static boolean isInStaticContext(PsiElement element, @Nullable final PsiClass aClass) {
return PsiUtil.getEnclosingStaticElement(element, aClass) != null;
}
public static boolean isResolvableType(PsiType type) {
return type.accept(new PsiTypeVisitor<Boolean>() {
public Boolean visitPrimitiveType(PsiPrimitiveType primitiveType) {
return Boolean.TRUE;
}
public Boolean visitArrayType(PsiArrayType arrayType) {
return arrayType.getComponentType().accept(this);
}
public Boolean visitClassType(PsiClassType classType) {
if (classType.resolve() == null) {
return Boolean.FALSE;
}
PsiType[] parameters = classType.getParameters();
for (PsiType parameter : parameters) {
if (parameter != null && !parameter.accept(this).booleanValue()) {
return Boolean.FALSE;
}
}
return Boolean.TRUE;
}
public Boolean visitWildcardType(PsiWildcardType wildcardType) {
if (wildcardType.getBound() != null) {
return wildcardType.getBound().accept(this);
}
return Boolean.TRUE;
}
}).booleanValue();
}
public static PsiElement replaceOccurenceWithFieldRef(PsiExpression occurrence, PsiField newField, PsiClass destinationClass)
throws IncorrectOperationException {
final PsiManager manager = destinationClass.getManager();
final PsiClass newFieldClass = getThisClass(newField);
final PsiClass occurenceClass = getThisClass(occurrence);
final String fieldName = occurenceClass != null && newFieldClass != null && occurenceClass.equals(newFieldClass) ?
newField.getName() : newFieldClass.getName() + "." + newField.getName();
// final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
// final PsiElement element = occurrence.getUserData(GosuElementToWorkOn.PARENT);
// final PsiVariable psiVariable = facade.getResolveHelper().resolveAccessibleReferencedVariable(fieldName, element != null ? element : occurrence);
// if (psiVariable == null || (!psiVariable.getName().equals(fieldName))) {
return GosuIntroduceVariableBase.replace(occurrence, (PsiExpression) GosuPsiParseUtil.parseExpression(fieldName, manager), manager.getProject());
// } else {
// final PsiExpression ref = (PsiExpression) GosuPsiParseUtil.parseExpression("this." + fieldName, manager);
// if (!occurrence.isValid()) {
// return null;
// }
// if (newField.hasModifierProperty(PsiModifier.STATIC)) {
//next line inserts static reference to class
// ref.setQualifierExpression(factory.createReferenceExpression(destinationClass));
// }
// return GosuIntroduceVariableBase.replace(occurrence, ref, manager.getProject());
// }
}
/**
* Cannot use method from code style manager: a collision with fieldToReplace is not a collision
*/
public static String suggestUniqueVariableName(String baseName, PsiElement place, PsiField fieldToReplace) {
int index = 0;
while (true) {
final String name = index > 0 ? baseName + index : baseName;
index++;
final PsiManager manager = place.getManager();
PsiResolveHelper helper = JavaPsiFacade.getInstance(manager.getProject()).getResolveHelper();
PsiVariable refVar = helper.resolveAccessibleReferencedVariable(name, place);
if (refVar != null && !manager.areElementsEquivalent(refVar, fieldToReplace)) {
continue;
}
class CancelException extends RuntimeException {
}
try {
place.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitClass(PsiClass aClass) {
}
@Override
public void visitVariable(PsiVariable variable) {
if (name.equals(variable.getName())) {
throw new CancelException();
}
}
});
} catch (CancelException e) {
continue;
}
return name;
}
}
//order of usages accross different files is irrelevant
public static void sortDepthFirstRightLeftOrder(final UsageInfo[] usages) {
Arrays.sort(usages, new Comparator<UsageInfo>() {
public int compare(final UsageInfo usage1, final UsageInfo usage2) {
final PsiElement element1 = usage1.getElement();
final PsiElement element2 = usage2.getElement();
if (element1 == null || element2 == null) {
return 0;
}
return element2.getTextRange().getStartOffset() - element1.getTextRange().getStartOffset();
}
});
}
@Nullable
public static String suggestNewOverriderName(String oldOverriderName, String oldBaseName, String newBaseName) {
if (oldOverriderName.equals(oldBaseName)) {
return newBaseName;
}
int i;
if (oldOverriderName.startsWith(oldBaseName)) {
i = 0;
} else {
i = StringUtil.indexOfIgnoreCase(oldOverriderName, oldBaseName, 0);
}
if (i >= 0) {
String newOverriderName = oldOverriderName.substring(0, i);
if (Character.isUpperCase(oldOverriderName.charAt(i))) {
newOverriderName += StringUtil.capitalize(newBaseName);
} else {
newOverriderName += newBaseName;
}
final int j = i + oldBaseName.length();
if (j < oldOverriderName.length()) {
newOverriderName += oldOverriderName.substring(j);
}
return newOverriderName;
}
return null;
}
public static boolean hasOnDemandStaticImport(final PsiElement element, final PsiClass aClass) {
if (element.getContainingFile() instanceof PsiJavaFile) {
final PsiImportList importList = ((PsiJavaFile) element.getContainingFile()).getImportList();
if (importList != null) {
final PsiImportStaticStatement[] importStaticStatements = importList.getImportStaticStatements();
for (PsiImportStaticStatement stmt : importStaticStatements) {
if (stmt.isOnDemand() && stmt.resolveTargetClass() == aClass) {
return true;
}
}
}
}
return false;
}
public static boolean hasStaticImportOn(final PsiElement expr, final PsiMember member) {
if (expr.getContainingFile() instanceof PsiJavaFile) {
final PsiImportList importList = ((PsiJavaFile) expr.getContainingFile()).getImportList();
if (importList != null) {
final PsiImportStaticStatement[] importStaticStatements = importList.getImportStaticStatements();
for (PsiImportStaticStatement stmt : importStaticStatements) {
if (!stmt.isOnDemand() && stmt.resolveTargetClass() == member.getContainingClass() && Comparing.strEqual(stmt.getReferenceName(), member.getName())) {
return true;
}
}
}
}
return false;
}
public static PsiElement replaceElementsWithMap(PsiElement replaceIn, final Map<PsiElement, PsiElement> elementsToReplace) throws IncorrectOperationException {
for (Map.Entry<PsiElement, PsiElement> e : elementsToReplace.entrySet()) {
if (e.getKey() == replaceIn) {
return e.getKey().replace(e.getValue());
}
e.getKey().replace(e.getValue());
}
return replaceIn;
}
public static PsiElement getVariableScope(PsiLocalVariable localVar) {
if (!(localVar instanceof ImplicitVariable)) {
return localVar.getParent().getParent();
} else {
return ((ImplicitVariable) localVar).getDeclarationScope();
}
}
public static PsiReturnStatement[] findReturnStatements(PsiMethod method) {
ArrayList<PsiReturnStatement> vector = new ArrayList<>();
PsiCodeBlock body = method.getBody();
if (body != null) {
addReturnStatements(vector, body);
}
return vector.toArray(new PsiReturnStatement[vector.size()]);
}
private static void addReturnStatements(ArrayList<PsiReturnStatement> vector, PsiElement element) {
if (element instanceof PsiReturnStatement) {
vector.add((PsiReturnStatement) element);
} else if (!(element instanceof PsiClass)) {
PsiElement[] children = element.getChildren();
for (PsiElement child : children) {
addReturnStatements(vector, child);
}
}
}
public static PsiElement getParentStatement(PsiElement place, boolean skipScopingStatements) {
PsiElement parent = place;
while (true) {
if (GosuRefactoringUtil.isStatement(parent)) {
break;
}
parent = parent.getParent();
if (parent == null) {
return null;
}
}
PsiElement parentStatement = parent;
parent = GosuRefactoringUtil.isStatement(parentStatement) ? parentStatement : parentStatement.getParent();
while (GosuRefactoringUtil.isStatement(parent) && !(parent instanceof GosuStatementListImpl)) {
if (!skipScopingStatements && ((parent instanceof PsiForStatement && parentStatement == ((PsiForStatement) parent).getBody()) || (
parent instanceof PsiForeachStatement && parentStatement == ((PsiForeachStatement) parent).getBody()) || (
parent instanceof PsiWhileStatement && parentStatement == ((PsiWhileStatement) parent).getBody()) || (
parent instanceof PsiIfStatement &&
(parentStatement == ((PsiIfStatement) parent).getThenBranch() || parentStatement == ((PsiIfStatement) parent).getElseBranch())))) {
return parentStatement;
}
parentStatement = parent;
parent = parent.getParent();
}
return parentStatement;
}
public static PsiElement getParentExpressionAnchorElement(PsiElement place) {
PsiElement parent = place.getUserData(GosuElementToWorkOn.PARENT);
if (place.getUserData(GosuElementToWorkOn.OUT_OF_CODE_BLOCK) != null) {
return parent;
}
if (parent == null) {
parent = place;
}
while (true) {
if (isExpressionAnchorElement(parent)) {
return parent;
}
parent = parent.getParent();
if (parent == null) {
return null;
}
}
}
public static boolean isExpressionAnchorElement(PsiElement element) {
return GosuRefactoringUtil.isStatement(element) || element instanceof PsiClassInitializer || element instanceof PsiField ||
element instanceof PsiMethod;
}
/**
* @param expression
* @return loop body if expression is part of some loop's condition or for loop's increment part
* null otherwise
*/
public static PsiElement getLoopForLoopCondition(PsiExpression expression) {
PsiExpression outermost = expression;
while (outermost.getParent() instanceof PsiExpression) {
outermost = (PsiExpression) outermost.getParent();
}
if (outermost.getParent() instanceof PsiForStatement) {
final PsiForStatement forStatement = (PsiForStatement) outermost.getParent();
if (forStatement.getCondition() == outermost) {
return forStatement;
} else {
return null;
}
}
if (outermost.getParent() instanceof PsiExpressionStatement && outermost.getParent().getParent() instanceof PsiForStatement) {
final PsiForStatement forStatement = (PsiForStatement) outermost.getParent().getParent();
if (forStatement.getUpdate() == outermost.getParent()) {
return forStatement;
} else {
return null;
}
}
if (outermost.getParent() instanceof PsiWhileStatement) {
return outermost.getParent();
}
if (outermost.getParent() instanceof PsiDoWhileStatement) {
return outermost.getParent();
}
return null;
}
public static PsiClass getThisClass(PsiElement place) {
PsiElement parent = place.getContext();
if (parent == null) {
return null;
}
PsiElement prev = null;
while (true) {
if (parent instanceof PsiClass) {
if (!(parent instanceof PsiAnonymousClass && ((PsiAnonymousClass) parent).getArgumentList() == prev)) {
return (PsiClass) parent;
}
}
prev = parent;
parent = parent.getContext();
if (parent == null) {
return null;
}
}
}
public static PsiClass getThisResolveClass(final PsiReferenceExpression place) {
final JavaResolveResult resolveResult = place.advancedResolve(false);
final PsiElement scope = resolveResult.getCurrentFileResolveScope();
if (scope instanceof PsiClass) {
return (PsiClass) scope;
}
return null;
}
public static PsiCall getEnclosingConstructorCall(PsiJavaCodeReferenceElement ref) {
PsiElement parent = ref.getParent();
if (ref instanceof PsiReferenceExpression && parent instanceof PsiMethodCallExpression) {
return (PsiCall) parent;
}
if (parent instanceof PsiAnonymousClass) {
parent = parent.getParent();
}
return parent instanceof PsiNewExpression ? (PsiNewExpression) parent : null;
}
public static PsiMethod getEnclosingMethod(PsiElement element) {
final PsiElement container = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiClass.class);
return container instanceof PsiMethod ? (PsiMethod) container : null;
}
public static void renameVariableReferences(PsiVariable variable, String newName, SearchScope scope) throws IncorrectOperationException {
renameVariableReferences(variable, newName, scope, false);
}
public static void renameVariableReferences(PsiVariable variable,
String newName,
SearchScope scope,
final boolean ignoreAccessScope) throws IncorrectOperationException {
for (PsiReference reference : ReferencesSearch.search(variable, scope, ignoreAccessScope)) {
reference.handleElementRename(newName);
}
}
public static boolean canBeDeclaredFinal(PsiVariable variable) {
LOG.assertTrue(variable instanceof PsiLocalVariable || variable instanceof PsiParameter);
final boolean isReassigned = HighlightControlFlowUtil
.isReassigned(variable, new THashMap<PsiElement, Collection<ControlFlowUtil.VariableInfo>>());
return !isReassigned;
}
public static PsiThisExpression createThisExpression(PsiManager manager, PsiClass qualifierClass) throws IncorrectOperationException {
return GosuRefactoringUtil.createQualifiedExpression(manager, qualifierClass, "this");
}
public static PsiSuperExpression createSuperExpression(PsiManager manager, PsiClass qualifierClass) throws IncorrectOperationException {
return GosuRefactoringUtil.createQualifiedExpression(manager, qualifierClass, "super");
}
private static <T extends PsiQualifiedExpression> T createQualifiedExpression(PsiManager manager, PsiClass qualifierClass, String qName) throws IncorrectOperationException {
PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
if (qualifierClass != null) {
T qualifiedThis = (T) factory.createExpressionFromText("q." + qName, null);
qualifiedThis = (T) CodeStyleManager.getInstance(manager.getProject()).reformat(qualifiedThis);
PsiJavaCodeReferenceElement thisQualifier = qualifiedThis.getQualifier();
LOG.assertTrue(thisQualifier != null);
thisQualifier.bindToElement(qualifierClass);
return qualifiedThis;
} else {
return (T) factory.createExpressionFromText(qName, null);
}
}
/**
* removes a reference to the specified class from the reference list given
*
* @return if removed - a reference to the class or null if there were no references to this class in the reference list
*/
public static PsiJavaCodeReferenceElement removeFromReferenceList(PsiReferenceList refList, PsiClass aClass)
throws IncorrectOperationException {
PsiJavaCodeReferenceElement[] refs = refList.getReferenceElements();
for (PsiJavaCodeReferenceElement ref : refs) {
if (ref.isReferenceTo(aClass)) {
PsiJavaCodeReferenceElement refCopy = (PsiJavaCodeReferenceElement) ref.copy();
ref.delete();
return refCopy;
}
}
return null;
}
public static PsiJavaCodeReferenceElement findReferenceToClass(PsiReferenceList refList, PsiClass aClass) {
PsiJavaCodeReferenceElement[] refs = refList.getReferenceElements();
for (PsiJavaCodeReferenceElement ref : refs) {
if (ref.isReferenceTo(aClass)) {
return ref;
}
}
return null;
}
public static PsiType getTypeByExpressionWithExpectedType(PsiExpression expr) {
PsiType type = getTypeByExpression(expr);
if (type != null) {
return type;
}
ExpectedTypeInfo[] expectedTypes = ExpectedTypesProvider.getExpectedTypes(expr, false);
if (expectedTypes.length == 1) {
type = expectedTypes[0].getType();
if (!type.equalsToText("java.lang.Object")) {
return type;
}
}
return null;
}
public static PsiType getTypeByExpression(PsiExpression expr) {
PsiElementFactory factory = JavaPsiFacade.getInstance(expr.getProject()).getElementFactory();
return getTypeByExpression(expr, factory);
}
private static PsiType getTypeByExpression(PsiExpression expr, final PsiElementFactory factory) {
PsiType type = expr.getType();
if (type == null) {
if (expr instanceof PsiArrayInitializerExpression) {
PsiExpression[] initializers = ((PsiArrayInitializerExpression) expr).getInitializers();
if (initializers.length > 0) {
PsiType initType = getTypeByExpression(initializers[0]);
if (initType == null) {
return null;
}
return initType.createArrayType();
}
}
return null;
}
PsiClass refClass = PsiUtil.resolveClassInType(type);
if (refClass instanceof PsiAnonymousClass) {
type = ((PsiAnonymousClass) refClass).getBaseClassType();
}
if (PsiType.NULL.equals(type)) {
ExpectedTypeInfo[] infos = ExpectedTypesProvider.getExpectedTypes(expr, false);
if (infos.length == 1) {
type = infos[0].getType();
} else {
type = factory.createTypeByFQClassName("java.lang.Object", expr.getResolveScope());
}
}
return GenericsUtil.getVariableTypeByExpressionType(type);
}
public static boolean isAssignmentLHS(PsiElement element) {
PsiElement parent = element.getParent();
return parent instanceof PsiAssignmentExpression && element.equals(((PsiAssignmentExpression) parent).getLExpression()) ||
isPlusPlusOrMinusMinus(parent);
}
public static boolean isPlusPlusOrMinusMinus(PsiElement element) {
if (element instanceof PsiPrefixExpression) {
return ((PsiPrefixExpression) element).getOperationTokenType() == JavaTokenType.PLUSPLUS ||
((PsiPrefixExpression) element).getOperationTokenType() == JavaTokenType.MINUSMINUS;
} else if (element instanceof PsiPostfixExpression) {
IElementType operandTokenType = ((PsiPostfixExpression) element).getOperationTokenType();
return operandTokenType == JavaTokenType.PLUSPLUS || operandTokenType == JavaTokenType.MINUSMINUS;
} else {
return false;
}
}
private static void removeFinalParameters(PsiMethod method) throws IncorrectOperationException {
// Remove final parameters
PsiParameterList paramList = method.getParameterList();
PsiParameter[] params = paramList.getParameters();
for (PsiParameter param : params) {
if (param.hasModifierProperty(PsiModifier.FINAL)) {
PsiUtil.setModifierProperty(param, PsiModifier.FINAL, false);
}
}
}
public static PsiElement getAnchorElementForMultipleExpressions(@NotNull PsiExpression[] occurrences, PsiElement scope) {
PsiElement anchor = null;
for (PsiExpression occurrence : occurrences) {
// if (!occurrence.isPhysical()) continue;
if (scope != null && !PsiTreeUtil.isAncestor(scope, occurrence, false)) {
continue;
}
PsiElement anchor1 = getParentExpressionAnchorElement(occurrence);
if (anchor1 == null) {
return null;
}
if (anchor == null) {
anchor = anchor1;
} else {
PsiElement commonParent = PsiTreeUtil.findCommonParent(anchor, anchor1);
if (commonParent == null || anchor.getTextRange() == null || anchor1.getTextRange() == null) {
return null;
}
PsiElement firstAnchor = anchor.getTextRange().getStartOffset() < anchor1.getTextRange().getStartOffset() ? anchor : anchor1;
if (commonParent.equals(firstAnchor)) {
anchor = firstAnchor;
} else {
if (GosuRefactoringUtil.isStatement(commonParent) && !(commonParent instanceof GosuStatementListImpl)) {
anchor = commonParent;
} else {
PsiElement parent = firstAnchor;
while (!parent.getParent().equals(commonParent)) {
parent = parent.getParent();
}
final PsiElement newAnchor = getParentExpressionAnchorElement(parent);
if (newAnchor != null) {
anchor = newAnchor;
} else {
anchor = parent;
}
}
}
}
}
if (occurrences.length > 1 && anchor.getParent().getParent() instanceof PsiSwitchStatement) {
PsiSwitchStatement switchStatement = (PsiSwitchStatement) anchor.getParent().getParent();
if (switchStatement.getBody().equals(anchor.getParent())) {
int startOffset = occurrences[0].getTextRange().getStartOffset();
int endOffset = occurrences[occurrences.length - 1].getTextRange().getEndOffset();
PsiStatement[] statements = switchStatement.getBody().getStatements();
boolean isInDifferentCases = false;
for (PsiStatement statement : statements) {
if (statement instanceof PsiSwitchLabelStatement) {
int caseOffset = statement.getTextRange().getStartOffset();
if (startOffset < caseOffset && caseOffset < endOffset) {
isInDifferentCases = true;
break;
}
}
}
if (isInDifferentCases) {
anchor = switchStatement;
}
}
}
return anchor;
}
public static boolean isStatement(PsiElement element) {
if (element instanceof PsiStatement) {
return true;
}
if (element instanceof GosuRawPsiElement) {
final ASTNode node = element.getNode();
return node != null && node.getElementType() != null ?
IStatement.class.isAssignableFrom(((GosuElementType) node.getElementType()).getParsedElementType())
: false;
}
return false;
}
/**
* this is gosu replacment for PsiUtil.isStatement(PsiElement)
*
* @return true if element specified is statement or expression statement.
*/
public static boolean isStatementOrExpressionstatement(@NotNull PsiElement element) {
PsiElement parent = element.getParent();
if (element instanceof PsiExpressionListStatement) {
// statement list allowed in for() init or update only
if (!(parent instanceof PsiForStatement)) {
return false;
}
final PsiForStatement forStatement = (PsiForStatement) parent;
if (!(element == forStatement.getInitialization() || element == forStatement.getUpdate())) {
return false;
}
final PsiExpressionList expressionList = ((PsiExpressionListStatement) element).getExpressionList();
final PsiExpression[] expressions = expressionList.getExpressions();
for (PsiExpression expression : expressions) {
if (!isStatementOrExpressionstatement(expression)) {
return false;
}
}
return true;
} else if (element instanceof PsiExpressionStatement) {
return isStatementOrExpressionstatement(((PsiExpressionStatement) element).getExpression());
}
if (element instanceof PsiDeclarationStatement) {
if (parent instanceof PsiCodeBlock) {
return true;
}
if (parent instanceof PsiCodeFragment) {
return true;
}
if (!(parent instanceof PsiForStatement) || ((PsiForStatement) parent).getBody() == element) {
return false;
}
}
if (isStatement(element)) {
return true;
}
if (element instanceof PsiAssignmentExpression) {
return true;
}
if (isIncrementDecrementOperation(element)) {
return true;
}
if (element instanceof PsiMethodCallExpression) {
return true;
}
if (element instanceof PsiCallExpression) {
return true;
}
if (element instanceof PsiNewExpression) {
return !(((PsiNewExpression) element).getType() instanceof PsiArrayType);
}
return element instanceof PsiCodeBlock;
}
public static boolean isIncrementDecrementOperation(@NotNull PsiElement element) {
if (element instanceof PsiPostfixExpression) {
final IElementType sign = ((PsiPostfixExpression) element).getOperationTokenType();
if (sign == JavaTokenType.PLUSPLUS || sign == JavaTokenType.MINUSMINUS) {
return true;
}
} else if (element instanceof PsiPrefixExpression) {
final IElementType sign = ((PsiPrefixExpression) element).getOperationTokenType();
if (sign == JavaTokenType.PLUSPLUS || sign == JavaTokenType.MINUSMINUS) {
return true;
}
}
return false;
}
public static boolean isCompileTimeConstant(PsiExpression expression) {
if (expression instanceof IGosuExpression) {
return ((IGosuExpression) expression).getParsedElement().isCompileTimeConstant();
}
return PsiUtil.isConstantExpression(expression);
}
public static boolean isPsiReferenceExpression(PsiElement element) {
if (element instanceof PsiReferenceExpression || element instanceof IGosuReferenceExpression) {
return true;
}
return false;
}
public static boolean isMethodUsage(PsiElement element) {
if (element instanceof PsiEnumConstant) {
return JavaLanguage.INSTANCE.equals(element.getLanguage());
}
if (!(element instanceof PsiJavaCodeReferenceElement)) {
return false;
}
PsiElement parent = element.getParent();
if (parent instanceof PsiCall) {
return true;
} else if (parent instanceof PsiAnonymousClass) {
return element.equals(((PsiAnonymousClass) parent).getBaseClassReference());
}
return false;
}
@Nullable
public static PsiExpressionList getArgumentListByMethodReference(PsiElement ref) {
if (ref instanceof PsiEnumConstant) {
return ((PsiEnumConstant) ref).getArgumentList();
}
PsiElement parent = ref.getParent();
if (parent instanceof PsiCall) {
return ((PsiCall) parent).getArgumentList();
} else if (parent instanceof PsiAnonymousClass) {
return ((PsiNewExpression) parent.getParent()).getArgumentList();
}
LOG.assertTrue(false);
return null;
}
public static PsiCall getCallExpressionByMethodReference(PsiElement ref) {
if (ref instanceof PsiEnumConstant) {
return (PsiCall) ref;
}
PsiElement parent = ref.getParent();
if (parent instanceof PsiMethodCallExpression) {
return (PsiMethodCallExpression) parent;
} else if (parent instanceof PsiNewExpression) {
return (PsiNewExpression) parent;
} else if (parent instanceof PsiAnonymousClass) {
return (PsiNewExpression) parent.getParent();
} else {
LOG.assertTrue(false);
return null;
}
}
/**
* @return List of highlighters
*/
public static List<RangeHighlighter> highlightAllOccurrences(Project project, PsiElement[] occurrences, Editor editor) {
ArrayList<RangeHighlighter> highlighters = new ArrayList<>();
HighlightManager highlightManager = HighlightManager.getInstance(project);
EditorColorsManager colorsManager = EditorColorsManager.getInstance();
TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
if (occurrences.length > 1) {
for (PsiElement occurrence : occurrences) {
final RangeMarker rangeMarker = occurrence.getUserData(GosuElementToWorkOn.TEXT_RANGE);
if (rangeMarker != null) {
highlightManager
.addRangeHighlight(editor, rangeMarker.getStartOffset(), rangeMarker.getEndOffset(), attributes, true, highlighters);
} else {
final TextRange textRange = occurrence.getTextRange();
highlightManager.addRangeHighlight(editor, textRange.getStartOffset(), textRange.getEndOffset(), attributes, true, highlighters);
}
}
}
return highlighters;
}
public static String createTempVar(PsiExpression expr, PsiElement context, boolean declareFinal) throws IncorrectOperationException {
PsiElement anchorStatement = getParentStatement(context, true);
LOG.assertTrue(anchorStatement != null && anchorStatement.getParent() != null);
Project project = expr.getProject();
String[] suggestedNames =
JavaCodeStyleManager.getInstance(project).suggestVariableName(VariableKind.LOCAL_VARIABLE, null, expr, null).names;
final String prefix = suggestedNames[0];
final String id = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName(prefix, context, true);
PsiElementFactory factory = JavaPsiFacade.getInstance(expr.getProject()).getElementFactory();
if (expr instanceof PsiParenthesizedExpression) {
PsiExpression expr1 = ((PsiParenthesizedExpression) expr).getExpression();
if (expr1 != null) {
expr = expr1;
}
}
PsiDeclarationStatement decl = factory.createVariableDeclarationStatement(id, expr.getType(), expr);
if (declareFinal) {
PsiUtil.setModifierProperty(((PsiLocalVariable) decl.getDeclaredElements()[0]), PsiModifier.FINAL, true);
}
anchorStatement.getParent().addBefore(decl, anchorStatement);
return id;
}
public static int verifySafeCopyExpression(PsiElement expr) {
return verifySafeCopyExpressionSubElement(expr);
}
private static int verifySafeCopyExpressionSubElement(PsiElement element) {
int result = EXPR_COPY_SAFE;
if (element == null) {
return result;
}
if (element instanceof PsiThisExpression || element instanceof PsiSuperExpression || element instanceof PsiIdentifier) {
return EXPR_COPY_SAFE;
}
if (element instanceof PsiMethodCallExpression) {
result = EXPR_COPY_UNSAFE;
}
if (element instanceof PsiNewExpression) {
return EXPR_COPY_PROHIBITED;
}
if (element instanceof PsiAssignmentExpression) {
return EXPR_COPY_PROHIBITED;
}
if (isPlusPlusOrMinusMinus(element)) {
return EXPR_COPY_PROHIBITED;
}
PsiElement[] children = element.getChildren();
for (PsiElement child : children) {
int childResult = verifySafeCopyExpressionSubElement(child);
result = Math.max(result, childResult);
}
return result;
}
public static PsiExpression convertInitializerToNormalExpression(PsiExpression expression, PsiType forcedReturnType)
throws IncorrectOperationException {
if (expression instanceof PsiArrayInitializerExpression) {
return createNewExpressionFromArrayInitializer((PsiArrayInitializerExpression) expression, forcedReturnType);
}
return expression;
}
private static PsiExpression createNewExpressionFromArrayInitializer(PsiArrayInitializerExpression initializer, PsiType forcedType)
throws IncorrectOperationException {
PsiType initializerType = null;
if (initializer != null) {
// initializerType = myExpresssion.getType();
if (forcedType != null) {
initializerType = forcedType;
} else {
initializerType = getTypeByExpression(initializer);
}
}
if (initializerType == null) {
return initializer;
}
LOG.assertTrue(initializerType instanceof PsiArrayType);
PsiElementFactory factory = JavaPsiFacade.getInstance(initializer.getProject()).getElementFactory();
PsiNewExpression result =
(PsiNewExpression) factory.createExpressionFromText("new " + initializerType.getPresentableText() + "{}", null);
result = (PsiNewExpression) CodeStyleManager.getInstance(initializer.getProject()).reformat(result);
PsiArrayInitializerExpression arrayInitializer = result.getArrayInitializer();
LOG.assertTrue(arrayInitializer != null);
arrayInitializer.replace(initializer);
return result;
}
public static void abstractizeMethod(PsiClass targetClass, PsiMethod method) throws IncorrectOperationException {
PsiCodeBlock body = method.getBody();
if (body != null) {
body.delete();
}
PsiUtil.setModifierProperty(method, PsiModifier.ABSTRACT, true);
PsiUtil.setModifierProperty(method, PsiModifier.FINAL, false);
PsiUtil.setModifierProperty(method, PsiModifier.SYNCHRONIZED, false);
PsiUtil.setModifierProperty(method, PsiModifier.NATIVE, false);
if (!targetClass.isInterface()) {
PsiUtil.setModifierProperty(targetClass, PsiModifier.ABSTRACT, true);
}
removeFinalParameters(method);
}
public static boolean isInsideAnonymous(PsiElement element, PsiElement upTo) {
for (PsiElement current = element; current != null && current != upTo; current = current.getParent()) {
if (current instanceof PsiAnonymousClass) {
return true;
}
}
return false;
}
public static PsiExpression unparenthesizeExpression(PsiExpression expression) {
while (expression instanceof PsiParenthesizedExpression) {
final PsiExpression innerExpression = ((PsiParenthesizedExpression) expression).getExpression();
if (innerExpression == null) {
return expression;
}
expression = innerExpression;
}
return expression;
}
public static PsiExpression outermostParenthesizedExpression(PsiExpression expression) {
while (expression.getParent() instanceof PsiParenthesizedExpression) {
expression = (PsiParenthesizedExpression) expression.getParent();
}
return expression;
}
public static String getNewInnerClassName(PsiClass aClass, String oldInnerClassName, String newName) {
if (!oldInnerClassName.endsWith(aClass.getName())) {
return newName;
}
StringBuilder buffer = new StringBuilder(oldInnerClassName);
buffer.replace(buffer.length() - aClass.getName().length(), buffer.length(), newName);
return buffer.toString();
}
public static boolean isSuperOrThisCall(PsiStatement statement, boolean testForSuper, boolean testForThis) {
if (!(statement instanceof PsiExpressionStatement)) {
return false;
}
PsiExpression expression = ((PsiExpressionStatement) statement).getExpression();
if (!(expression instanceof PsiMethodCallExpression)) {
return false;
}
final PsiReferenceExpression methodExpression = ((PsiMethodCallExpression) expression).getMethodExpression();
if (testForSuper) {
if ("super".equals(methodExpression.getText())) {
return true;
}
}
if (testForThis) {
if ("this".equals(methodExpression.getText())) {
return true;
}
}
return false;
}
public static void visitImplicitSuperConstructorUsages(PsiClass subClass,
final ImplicitConstructorUsageVisitor implicitConstructorUsageVistor,
PsiClass superClass) {
final PsiMethod baseDefaultConstructor = findDefaultConstructor(superClass);
final PsiMethod[] constructors = subClass.getConstructors();
if (constructors.length > 0) {
for (PsiMethod constructor : constructors) {
final PsiStatement[] statements = constructor.getBody().getStatements();
if (statements.length < 1 || !isSuperOrThisCall(statements[0], true, true)) {
implicitConstructorUsageVistor.visitConstructor(constructor, baseDefaultConstructor);
}
}
} else {
implicitConstructorUsageVistor.visitClassWithoutConstructors(subClass);
}
}
private static PsiMethod findDefaultConstructor(final PsiClass aClass) {
final PsiMethod[] constructors = aClass.getConstructors();
for (PsiMethod constructor : constructors) {
if (constructor.getParameterList().getParametersCount() == 0) {
return constructor;
}
}
return null;
}
public static void replaceMovedMemberTypeParameters(final PsiElement member,
final Iterable<PsiTypeParameter> parametersIterable,
final PsiSubstitutor substitutor,
final PsiElementFactory factory) {
for (PsiTypeParameter parameter : parametersIterable) {
PsiType substitutedType = substitutor.substitute(parameter);
if (substitutedType == null) {
substitutedType = TypeConversionUtil.erasure(factory.createType(parameter));
}
for (PsiReference reference : ReferencesSearch.search(parameter, new LocalSearchScope(member))) {
final PsiElement element = reference.getElement();
final PsiElement parent = element.getParent();
if (parent instanceof PsiTypeElement) {
parent.replace(factory.createTypeElement(substitutedType));
} else if (element instanceof PsiJavaCodeReferenceElement && substitutedType instanceof PsiClassType) {
element.replace(factory.createReferenceElementByType((PsiClassType) substitutedType));
}
}
}
}
@Nullable
public static PsiMethod getChainedConstructor(PsiMethod constructor) {
final PsiCodeBlock constructorBody = constructor.getBody();
if (constructorBody == null) {
return null;
}
final PsiStatement[] statements = constructorBody.getStatements();
if (statements.length == 1 && statements[0] instanceof PsiExpressionStatement) {
final PsiExpression expression = ((PsiExpressionStatement) statements[0]).getExpression();
if (expression instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression) expression;
final PsiReferenceExpression methodExpr = methodCallExpression.getMethodExpression();
if ("this".equals(methodExpr.getReferenceName())) {
return (PsiMethod) methodExpr.resolve();
}
}
}
return null;
}
public static boolean isInMovedElement(PsiElement element, Set<PsiMember> membersToMove) {
for (PsiMember member : membersToMove) {
if (PsiTreeUtil.isAncestor(member, element, false)) {
return true;
}
}
return false;
}
public static boolean inImportStatement(PsiReference ref, PsiElement element) {
if (PsiTreeUtil.getParentOfType(element, PsiImportStatement.class) != null) {
return true;
}
final PsiFile containingFile = element.getContainingFile();
if (containingFile instanceof PsiJavaFile) {
final PsiImportList importList = ((PsiJavaFile) containingFile).getImportList();
if (importList != null) {
final TextRange refRange = ref.getRangeInElement().shiftRight(element.getTextRange().getStartOffset());
for (PsiImportStatementBase importStatementBase : importList.getAllImportStatements()) {
final TextRange textRange = importStatementBase.getTextRange();
if (textRange.contains(refRange)) {
return true;
}
}
}
}
return false;
}
public static interface ImplicitConstructorUsageVisitor {
void visitConstructor(PsiMethod constructor, PsiMethod baseConstructor);
void visitClassWithoutConstructors(PsiClass aClass);
}
public interface Graph<T> {
Set<T> getVertices();
Set<T> getTargets(T source);
}
/**
* Returns subset of <code>graph.getVertices()</code> that is a tranistive closure (by <code>graph.getTargets()<code>)
* of the following property: initialRelation.value() of vertex or <code>graph.getTargets(vertex)</code> is true.
* <p/>
* Note that <code>graph.getTargets()</code> is not neccesrily a subset of <code>graph.getVertex()</code>
*
* @param graph
* @param initialRelation
* @return subset of graph.getVertices()
*/
public static <T> Set<T> transitiveClosure(Graph<T> graph, Condition<T> initialRelation) {
Set<T> result = new HashSet<>();
final Set<T> vertices = graph.getVertices();
boolean anyChanged;
do {
anyChanged = false;
for (T currentVertex : vertices) {
if (!result.contains(currentVertex)) {
if (!initialRelation.value(currentVertex)) {
Set<T> targets = graph.getTargets(currentVertex);
for (T currentTarget : targets) {
if (result.contains(currentTarget) || initialRelation.value(currentTarget)) {
result.add(currentVertex);
anyChanged = true;
break;
}
}
} else {
result.add(currentVertex);
}
}
}
}
while (anyChanged);
return result;
}
public static boolean equivalentTypes(PsiType t1, PsiType t2, PsiManager manager) {
while (t1 instanceof PsiArrayType) {
if (!(t2 instanceof PsiArrayType)) {
return false;
}
t1 = ((PsiArrayType) t1).getComponentType();
t2 = ((PsiArrayType) t2).getComponentType();
}
if (t1 instanceof PsiPrimitiveType) {
return t2 instanceof PsiPrimitiveType && t1.equals(t2);
}
return manager.areElementsEquivalent(PsiUtil.resolveClassInType(t1), PsiUtil.resolveClassInType(t2));
}
public static List<PsiVariable> collectReferencedVariables(PsiElement scope) {
final List<PsiVariable> result = new ArrayList<>();
scope.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
final PsiElement element = expression.resolve();
if (element instanceof PsiVariable) {
result.add((PsiVariable) element);
}
final PsiExpression qualifier = expression.getQualifierExpression();
if (qualifier != null) {
qualifier.accept(this);
}
}
});
return result;
}
public static boolean isModifiedInScope(PsiVariable variable, PsiElement scope) {
for (PsiReference reference : ReferencesSearch.search(variable, new LocalSearchScope(scope), false)) {
if (isAssignmentLHS(reference.getElement())) {
return true;
}
}
return false;
}
private static String getNameOfReferencedParameter(PsiDocTag tag) {
LOG.assertTrue("param".equals(tag.getName()));
final PsiElement[] dataElements = tag.getDataElements();
if (dataElements.length < 1) {
return null;
}
return dataElements[0].getText();
}
public static void fixJavadocsForParams(PsiMethod method, Set<PsiParameter> newParameters) throws IncorrectOperationException {
final PsiDocComment docComment = method.getDocComment();
if (docComment == null) {
return;
}
final PsiParameter[] parameters = method.getParameterList().getParameters();
final PsiDocTag[] paramTags = docComment.findTagsByName("param");
if (parameters.length > 0 && newParameters.size() < parameters.length && paramTags.length == 0) {
return;
}
Map<PsiParameter, PsiDocTag> tagForParam = new HashMap<>();
for (PsiParameter parameter : parameters) {
boolean found = false;
for (PsiDocTag paramTag : paramTags) {
if (parameter.getName().equals(getNameOfReferencedParameter(paramTag))) {
tagForParam.put(parameter, paramTag);
found = true;
break;
}
}
if (!found && !newParameters.contains(parameter)) {
tagForParam.put(parameter, null);
}
}
List<PsiDocTag> newTags = new ArrayList<>();
for (PsiParameter parameter : parameters) {
if (tagForParam.containsKey(parameter)) {
final PsiDocTag psiDocTag = tagForParam.get(parameter);
if (psiDocTag != null) {
newTags.add((PsiDocTag) psiDocTag.copy());
}
} else {
newTags.add(JavaPsiFacade.getInstance(method.getProject()).getElementFactory().createParamTag(parameter.getName(), ""));
}
}
PsiElement anchor = paramTags.length > 0 ? paramTags[0].getPrevSibling() : null;
for (PsiDocTag paramTag : paramTags) {
paramTag.delete();
}
for (PsiDocTag psiDocTag : newTags) {
anchor = docComment.addAfter(psiDocTag, anchor);
}
}
public static PsiDirectory createPackageDirectoryInSourceRoot(PackageWrapper aPackage, final VirtualFile sourceRoot)
throws IncorrectOperationException {
final PsiDirectory[] directories = aPackage.getDirectories();
for (PsiDirectory directory : directories) {
if (VfsUtil.isAncestor(sourceRoot, directory.getVirtualFile(), false)) {
return directory;
}
}
String qNameToCreate = qNameToCreateInSourceRoot(aPackage, sourceRoot);
final String[] shortNames = qNameToCreate.split("\\.");
PsiDirectory current = aPackage.getManager().findDirectory(sourceRoot);
LOG.assertTrue(current != null);
for (String shortName : shortNames) {
PsiDirectory subdirectory = current.findSubdirectory(shortName);
if (subdirectory == null) {
subdirectory = current.createSubdirectory(shortName);
}
current = subdirectory;
}
return current;
}
public static String qNameToCreateInSourceRoot(PackageWrapper aPackage, final VirtualFile sourceRoot) throws IncorrectOperationException {
String targetQName = aPackage.getQualifiedName();
String sourceRootPackage =
ProjectRootManager.getInstance(aPackage.getManager().getProject()).getFileIndex().getPackageNameByDirectory(sourceRoot);
if (!canCreateInSourceRoot(sourceRootPackage, targetQName)) {
throw new IncorrectOperationException(
"Cannot create package '" + targetQName + "' in source folder " + sourceRoot.getPresentableUrl());
}
String result = targetQName.substring(sourceRootPackage.length());
if (StringUtil.startsWithChar(result, '.')) {
result = result.substring(1); // remove initial '.'
}
return result;
}
public static boolean canCreateInSourceRoot(final String sourceRootPackage, final String targetQName) {
if (sourceRootPackage == null || !targetQName.startsWith(sourceRootPackage)) {
return false;
}
if (sourceRootPackage.length() == 0 || targetQName.length() == sourceRootPackage.length()) {
return true;
}
return targetQName.charAt(sourceRootPackage.length()) == '.';
}
@Nullable
public static PsiDirectory findPackageDirectoryInSourceRoot(PackageWrapper aPackage, final VirtualFile sourceRoot) {
final PsiDirectory[] directories = aPackage.getDirectories();
for (PsiDirectory directory : directories) {
if (VfsUtil.isAncestor(sourceRoot, directory.getVirtualFile(), false)) {
return directory;
}
}
String qNameToCreate;
try {
qNameToCreate = qNameToCreateInSourceRoot(aPackage, sourceRoot);
} catch (IncorrectOperationException e) {
return null;
}
final String[] shortNames = qNameToCreate.split("\\.");
PsiDirectory current = aPackage.getManager().findDirectory(sourceRoot);
LOG.assertTrue(current != null);
for (String shortName : shortNames) {
PsiDirectory subdirectory = current.findSubdirectory(shortName);
if (subdirectory == null) {
return null;
}
current = subdirectory;
}
return current;
}
public static class ConditionCache<T> implements Condition<T> {
private final Condition<T> myCondition;
private final HashSet<T> myProcessedSet = new HashSet<>();
private final HashSet<T> myTrueSet = new HashSet<>();
public ConditionCache(Condition<T> condition) {
myCondition = condition;
}
public boolean value(T object) {
if (!myProcessedSet.contains(object)) {
myProcessedSet.add(object);
final boolean value = myCondition.value(object);
if (value) {
myTrueSet.add(object);
return true;
}
return false;
}
return myTrueSet.contains(object);
}
}
public static class IsDescendantOf implements Condition<PsiClass> {
private final PsiClass myClass;
private final ConditionCache<PsiClass> myConditionCache;
public IsDescendantOf(PsiClass aClass) {
myClass = aClass;
myConditionCache = new ConditionCache<>(new Condition<PsiClass>() {
public boolean value(PsiClass aClass) {
return InheritanceUtil.isInheritorOrSelf(aClass, myClass, true);
}
});
}
public boolean value(PsiClass aClass) {
return myConditionCache.value(aClass);
}
}
@Nullable
public static PsiTypeParameterList createTypeParameterListWithUsedTypeParameters(@NotNull final PsiElement... elements) {
return createTypeParameterListWithUsedTypeParameters(null, elements);
}
@Nullable
public static PsiTypeParameterList createTypeParameterListWithUsedTypeParameters(final PsiTypeParameterList fromList,
@NotNull final PsiElement... elements) {
return createTypeParameterListWithUsedTypeParameters(fromList, Condition.TRUE, elements);
}
@Nullable
public static PsiTypeParameterList createTypeParameterListWithUsedTypeParameters(final PsiTypeParameterList fromList,
Condition<PsiTypeParameter> filter,
@NotNull final PsiElement... elements) {
if (elements.length == 0) {
return null;
}
final Set<PsiTypeParameter> used = new HashSet<>();
for (final PsiElement element : elements) {
if (element == null) {
continue;
}
collectTypeParameters(used, element, filter); //pull up extends cls class with type params
}
if (fromList != null) {
used.retainAll(Arrays.asList(fromList.getTypeParameters()));
}
PsiTypeParameter[] typeParameters = used.toArray(new PsiTypeParameter[used.size()]);
Arrays.sort(typeParameters, new Comparator<PsiTypeParameter>() {
public int compare(final PsiTypeParameter tp1, final PsiTypeParameter tp2) {
return tp1.getTextRange().getStartOffset() - tp2.getTextRange().getStartOffset();
}
});
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(elements[0].getProject()).getElementFactory();
try {
final PsiClass aClass = elementFactory.createClassFromText("class A {}", null);
PsiTypeParameterList list = aClass.getTypeParameterList();
assert list != null;
for (final PsiTypeParameter typeParameter : typeParameters) {
list.add(typeParameter);
}
return list;
} catch (IncorrectOperationException e) {
LOG.error(e);
assert false;
return null;
}
}
public static void collectTypeParameters(final Set<PsiTypeParameter> used, final PsiElement element) {
collectTypeParameters(used, element, Condition.TRUE);
}
public static void collectTypeParameters(final Set<PsiTypeParameter> used, final PsiElement element,
final Condition<PsiTypeParameter> filter) {
element.accept(new JavaRecursiveElementVisitor() {
@Override
public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
super.visitReferenceElement(reference);
if (!reference.isQualified()) {
final PsiElement resolved = reference.resolve();
if (resolved instanceof PsiTypeParameter) {
final PsiTypeParameter typeParameter = (PsiTypeParameter) resolved;
if (PsiTreeUtil.isAncestor(typeParameter.getOwner(), element, false) && filter.value(typeParameter)) {
used.add(typeParameter);
}
}
}
}
@Override
public void visitExpression(final PsiExpression expression) {
super.visitExpression(expression);
final PsiType type = expression.getType();
if (type != null) {
final TypeParameterSearcher searcher = new TypeParameterSearcher();
type.accept(searcher);
for (PsiTypeParameter typeParam : searcher.myTypeParams) {
if (PsiTreeUtil.isAncestor(typeParam.getOwner(), element, false) && filter.value(typeParam)) {
used.add(typeParam);
}
}
}
}
class TypeParameterSearcher extends PsiTypeVisitor<Boolean> {
private final Set<PsiTypeParameter> myTypeParams = new java.util.HashSet<>();
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.add((PsiTypeParameter) aClass);
}
final PsiType[] types = classType.getParameters();
for (final PsiType psiType : types) {
psiType.accept(this);
}
return false;
}
public Boolean visitWildcardType(final PsiWildcardType wildcardType) {
final PsiType bound = wildcardType.getBound();
if (bound != null) {
bound.accept(this);
}
return false;
}
}
});
}
public static PsiType findFragmentType(PsiElement fragment) {
GosuTypeLiteralImpl typeLiteral = PsiTreeUtil.findChildOfType(fragment, GosuTypeLiteralImpl.class);
if (typeLiteral != null) {
return typeLiteral.getType();
}
String text = fragment.getText();
String qName = ((GosuFragmentFileImpl) fragment).getImports().get(text);
if (qName == null) {
qName = text;
}
return ((GosuTypeLiteralImpl) GosuPsiParseUtil.parseTypeLiteral(qName, fragment)).getType();
}
}