blob: 37fb882e5e6675e1dc7dc9bd9a4814e9c6c17a09 [file] [log] [blame]
/*
* Copyright 2000-2012 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.codeInsight.intention.impl;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.search.LocalSearchScope;
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.util.RefactoringUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
/**
* @author Danila Ponomarenko
*/
public final class FieldFromParameterUtils {
@Nullable
public static PsiParameter findParameterAtCursor(@NotNull PsiFile file, @NotNull Editor editor) {
final int offset = editor.getCaretModel().getOffset();
final PsiParameterList parameterList = PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiParameterList.class, false);
if (parameterList == null) return null;
final PsiParameter[] parameters = parameterList.getParameters();
for (PsiParameter parameter : parameters) {
final TextRange range = parameter.getTextRange();
if (range.getStartOffset() <= offset && offset <= range.getEndOffset()) return parameter;
}
return null;
}
@Nullable
public static PsiType getType(@Nullable PsiParameter myParameter) {
if (myParameter == null) return null;
PsiType type = myParameter.getType();
return type instanceof PsiEllipsisType ? ((PsiEllipsisType)type).toArrayType() : type;
}
@Nullable
public static PsiType getSubstitutedType(@Nullable PsiParameter parameter) {
if (parameter == null) return null;
final PsiType type = getType(parameter);
if (type instanceof PsiArrayType) {
return type;
}
final PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(type);
final PsiClass psiClass = result.getElement();
if (psiClass == null) return type;
final HashSet<PsiTypeParameter> usedTypeParameters = new HashSet<PsiTypeParameter>();
RefactoringUtil.collectTypeParameters(usedTypeParameters, parameter);
for (Iterator<PsiTypeParameter> iterator = usedTypeParameters.iterator(); iterator.hasNext(); ) {
PsiTypeParameter usedTypeParameter = iterator.next();
if (parameter.getDeclarationScope() != usedTypeParameter.getOwner()) {
iterator.remove();
}
}
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(parameter.getProject());
PsiSubstitutor subst = PsiSubstitutor.EMPTY;
for (PsiTypeParameter usedTypeParameter : usedTypeParameters) {
subst = subst.put(usedTypeParameter, TypeConversionUtil.typeParameterErasure(usedTypeParameter));
}
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
final Map<PsiTypeParameter, PsiType> typeMap = result.getSubstitutor().getSubstitutionMap();
for (PsiTypeParameter typeParameter : typeMap.keySet()) {
final PsiType psiType = typeMap.get(typeParameter);
substitutor = substitutor.put(typeParameter, psiType != null ? subst.substitute(psiType) : null);
}
return psiClass instanceof PsiTypeParameter ? subst.substitute((PsiTypeParameter)psiClass) : elementFactory.createType(psiClass, substitutor);
}
@Nullable
public static PsiField getParameterAssignedToField(final PsiParameter parameter) {
for (PsiReference reference : ReferencesSearch.search(parameter, new LocalSearchScope(parameter.getDeclarationScope()), false)) {
if (!(reference instanceof PsiReferenceExpression)) continue;
final PsiReferenceExpression expression = (PsiReferenceExpression)reference;
if (!(expression.getParent() instanceof PsiAssignmentExpression)) continue;
final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression.getParent();
if (assignmentExpression.getRExpression() != expression) continue;
final PsiExpression lExpression = assignmentExpression.getLExpression();
if (!(lExpression instanceof PsiReferenceExpression)) continue;
final PsiElement element = ((PsiReferenceExpression)lExpression).resolve();
if (element instanceof PsiField) return (PsiField)element;
}
return null;
}
public static int findFieldAssignmentAnchor(final PsiStatement[] statements,
final @Nullable Ref<Pair<PsiField, Boolean>> anchorRef,
final PsiClass targetClass,
final PsiParameter myParameter) {
int i = 0;
for (; i < statements.length; i++) {
PsiStatement psiStatement = statements[i];
if (psiStatement instanceof PsiExpressionStatement) {
PsiExpressionStatement expressionStatement = (PsiExpressionStatement)psiStatement;
PsiExpression expression = expressionStatement.getExpression();
if (expression instanceof PsiMethodCallExpression) {
PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression;
@NonNls String text = methodCallExpression.getMethodExpression().getText();
if (text.equals("super") || text.equals("this")) {
continue;
}
}
else if (expression instanceof PsiAssignmentExpression) {
PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression;
PsiExpression lExpression = assignmentExpression.getLExpression();
PsiExpression rExpression = assignmentExpression.getRExpression();
if (!(lExpression instanceof PsiReferenceExpression)) break;
if (!(rExpression instanceof PsiReferenceExpression)) break;
PsiReferenceExpression lReference = (PsiReferenceExpression)lExpression;
PsiReferenceExpression rReference = (PsiReferenceExpression)rExpression;
PsiElement lElement = lReference.resolve();
PsiElement rElement = rReference.resolve();
if (!(lElement instanceof PsiField) || ((PsiField)lElement).getContainingClass() != targetClass) break;
if (!(rElement instanceof PsiParameter)) break;
if (myParameter.getTextRange().getStartOffset() < rElement.getTextRange().getStartOffset()) {
if (anchorRef != null) {
anchorRef.set(Pair.create((PsiField)lElement, Boolean.TRUE));
}
break;
}
if (anchorRef != null) {
anchorRef.set(Pair.create((PsiField)lElement, Boolean.FALSE));
}
continue;
}
}
break;
}
return i;
}
public static void createFieldAndAddAssignment(final @NotNull Project project,
final @NotNull PsiClass targetClass,
final @NotNull PsiMethod method,
final @NotNull PsiParameter parameter,
final @NotNull PsiType fieldType, final @NotNull String fieldName,
final boolean isStatic, final boolean isFinal) throws IncorrectOperationException {
PsiManager psiManager = PsiManager.getInstance(project);
PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory();
PsiField field = factory.createField(fieldName, fieldType);
PsiModifierList modifierList = field.getModifierList();
if (modifierList == null) return;
modifierList.setModifierProperty(PsiModifier.STATIC, isStatic);
modifierList.setModifierProperty(PsiModifier.FINAL, isFinal);
final NullableNotNullManager manager = NullableNotNullManager.getInstance(project);
final PsiAnnotation nullable = manager.getNullableAnnotation(parameter, false);
if (nullable != null && !manager.isContainerAnnotation(nullable)) {
modifierList.addAfter(factory.createAnnotationFromText("@" + nullable.getQualifiedName(), field), null);
}
else if (isFinal) {
final PsiAnnotation notNull = manager.getNotNullAnnotation(parameter, false);
if (notNull != null && !manager.isContainerAnnotation(notNull)) {
modifierList.addAfter(factory.createAnnotationFromText("@" + notNull.getQualifiedName(), field), null);
}
}
PsiCodeBlock methodBody = method.getBody();
if (methodBody == null) return;
PsiStatement[] statements = methodBody.getStatements();
Ref<Pair<PsiField, Boolean>> anchorRef = new Ref<Pair<PsiField, Boolean>>();
int i = findFieldAssignmentAnchor(statements, anchorRef, targetClass, parameter);
Pair<PsiField, Boolean> fieldAnchor = anchorRef.get();
String stmtText = fieldName + " = " + parameter.getName() + ";";
if (fieldName.equals(parameter.getName())) {
@NonNls String prefix = isStatic ? targetClass.getName() == null ? "" : targetClass.getName() + "." : "this.";
stmtText = prefix + stmtText;
}
PsiStatement assignmentStmt = factory.createStatementFromText(stmtText, methodBody);
assignmentStmt = (PsiStatement)CodeStyleManager.getInstance(project).reformat(assignmentStmt);
if (i == statements.length) {
methodBody.add(assignmentStmt);
}
else {
methodBody.addAfter(assignmentStmt, i > 0 ? statements[i - 1] : null);
}
if (fieldAnchor != null) {
PsiVariable psiVariable = fieldAnchor.getFirst();
psiVariable.normalizeDeclaration();
}
boolean found = false;
final PsiField[] fields = targetClass.getFields();
for (PsiField f : fields) {
if (f.getName().equals(field.getName())) {
found = true;
break;
}
}
if (!found) {
if (fieldAnchor != null) {
Boolean insertBefore = fieldAnchor.getSecond();
PsiField inField = fieldAnchor.getFirst();
if (insertBefore.booleanValue()) {
targetClass.addBefore(field, inField);
}
else {
targetClass.addAfter(field, inField);
}
}
else {
targetClass.add(field);
}
}
}
public static boolean isAvailable(@Nullable PsiParameter myParameter,
@Nullable PsiType type,
@Nullable PsiClass targetClass){
return myParameter != null
&& myParameter.isValid()
&& myParameter.getManager().isInProject(myParameter)
&& myParameter.getDeclarationScope() instanceof PsiMethod
&& ((PsiMethod)myParameter.getDeclarationScope()).getBody() != null
&& type != null
&& type.isValid()
&& targetClass != null
&& !targetClass.isInterface()
&& getParameterAssignedToField(myParameter) == null;
}
private FieldFromParameterUtils() {
}
}