blob: f8bc8e425b9c21015e617527d3367f9f1e15f4a2 [file] [log] [blame]
/*
* Copyright 2000-2013 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.tree.java;
import com.intellij.lang.ASTNode;
import com.intellij.navigation.ItemPresentation;
import com.intellij.navigation.ItemPresentationProviders;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.impl.*;
import com.intellij.psi.impl.source.Constants;
import com.intellij.psi.impl.source.JavaDummyHolder;
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.psi.impl.source.tree.*;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.tree.ChildRoleBase;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.ui.RowIcon;
import com.intellij.util.CharTable;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.PlatformIcons;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.Arrays;
import java.util.Set;
public class PsiLocalVariableImpl extends CompositePsiElement implements PsiLocalVariable, PsiVariableEx, Constants {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiLocalVariableImpl");
private volatile String myCachedName = null;
@SuppressWarnings({"UnusedDeclaration"})
public PsiLocalVariableImpl() {
this(LOCAL_VARIABLE);
}
protected PsiLocalVariableImpl(final IElementType type) {
super(type);
}
@Override
public void clearCaches() {
super.clearCaches();
myCachedName = null;
}
@Override
@NotNull
public final PsiIdentifier getNameIdentifier() {
final PsiElement element = findChildByRoleAsPsiElement(ChildRole.NAME);
assert element instanceof PsiIdentifier : getText();
return (PsiIdentifier)element;
}
@Override
@NotNull
public final String getName() {
String cachedName = myCachedName;
if (cachedName == null){
myCachedName = cachedName = getNameIdentifier().getText();
}
return cachedName;
}
@Override
public void setInitializer(PsiExpression initializer) throws IncorrectOperationException {
JavaSharedImplUtil.setInitializer(this, initializer);
}
@Override
public PsiElement setName(@NotNull String name) throws IncorrectOperationException {
PsiImplUtil.setName(getNameIdentifier(), name);
return this;
}
@Override
@NotNull
public final PsiType getType() {
return JavaSharedImplUtil.getType(getTypeElement(), getNameIdentifier());
}
@Override
@NotNull
public PsiTypeElement getTypeElement() {
PsiTypeElement typeElement = PsiTreeUtil.getChildOfType(this, PsiTypeElement.class);
if (typeElement != null) return typeElement;
final PsiElement parent = getParent();
assert parent != null : "no parent; " + this + "; [" + getText() + "]";
final PsiLocalVariable localVariable = PsiTreeUtil.getChildOfType(parent, PsiLocalVariable.class);
assert localVariable != null : "no local variable in " + Arrays.toString(parent.getChildren());
typeElement = PsiTreeUtil.getChildOfType(localVariable, PsiTypeElement.class);
assert typeElement != null : "no type element in " + Arrays.toString(localVariable.getChildren());
return typeElement;
}
@Override
public PsiModifierList getModifierList() {
final CompositeElement parent = getTreeParent();
if (parent == null) return null;
final CompositeElement first = (CompositeElement)parent.findChildByType(LOCAL_VARIABLE);
return first != null ? (PsiModifierList)first.findChildByRoleAsPsiElement(ChildRole.MODIFIER_LIST) : null;
}
@Override
public boolean hasModifierProperty(@NotNull String name) {
final PsiModifierList modifierList = getModifierList();
return modifierList != null && modifierList.hasModifierProperty(name);
}
@Override
public PsiExpression getInitializer() {
return (PsiExpression)findChildByRoleAsPsiElement(ChildRole.INITIALIZER);
}
@Override
public boolean hasInitializer() {
return getInitializer() != null;
}
@Override
public Object computeConstantValue() {
return computeConstantValue(new THashSet<PsiVariable>());
}
@Override
public Object computeConstantValue(Set<PsiVariable> visitedVars) {
if (!hasModifierProperty(PsiModifier.FINAL)) return null;
PsiType type = getType();
// javac rejects all non primitive and non String constants, although JLS states constants "variables whose initializers are constant expressions"
if (!(type instanceof PsiPrimitiveType) && !type.equalsToText("java.lang.String")) return null;
PsiExpression initializer = getInitializer();
if (initializer == null) return null;
return PsiConstantEvaluationHelperImpl.computeCastTo(initializer, getType(), visitedVars);
}
@Override
public int getTextOffset() {
return getNameIdentifier().getTextOffset();
}
@Override
public void normalizeDeclaration() throws IncorrectOperationException {
CheckUtil.checkWritable(this);
final CharTable treeCharTab = SharedImplUtil.findCharTableByTree(this);
final CompositeElement statement = getTreeParent();
final PsiElement psiElement = SourceTreeToPsiMap.treeElementToPsi(statement);
final PsiElement[] variables = psiElement instanceof PsiDeclarationStatement
? ((PsiDeclarationStatement)psiElement).getDeclaredElements() : PsiElement.EMPTY_ARRAY;
if (variables.length > 1) {
final PsiModifierList modifierList = getModifierList();
final PsiTypeElement typeElement = getTypeElement();
assert modifierList != null : getText();
ASTNode last = statement;
for (int i = 1; i < variables.length; i++) {
ASTNode typeCopy = typeElement.copy().getNode();
ASTNode modifierListCopy = modifierList.copy().getNode();
CompositeElement variable = (CompositeElement)SourceTreeToPsiMap.psiToTreeNotNull(variables[i]);
ASTNode comma = PsiImplUtil.skipWhitespaceAndCommentsBack(variable.getTreePrev());
if (comma != null && comma.getElementType() == JavaTokenType.COMMA) {
CodeEditUtil.removeChildren(statement, comma, variable.getTreePrev());
}
CodeEditUtil.removeChild(statement, variable);
final CharTable charTableByTree = SharedImplUtil.findCharTableByTree(statement);
CompositeElement statement1 = Factory.createCompositeElement(DECLARATION_STATEMENT, charTableByTree, getManager());
statement1.addChild(variable, null);
ASTNode space = Factory.createSingleLeafElement(TokenType.WHITE_SPACE, " ", 0, 1, treeCharTab, getManager());
variable.addChild(space, variable.getFirstChildNode());
variable.addChild(typeCopy, variable.getFirstChildNode());
if (modifierListCopy.getTextLength() > 0) {
space = Factory.createSingleLeafElement(TokenType.WHITE_SPACE, " ", 0, 1, treeCharTab, getManager());
variable.addChild(space, variable.getFirstChildNode());
}
variable.addChild(modifierListCopy, variable.getFirstChildNode());
ASTNode semicolon = Factory.createSingleLeafElement(JavaTokenType.SEMICOLON, ";", 0, 1, treeCharTab, getManager());
SourceTreeToPsiMap.psiToTreeNotNull(variables[i - 1]).addChild(semicolon, null);
CodeEditUtil.addChild(statement.getTreeParent(), statement1, last.getTreeNext());
last = statement1;
}
}
JavaSharedImplUtil.normalizeBrackets(this);
}
@Override
public void deleteChildInternal(@NotNull ASTNode child) {
if (getChildRole(child) == ChildRole.INITIALIZER){
ASTNode eq = findChildByRole(ChildRole.INITIALIZER_EQ);
if (eq != null){
deleteChildInternal(eq);
}
}
super.deleteChildInternal(child);
}
@Override
public ASTNode findChildByRole(int role) {
LOG.assertTrue(ChildRole.isUnique(role));
switch(role){
default:
return null;
case ChildRole.MODIFIER_LIST:
return findChildByType(MODIFIER_LIST);
case ChildRole.TYPE:
return findChildByType(TYPE);
case ChildRole.NAME:
return findChildByType(JavaTokenType.IDENTIFIER);
case ChildRole.INITIALIZER_EQ:
return findChildByType(JavaTokenType.EQ);
case ChildRole.INITIALIZER:
return findChildByType(ElementType.EXPRESSION_BIT_SET);
case ChildRole.CLOSING_SEMICOLON:
return TreeUtil.findChildBackward(this, JavaTokenType.SEMICOLON);
}
}
@Override
public int getChildRole(ASTNode child) {
LOG.assertTrue(child.getTreeParent() == this);
IElementType i = child.getElementType();
if (i == MODIFIER_LIST) {
return ChildRole.MODIFIER_LIST;
}
else if (i == TYPE) {
return getChildRole(child, ChildRole.TYPE);
}
else if (i == JavaTokenType.IDENTIFIER) {
return getChildRole(child, ChildRole.NAME);
}
else if (i == JavaTokenType.EQ) {
return getChildRole(child, ChildRole.INITIALIZER_EQ);
}
else if (i == JavaTokenType.SEMICOLON) {
return getChildRole(child, ChildRole.CLOSING_SEMICOLON);
}
else {
if (ElementType.EXPRESSION_BIT_SET.contains(child.getElementType())) {
return ChildRole.INITIALIZER;
}
return ChildRoleBase.NONE;
}
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof JavaElementVisitor) {
((JavaElementVisitor)visitor).visitLocalVariable(this);
}
else {
visitor.visitElement(this);
}
}
@Override
public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place) {
if (lastParent == null) return true;
if (lastParent.getContext() instanceof JavaDummyHolder) {
return processor.execute(this, state);
}
if (lastParent.getParent() != this) return true;
final ASTNode lastParentTree = SourceTreeToPsiMap.psiElementToTree(lastParent);
return getChildRole(lastParentTree) != ChildRole.INITIALIZER ||
processor.execute(this, state);
}
@Override
public ItemPresentation getPresentation() {
return ItemPresentationProviders.getItemPresentation(this);
}
public String toString() {
return "PsiLocalVariable:" + getName();
}
@Override
@NotNull
public SearchScope getUseScope() {
final PsiElement parentElement = getParent();
if (parentElement instanceof PsiDeclarationStatement) {
return new LocalSearchScope(parentElement.getParent());
}
else {
return ResolveScopeManager.getElementUseScope(this);
}
}
@Override
public Icon getElementIcon(final int flags) {
final RowIcon baseIcon = ElementPresentationUtil.createLayeredIcon(PlatformIcons.VARIABLE_ICON, this, false);
return ElementPresentationUtil.addVisibilityIcon(this, flags, baseIcon);
}
@Override
protected boolean isVisibilitySupported() {
return true;
}
}