blob: fd38e3ddaf563a478818908c68796b9bdd29032d [file] [log] [blame]
/*
* Copyright 2000-2014 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;
import com.intellij.lang.ASTNode;
import com.intellij.navigation.ItemPresentation;
import com.intellij.navigation.ItemPresentationProviders;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.ui.Queryable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.*;
import com.intellij.psi.impl.cache.TypeInfo;
import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
import com.intellij.psi.impl.java.stubs.PsiFieldStub;
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.psi.impl.source.resolve.JavaResolveCache;
import com.intellij.psi.impl.source.tree.*;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.stubs.IStubElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.reference.SoftReference;
import com.intellij.ui.RowIcon;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.PlatformIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class PsiFieldImpl extends JavaStubPsiElement<PsiFieldStub> implements PsiField, PsiVariableEx, Queryable {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.PsiFieldImpl");
private volatile SoftReference<PsiType> myCachedType = null;
private volatile Object myCachedInitializerValue = null; // PsiExpression on constant value for literal
public PsiFieldImpl(final PsiFieldStub stub) {
this(stub, JavaStubElementTypes.FIELD);
}
protected PsiFieldImpl(final PsiFieldStub stub, final IStubElementType type) {
super(stub, type);
}
public PsiFieldImpl(final ASTNode node) {
super(node);
}
@Override
public void subtreeChanged() {
super.subtreeChanged();
dropCached();
}
private void dropCached() {
myCachedInitializerValue = null;
myCachedType = null;
}
@Override
protected Object clone() {
PsiFieldImpl clone = (PsiFieldImpl)super.clone();
clone.dropCached();
return clone;
}
@Override
public PsiClass getContainingClass() {
PsiElement parent = getParent();
return parent instanceof PsiClass ? (PsiClass)parent : PsiTreeUtil.getParentOfType(this, PsiSyntheticClass.class);
}
@Override
public PsiElement getContext() {
final PsiClass cc = getContainingClass();
return cc != null ? cc : super.getContext();
}
@Override
@NotNull
public CompositeElement getNode() {
return (CompositeElement)super.getNode();
}
@Override
@NotNull
public PsiIdentifier getNameIdentifier() {
return (PsiIdentifier)getNode().findChildByRoleAsPsiElement(ChildRole.NAME);
}
@Override
@NotNull
public String getName() {
final PsiFieldStub stub = getStub();
if (stub != null) {
return stub.getName();
}
return getNameIdentifier().getText();
}
@Override
public PsiElement setName(@NotNull String name) throws IncorrectOperationException{
PsiImplUtil.setName(getNameIdentifier(), name);
return this;
}
@Override
@NotNull
public PsiType getType() {
final PsiFieldStub stub = getStub();
if (stub != null) {
PsiType type = SoftReference.dereference(myCachedType);
if (type != null) return type;
String typeText = TypeInfo.createTypeText(stub.getType(true));
try {
type = JavaPsiFacade.getInstance(getProject()).getParserFacade().createTypeFromText(typeText, this);
myCachedType = new SoftReference<PsiType>(type);
return type;
}
catch (IncorrectOperationException e) {
LOG.error(e);
return null;
}
}
myCachedType = null;
return JavaSharedImplUtil.getType(getTypeElement(), getNameIdentifier());
}
@Override
public PsiTypeElement getTypeElement(){
PsiField firstField = findFirstFieldInDeclaration();
if (firstField != this){
return firstField.getTypeElement();
}
return (PsiTypeElement)getNode().findChildByRoleAsPsiElement(ChildRole.TYPE);
}
@Override
@NotNull
public PsiModifierList getModifierList() {
final PsiModifierList selfModifierList = getSelfModifierList();
if (selfModifierList != null) {
return selfModifierList;
}
PsiField firstField = findFirstFieldInDeclaration();
if (firstField == this) {
if (!isValid()) throw new PsiInvalidElementAccessException(this);
final PsiField lastResort = findFirstFieldByTree();
if (lastResort == this) {
throw new IllegalStateException("Missing modifier list for sequence of fields: '" + getText() + "'");
}
firstField = lastResort;
}
return firstField.getModifierList();
}
@Nullable
private PsiModifierList getSelfModifierList() {
return getStubOrPsiChild(JavaStubElementTypes.MODIFIER_LIST);
}
@Override
public boolean hasModifierProperty(@NotNull String name) {
return getModifierList().hasModifierProperty(name);
}
private PsiField findFirstFieldInDeclaration() {
if (getSelfModifierList() != null) return this;
final PsiFieldStub stub = getStub();
if (stub != null) {
final List siblings = stub.getParentStub().getChildrenStubs();
final int idx = siblings.indexOf(stub);
assert idx >= 0;
for (int i = idx - 1; i >= 0; i--) {
if (!(siblings.get(i) instanceof PsiFieldStub)) break;
PsiFieldStub prevField = (PsiFieldStub)siblings.get(i);
final PsiFieldImpl prevFieldPsi = (PsiFieldImpl)prevField.getPsi();
if (prevFieldPsi.getSelfModifierList() != null) return prevFieldPsi;
}
}
return findFirstFieldByTree();
}
private PsiField findFirstFieldByTree() {
CompositeElement treeElement = getNode();
ASTNode modifierList = treeElement.findChildByRole(ChildRole.MODIFIER_LIST);
if (modifierList == null) {
ASTNode prevField = treeElement.getTreePrev();
while (prevField != null && prevField.getElementType() != JavaElementType.FIELD) {
prevField = prevField.getTreePrev();
}
if (prevField == null) return this;
return ((PsiFieldImpl)SourceTreeToPsiMap.treeElementToPsi(prevField)).findFirstFieldInDeclaration();
}
else {
return this;
}
}
@Override
public PsiExpression getInitializer() {
return (PsiExpression)getNode().findChildByRoleAsPsiElement(ChildRole.INITIALIZER);
}
@Override
public boolean hasInitializer() {
PsiFieldStub stub = getStub();
if (stub != null) {
return stub.getInitializerText() != null;
}
return getInitializer() != null;
}
@Override
public Icon getElementIcon(final int flags) {
final RowIcon baseIcon = ElementPresentationUtil.createLayeredIcon(PlatformIcons.FIELD_ICON, this, false);
return ElementPresentationUtil.addVisibilityIcon(this, flags, baseIcon);
}
private static class OurConstValueComputer implements JavaResolveCache.ConstValueComputer {
private static final OurConstValueComputer INSTANCE = new OurConstValueComputer();
@Override
public Object execute(@NotNull PsiVariable variable, Set<PsiVariable> visitedVars) {
return ((PsiFieldImpl)variable)._computeConstantValue(visitedVars);
}
}
@Nullable
private Object _computeConstantValue(Set<PsiVariable> visitedVars) {
Object cachedInitializerValue = myCachedInitializerValue;
if (cachedInitializerValue != null && !(cachedInitializerValue instanceof PsiExpression)){
return cachedInitializerValue;
}
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;
if (cachedInitializerValue != null) {
initializer = (PsiExpression)cachedInitializerValue;
}
else{
final PsiFieldStub stub = getStub();
if (stub == null) {
initializer = getInitializer();
if (initializer == null) return null;
}
else{
String initializerText = stub.getInitializerText();
if (StringUtil.isEmpty(initializerText)) return null;
if (PsiFieldStub.INITIALIZER_NOT_STORED.equals(initializerText)) return null;
if (PsiFieldStub.INITIALIZER_TOO_LONG.equals(initializerText)) {
getNode();
return computeConstantValue(visitedVars);
}
final PsiJavaParserFacade parserFacade = JavaPsiFacade.getInstance(getProject()).getParserFacade();
initializer = parserFacade.createExpressionFromText(initializerText, this);
}
}
Object result = PsiConstantEvaluationHelperImpl.computeCastTo(initializer, type, visitedVars);
if (initializer instanceof PsiLiteralExpression){
myCachedInitializerValue = result;
}
else{
myCachedInitializerValue = initializer;
}
return result;
}
@Override
public Object computeConstantValue() {
Object cachedInitializerValue = myCachedInitializerValue;
if (cachedInitializerValue != null && !(cachedInitializerValue instanceof PsiExpression)){
return cachedInitializerValue;
}
return computeConstantValue(new HashSet<PsiVariable>(2));
}
@Override
public Object computeConstantValue(Set<PsiVariable> visitedVars) {
if (!hasModifierProperty(PsiModifier.FINAL)) return null;
return JavaResolveCache.getInstance(getProject()).computeConstantValueWithCaching(this, OurConstValueComputer.INSTANCE, visitedVars);
}
@Override
public boolean isDeprecated() {
final PsiFieldStub stub = getStub();
if (stub != null) {
return stub.isDeprecated() || stub.hasDeprecatedAnnotation() && PsiImplUtil.isDeprecatedByAnnotation(this);
}
return PsiImplUtil.isDeprecatedByDocTag(this) || PsiImplUtil.isDeprecatedByAnnotation(this);
}
@Override
public PsiDocComment getDocComment(){
CompositeElement treeElement = getNode();
if (getTypeElement() != null) {
PsiElement element = treeElement.findChildByRoleAsPsiElement(ChildRole.DOC_COMMENT);
return element instanceof PsiDocComment ? (PsiDocComment)element : null;
}
else {
ASTNode prevField = treeElement.getTreePrev();
while(prevField.getElementType() != JavaElementType.FIELD){
prevField = prevField.getTreePrev();
}
return ((PsiField)SourceTreeToPsiMap.treeElementToPsi(prevField)).getDocComment();
}
}
@Override
public void normalizeDeclaration() throws IncorrectOperationException{
CheckUtil.checkWritable(this);
final PsiTypeElement type = getTypeElement();
PsiElement modifierList = getModifierList();
ASTNode field = SourceTreeToPsiMap.psiElementToTree(type.getParent());
while(true){
ASTNode comma = PsiImplUtil.skipWhitespaceAndComments(field.getTreeNext());
if (comma == null || comma.getElementType() != JavaTokenType.COMMA) break;
ASTNode nextField = PsiImplUtil.skipWhitespaceAndComments(comma.getTreeNext());
if (nextField == null || nextField.getElementType() != JavaElementType.FIELD) break;
TreeElement semicolon = Factory.createSingleLeafElement(JavaTokenType.SEMICOLON, ";", 0, 1, null, getManager());
CodeEditUtil.addChild(field, semicolon, null);
CodeEditUtil.removeChild(comma.getTreeParent(), comma);
PsiElement typeClone = type.copy();
CodeEditUtil.addChild(nextField, SourceTreeToPsiMap.psiElementToTree(typeClone), nextField.getFirstChildNode());
PsiElement modifierListClone = modifierList.copy();
CodeEditUtil.addChild(nextField, SourceTreeToPsiMap.psiElementToTree(modifierListClone), nextField.getFirstChildNode());
field = nextField;
}
JavaSharedImplUtil.normalizeBrackets(this);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor){
if (visitor instanceof JavaElementVisitor) {
((JavaElementVisitor)visitor).visitField(this);
}
else {
visitor.visitElement(this);
}
}
@Override
public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place){
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, this);
return true;
}
public String toString(){
return "PsiField:" + getName();
}
@Override
public PsiElement getOriginalElement() {
PsiClass containingClass = getContainingClass();
if (containingClass != null) {
PsiField originalField = ((PsiClass)containingClass.getOriginalElement()).findFieldByName(getName(), false);
if (originalField != null) {
return originalField;
}
}
return this;
}
@Override
public ItemPresentation getPresentation() {
return ItemPresentationProviders.getItemPresentation(this);
}
@Override
public void setInitializer(PsiExpression initializer) throws IncorrectOperationException {
JavaSharedImplUtil.setInitializer(this, initializer);
}
@Override
public boolean isEquivalentTo(final PsiElement another) {
return PsiClassImplUtil.isFieldEquivalentTo(this, another);
}
@Override
@NotNull
public SearchScope getUseScope() {
return PsiImplUtil.getMemberUseScope(this);
}
@Override
public void putInfo(@NotNull Map<String, String> info) {
info.put("fieldName", getName());
}
@Override
protected boolean isVisibilitySupported() {
return true;
}
}