blob: 28da2a95e3b63a1cf739c9eff8e27109ec7ad45c [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.refactoring.introduceVariable;
import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
import com.intellij.codeInsight.template.impl.TemplateState;
import com.intellij.ui.ListCellRendererWrapper;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.VisualPosition;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Key;
import com.intellij.psi.*;
import com.intellij.psi.scope.processor.VariablesProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.rename.inplace.InplaceRefactoring;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.JBList;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
/**
* User: anna
* Date: 11/8/10
*/
public class ReassignVariableUtil {
static final Key<SmartPsiElementPointer<PsiDeclarationStatement>> DECLARATION_KEY = Key.create("var.type");
static final Key<RangeMarker[]> OCCURRENCES_KEY = Key.create("occurrences");
private ReassignVariableUtil() {
}
static boolean reassign(final Editor editor) {
final SmartPsiElementPointer<PsiDeclarationStatement> pointer = editor.getUserData(DECLARATION_KEY);
final PsiDeclarationStatement declaration = pointer != null ? pointer.getElement() : null;
final PsiType type = getVariableType(declaration);
if (type != null) {
VariablesProcessor proc = findVariablesOfType(declaration, type);
if (proc.size() > 0) {
if (proc.size() == 1) {
replaceWithAssignment(declaration, proc.getResult(0), editor);
return true;
}
final DefaultListModel model = new DefaultListModel();
for (int i = 0; i < proc.size(); i++) {
model.addElement(proc.getResult(i));
}
final JList list = new JBList(model);
list.setCellRenderer(new ListCellRendererWrapper() {
@Override
public void customize(JList list, Object value, int index, boolean selected, boolean hasFocus) {
if (value instanceof PsiVariable) {
setText(((PsiVariable)value).getName());
setIcon(((PsiVariable)value).getIcon(0));
}
}
});
final VisualPosition visualPosition = editor.getCaretModel().getVisualPosition();
final Point point = editor.visualPositionToXY(new VisualPosition(visualPosition.line + 1, visualPosition.column));
JBPopupFactory.getInstance().createListPopupBuilder(list)
.setTitle("Choose variable to reassign")
.setRequestFocus(true)
.setItemChoosenCallback(new Runnable() {
public void run() {
replaceWithAssignment(declaration, (PsiVariable)list.getSelectedValue(), editor);
}
}).createPopup().show(new RelativePoint(editor.getContentComponent(), point));
}
return true;
}
return false;
}
@Nullable
static PsiType getVariableType(@Nullable PsiDeclarationStatement declaration) {
if (declaration != null) {
final PsiElement[] declaredElements = declaration.getDeclaredElements();
if (declaredElements.length > 0 && declaredElements[0] instanceof PsiVariable) {
return ((PsiVariable)declaredElements[0]).getType();
}
}
return null;
}
static VariablesProcessor findVariablesOfType(final PsiDeclarationStatement declaration, final PsiType type) {
VariablesProcessor proc = new VariablesProcessor(false) {
@Override
protected boolean check(PsiVariable var, ResolveState state) {
for (PsiElement element : declaration.getDeclaredElements()) {
if (element == var) return false;
}
return TypeConversionUtil.isAssignable(var.getType(), type);
}
};
PsiElement scope = declaration;
while (scope != null) {
if (scope instanceof PsiFile || scope instanceof PsiMethod || scope instanceof PsiClassInitializer) break;
scope = scope.getParent();
}
if (scope == null) return proc;
PsiScopesUtil.treeWalkUp(proc, declaration, scope);
return proc;
}
static void replaceWithAssignment(final PsiDeclarationStatement declaration, final PsiVariable variable, final Editor editor) {
final PsiVariable var = (PsiVariable)declaration.getDeclaredElements()[0];
final PsiExpression initializer = var.getInitializer();
new WriteCommandAction(declaration.getProject()) {
@Override
protected void run(Result result) throws Throwable {
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(variable.getProject());
final String chosenVariableName = variable.getName();
//would generate red code for final variables
PsiElement newDeclaration = elementFactory.createStatementFromText(chosenVariableName + " = " + initializer.getText() + ";",
declaration);
newDeclaration = declaration.replace(newDeclaration);
final PsiFile containingFile = newDeclaration.getContainingFile();
final RangeMarker[] occurrenceMarkers = editor.getUserData(OCCURRENCES_KEY);
if (occurrenceMarkers != null) {
for (RangeMarker marker : occurrenceMarkers) {
final PsiElement refVariableElement = containingFile.findElementAt(marker.getStartOffset());
final PsiExpression expression = PsiTreeUtil.getParentOfType(refVariableElement, PsiReferenceExpression.class);
if (expression != null) {
expression.replace(elementFactory.createExpressionFromText(chosenVariableName, newDeclaration));
}
}
}
}
}.execute();
finishTemplate(editor);
}
private static void finishTemplate(Editor editor) {
final TemplateState templateState = TemplateManagerImpl.getTemplateState(editor);
final InplaceRefactoring renamer = editor.getUserData(InplaceRefactoring.INPLACE_RENAMER);
if (templateState != null && renamer != null) {
templateState.gotoEnd(true);
editor.putUserData(InplaceRefactoring.INPLACE_RENAMER, null);
}
}
}