blob: 2439aafc1e87831ed3cc1cc961a42944b4b2a80f [file] [log] [blame]
/*
* Copyright 2000-2009 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.generation.surroundWith;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.impl.source.tree.ElementType;
import com.intellij.psi.impl.source.tree.JavaJspElementType;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
public class SurroundWithUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.surroundWith.SurroundWithUtil");
private SurroundWithUtil() {
}
static PsiElement[] moveDeclarationsOut(PsiElement block, PsiElement[] statements, boolean generateInitializers) {
try{
PsiManager psiManager = block.getManager();
PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory();
ArrayList<PsiElement> array = new ArrayList<PsiElement>();
for (PsiElement statement : statements) {
if (statement instanceof PsiDeclarationStatement) {
PsiDeclarationStatement declaration = (PsiDeclarationStatement)statement;
if (needToDeclareOut(statements, declaration)) {
PsiElement[] elements = declaration.getDeclaredElements();
for (PsiElement element : elements) {
PsiVariable var = (PsiVariable)element;
PsiExpression initializer = var.getInitializer();
if (initializer != null) {
String name = var.getName();
PsiExpressionStatement assignment = (PsiExpressionStatement)factory.createStatementFromText(name + "=x;", null);
assignment = (PsiExpressionStatement)CodeStyleManager.getInstance(psiManager.getProject()).reformat(assignment);
PsiAssignmentExpression expr = (PsiAssignmentExpression)assignment.getExpression();
expr.getRExpression().replace(RefactoringUtil.convertInitializerToNormalExpression(initializer, var.getType()));
assignment = (PsiExpressionStatement)block.addAfter(assignment, declaration);
array.add(assignment);
}
}
PsiDeclarationStatement newDeclaration;
if (!array.isEmpty()) {
PsiElement firstStatement = array.get(0);
newDeclaration = (PsiDeclarationStatement)block.addBefore(declaration, firstStatement);
declaration.delete();
}
else {
newDeclaration = declaration;
}
elements = newDeclaration.getDeclaredElements();
for (PsiElement element1 : elements) {
PsiVariable var = (PsiVariable)element1;
PsiExpression initializer = var.getInitializer();
if (initializer != null) {
if (!generateInitializers || var.hasModifierProperty(PsiModifier.FINAL)) {
initializer.delete();
}
else {
String defaultValue = PsiTypesUtil.getDefaultValueOfType(var.getType());
PsiExpression expr = factory.createExpressionFromText(defaultValue, null);
initializer.replace(expr);
}
}
}
continue;
}
}
array.add(statement);
}
return PsiUtilCore.toPsiElementArray(array);
}
catch(IncorrectOperationException e){
LOG.error(e);
return statements;
}
}
private static boolean needToDeclareOut(PsiElement[] statements, PsiDeclarationStatement statement) {
PsiElement[] elements = statement.getDeclaredElements();
PsiElement lastStatement = statements[statements.length - 1];
int endOffset = lastStatement.getTextRange().getEndOffset();
for (PsiElement element : elements) {
if (element instanceof PsiVariable) {
GlobalSearchScope projectScope = GlobalSearchScope.projectScope(element.getProject());
PsiReference[] refs = ReferencesSearch.search(element, projectScope, false).toArray(PsiReference.EMPTY_ARRAY);
if (refs.length > 0) {
PsiReference lastRef = refs[refs.length - 1];
if (lastRef.getElement().getTextOffset() > endOffset) {
return true;
}
}
}
}
return false;
}
public static TextRange getRangeToSelect (@NotNull PsiCodeBlock block) {
PsiElement first = block.getFirstBodyElement();
if (first instanceof PsiWhiteSpace) {
first = first.getNextSibling();
}
if (first == null) {
int offset = block.getTextRange().getStartOffset() + 1;
return new TextRange(offset, offset);
}
PsiJavaToken rBrace = block.getRBrace();
PsiElement last = rBrace.getPrevSibling();
if (last instanceof PsiWhiteSpace) {
last = last.getPrevSibling();
}
final int startOffset = first.getTextRange().getStartOffset();
final int endOffset = last.getTextRange().getEndOffset();
return startOffset <= endOffset ? new TextRange(startOffset, endOffset)
: new TextRange(startOffset, startOffset);
}
/**
* Performs indentation (if necessary) of the given code block that surrounds target statements.
* <p/>
* The main trick here is to handle situations like the one below:
* <pre>
* void test() {
* // This is comment
* int i = 1;
* }
* </pre>
* The problem is that surround block doesn't contain any indent spaces, hence, the first statement is inserted to the
* zero column. But we have a dedicated code style setting <code>'keep comment at first column'</code>, i.e. the comment
* will not be moved if that setting is checked.
* <p/>
* Current method handles that situation.
*
* @param container code block that surrounds target statements
* @param statements target statements being surrounded
*/
public static void indentCommentIfNecessary(@NotNull PsiCodeBlock container, @Nullable PsiElement[] statements) {
if (statements == null || statements.length <= 0) {
return;
}
PsiElement first = statements[0];
ASTNode node = first.getNode();
if (node == null || !ElementType.JAVA_COMMENT_BIT_SET.contains(node.getElementType())) {
return;
}
ASTNode commentWsText = node.getTreePrev();
if (commentWsText == null || !JavaJspElementType.WHITE_SPACE_BIT_SET.contains(commentWsText.getElementType())) {
return;
}
int indent = 0;
CharSequence text = commentWsText.getChars();
for (int i = text.length() - 1; i >= 0; i--, indent++) {
if (text.charAt(i) == '\n') {
break;
}
}
if (indent <= 0) {
return;
}
PsiElement codeBlockWsElement = null;
ASTNode codeBlockWsNode = null;
boolean lbraceFound = false;
final PsiParserFacade parserFacade = PsiParserFacade.SERVICE.getInstance(container.getProject());
for (PsiElement codeBlockChild = container.getFirstChild(); codeBlockChild != null; codeBlockChild = codeBlockChild.getNextSibling()) {
ASTNode childNode = codeBlockChild.getNode();
if (childNode == null) {
continue;
}
if (!lbraceFound) {
if (JavaTokenType.LBRACE == childNode.getElementType()) {
lbraceFound = true;
}
continue;
}
if (JavaJspElementType.WHITE_SPACE_BIT_SET.contains(childNode.getElementType())) {
codeBlockWsElement = codeBlockChild;
codeBlockWsNode = childNode;
break;
}
else if (JavaTokenType.RBRACE == childNode.getElementType()) {
break;
}
}
if (codeBlockWsElement != null) {
CharSequence existingWhiteSpaceText = codeBlockWsNode.getChars();
int existingWhiteSpaceEndOffset = existingWhiteSpaceText.length();
for (int i = existingWhiteSpaceEndOffset - 1; i >= 0; i--) {
if (existingWhiteSpaceText.charAt(i) == '\n') {
existingWhiteSpaceEndOffset = i;
break;
}
}
String newWsText = text.subSequence(text.length() - indent, text.length()).toString();
// Add white spaces from all lines except the last one.
if (existingWhiteSpaceEndOffset < existingWhiteSpaceText.length()) {
newWsText = existingWhiteSpaceText.subSequence(0, existingWhiteSpaceEndOffset + 1).toString() + newWsText;
}
PsiElement indentElement = parserFacade.createWhiteSpaceFromText(newWsText);
codeBlockWsElement.replace(indentElement);
}
else {
PsiElement indentElement = parserFacade.createWhiteSpaceFromText(text.subSequence(text.length() - indent, text.length()).toString());
container.add(indentElement);
}
}
}