blob: 4adad419e8287fb49ed4d9c7503aa8256de35c83 [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.psi.impl.source.codeStyle;
import com.intellij.lang.java.JavaLanguage;
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.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
import com.intellij.psi.jsp.JavaJspRecursiveElementVisitor;
import com.intellij.psi.jsp.JspFile;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
/**
* @author max
*/
public class BraceEnforcer extends JavaJspRecursiveElementVisitor {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.codeStyle.BraceEnforcer");
private final PostFormatProcessorHelper myPostProcessor;
public BraceEnforcer(CodeStyleSettings settings) {
myPostProcessor = new PostFormatProcessorHelper(settings.getCommonSettings(JavaLanguage.INSTANCE));
}
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
visitElement(expression);
}
@Override public void visitIfStatement(PsiIfStatement statement) {
if (checkElementContainsRange(statement)) {
final SmartPsiElementPointer pointer = SmartPointerManager.getInstance(statement.getProject()).createSmartPsiElementPointer(statement);
super.visitIfStatement(statement);
statement = (PsiIfStatement)pointer.getElement();
if (statement == null) {
return;
}
processStatement(statement, statement.getThenBranch(), myPostProcessor.getSettings().IF_BRACE_FORCE);
final PsiStatement elseBranch = statement.getElseBranch();
if (!(elseBranch instanceof PsiIfStatement) || !myPostProcessor.getSettings().SPECIAL_ELSE_IF_TREATMENT) {
processStatement(statement, elseBranch, myPostProcessor.getSettings().IF_BRACE_FORCE);
}
}
}
@Override public void visitForStatement(PsiForStatement statement) {
if (checkElementContainsRange(statement)) {
super.visitForStatement(statement);
processStatement(statement, statement.getBody(), myPostProcessor.getSettings().FOR_BRACE_FORCE);
}
}
@Override public void visitForeachStatement(PsiForeachStatement statement) {
if (checkElementContainsRange(statement)) {
super.visitForeachStatement(statement);
processStatement(statement, statement.getBody(), myPostProcessor.getSettings().FOR_BRACE_FORCE);
}
}
@Override public void visitWhileStatement(PsiWhileStatement statement) {
if (checkElementContainsRange(statement)) {
super.visitWhileStatement(statement);
processStatement(statement, statement.getBody(), myPostProcessor.getSettings().WHILE_BRACE_FORCE);
}
}
@Override public void visitDoWhileStatement(PsiDoWhileStatement statement) {
if (checkElementContainsRange(statement)) {
super.visitDoWhileStatement(statement);
processStatement(statement, statement.getBody(), myPostProcessor.getSettings().DOWHILE_BRACE_FORCE);
}
}
@Override public void visitJspFile(JspFile file) {
final PsiClass javaRoot = file.getJavaClass();
if (javaRoot != null) {
javaRoot.accept(this);
}
}
private void processStatement(PsiStatement statement, PsiStatement blockCandidate, int options) {
if (blockCandidate instanceof PsiBlockStatement || blockCandidate == null) return;
if (options == CommonCodeStyleSettings.FORCE_BRACES_ALWAYS
|| (options == CommonCodeStyleSettings.FORCE_BRACES_IF_MULTILINE && PostFormatProcessorHelper.isMultiline(statement)))
{
replaceWithBlock(statement, blockCandidate);
}
}
private void replaceWithBlock(@NotNull PsiStatement statement, PsiStatement blockCandidate) {
if (!statement.isValid()) {
LOG.assertTrue(false);
}
if (!checkRangeContainsElement(blockCandidate)) return;
final PsiManager manager = statement.getManager();
LOG.assertTrue(manager != null);
final PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
String oldText = blockCandidate.getText();
// There is a possible case that target block to wrap ends with single-line comment. Example:
// if (true) i = 1; // Cool assignment
// We can't just surround target block of code with curly braces because the closing one will be treated as comment as well.
// Hence, we perform a check if we have such situation at the moment and insert new line before the closing brace.
int lastLineFeedIndex = oldText.lastIndexOf("\n");
lastLineFeedIndex = Math.max(0, lastLineFeedIndex);
int lastLineCommentIndex = oldText.indexOf("//", lastLineFeedIndex);
StringBuilder buf = new StringBuilder(oldText.length() + 5);
buf.append("{ ").append(oldText);
if (lastLineCommentIndex >= 0) {
buf.append("\n");
}
buf.append(" }");
final int oldTextLength = statement.getTextLength();
try {
CodeEditUtil.replaceChild(SourceTreeToPsiMap.psiElementToTree(statement),
SourceTreeToPsiMap.psiElementToTree(blockCandidate),
SourceTreeToPsiMap.psiElementToTree(factory.createStatementFromText(buf.toString(), null)));
CodeStyleManager.getInstance(statement.getProject()).reformat(statement, true);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
finally {
updateResultRange(oldTextLength , statement.getTextLength());
}
}
protected void updateResultRange(final int oldTextLength, final int newTextLength) {
myPostProcessor.updateResultRange(oldTextLength, newTextLength);
}
protected boolean checkElementContainsRange(final PsiElement element) {
return myPostProcessor.isElementPartlyInRange(element);
}
protected boolean checkRangeContainsElement(final PsiElement element) {
return myPostProcessor.isElementFullyInRange(element);
}
public PsiElement process(PsiElement formatted) {
LOG.assertTrue(formatted.isValid());
formatted.accept(this);
return formatted;
}
public TextRange processText(final PsiFile source, final TextRange rangeToReformat) {
myPostProcessor.setResultTextRange(rangeToReformat);
source.accept(this);
return myPostProcessor.getResultTextRange();
}
}