2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.psi
.impl
.source
;
18 import com
.intellij
.lang
.ASTNode
;
19 import com
.intellij
.navigation
.ItemPresentation
;
20 import com
.intellij
.openapi
.diagnostic
.Logger
;
21 import com
.intellij
.psi
.*;
22 import com
.intellij
.psi
.impl
.*;
23 import com
.intellij
.psi
.impl
.cache
.InitializerTooLongException
;
24 import com
.intellij
.psi
.impl
.cache
.TypeInfo
;
25 import com
.intellij
.psi
.impl
.java
.stubs
.JavaStubElementTypes
;
26 import com
.intellij
.psi
.impl
.java
.stubs
.PsiFieldStub
;
27 import com
.intellij
.psi
.impl
.source
.codeStyle
.CodeEditUtil
;
28 import com
.intellij
.psi
.impl
.source
.jsp
.jspJava
.JspClass
;
29 import com
.intellij
.psi
.impl
.source
.parsing
.ExpressionParsing
;
30 import com
.intellij
.psi
.impl
.source
.resolve
.JavaResolveCache
;
31 import com
.intellij
.psi
.impl
.source
.tree
.*;
32 import com
.intellij
.psi
.javadoc
.PsiDocComment
;
33 import com
.intellij
.psi
.presentation
.java
.JavaPresentationUtil
;
34 import com
.intellij
.psi
.scope
.PsiScopeProcessor
;
35 import com
.intellij
.psi
.search
.SearchScope
;
36 import com
.intellij
.psi
.util
.PsiTreeUtil
;
37 import com
.intellij
.ui
.RowIcon
;
38 import com
.intellij
.util
.Icons
;
39 import com
.intellij
.util
.IncorrectOperationException
;
40 import com
.intellij
.util
.PatchedSoftReference
;
41 import org
.jetbrains
.annotations
.NotNull
;
42 import org
.jetbrains
.annotations
.Nullable
;
45 import java
.util
.HashSet
;
46 import java
.util
.List
;
49 public class PsiFieldImpl
extends JavaStubPsiElement
<PsiFieldStub
> implements PsiField
, PsiVariableEx
{
50 private static final Logger LOG
= Logger
.getInstance("#com.intellij.psi.impl.source.PsiFieldImpl");
52 private volatile PatchedSoftReference
<PsiType
> myCachedType
= null;
53 private volatile Object myCachedInitializerValue
= null; // PsiExpression on constant value for literal
55 public PsiFieldImpl(final PsiFieldStub stub
) {
56 super(stub
, JavaStubElementTypes
.FIELD
);
59 public PsiFieldImpl(final ASTNode node
) {
63 public void subtreeChanged() {
64 super.subtreeChanged();
68 private void dropCached() {
69 myCachedInitializerValue
= null;
73 protected Object
clone() {
74 PsiFieldImpl clone
= (PsiFieldImpl
)super.clone();
79 public PsiClass
getContainingClass() {
80 PsiElement parent
= getParent();
81 return parent
instanceof PsiClass ?
(PsiClass
)parent
: PsiTreeUtil
.getParentOfType(this, JspClass
.class);
85 public PsiElement
getContext() {
86 final PsiClass cc
= getContainingClass();
87 return cc
!= null ? cc
: super.getContext();
91 public CompositeElement
getNode() {
92 return (CompositeElement
)super.getNode();
96 public final PsiIdentifier
getNameIdentifier(){
97 return (PsiIdentifier
)getNode().findChildByRoleAsPsiElement(ChildRole
.NAME
);
101 public String
getName() {
102 final PsiFieldStub stub
= getStub();
104 return stub
.getName();
106 return getNameIdentifier().getText();
109 public PsiElement
setName(@NotNull String name
) throws IncorrectOperationException
{
110 PsiImplUtil
.setName(getNameIdentifier(), name
);
114 public PsiType
getTypeNoResolve() {
115 final PsiFieldStub stub
= getStub();
117 String typeText
= TypeInfo
.createTypeText(stub
.getType(false));
119 final PsiType type
= JavaPsiFacade
.getInstance(getProject()).getParserFacade().createTypeFromText(typeText
, this);
120 myCachedType
= new PatchedSoftReference
<PsiType
>(type
);
123 catch (IncorrectOperationException e
) {
129 PsiTypeElement typeElement
= getTypeElement();
130 PsiIdentifier nameIdentifier
= getNameIdentifier();
131 return JavaSharedImplUtil
.getTypeNoResolve(typeElement
, nameIdentifier
, this);
135 public PsiType
getType(){
136 final PsiFieldStub stub
= getStub();
138 PatchedSoftReference
<PsiType
> cachedType
= myCachedType
;
139 if (cachedType
!= null) {
140 PsiType type
= cachedType
.get();
141 if (type
!= null) return type
;
144 String typeText
= TypeInfo
.createTypeText(stub
.getType(true));
146 final PsiType type
= JavaPsiFacade
.getInstance(getProject()).getParserFacade().createTypeFromText(typeText
, this);
147 myCachedType
= new PatchedSoftReference
<PsiType
>(type
);
150 catch (IncorrectOperationException e
) {
157 return JavaSharedImplUtil
.getType(this);
160 public PsiTypeElement
getTypeElement(){
161 PsiField firstField
= findFirstFieldInDeclaration();
162 if (firstField
!= this){
163 return firstField
.getTypeElement();
166 return (PsiTypeElement
)getNode().findChildByRoleAsPsiElement(ChildRole
.TYPE
);
170 public PsiModifierList
getModifierList() {
171 final PsiModifierList selfModifierList
= getSelfModifierList();
172 if (selfModifierList
!= null) {
173 return selfModifierList
;
175 PsiField firstField
= findFirstFieldInDeclaration();
176 if (firstField
== this) {
177 if (!isValid()) throw new PsiInvalidElementAccessException(this);
179 final PsiField lastResort
= findFirstFieldByTree();
180 if (lastResort
== this) {
181 throw new IllegalStateException("Missing modifier list for sequence of fields: '" + getText() + "'");
184 firstField
= lastResort
;
187 return firstField
.getModifierList();
191 private PsiModifierList
getSelfModifierList() {
192 return getStubOrPsiChild(JavaStubElementTypes
.MODIFIER_LIST
);
195 public boolean hasModifierProperty(@NotNull String name
) {
196 return getModifierList().hasModifierProperty(name
);
199 private PsiField
findFirstFieldInDeclaration() {
200 if (getSelfModifierList() != null) return this;
202 final PsiFieldStub stub
= getStub();
204 final List siblings
= stub
.getParentStub().getChildrenStubs();
205 final int idx
= siblings
.indexOf(stub
);
207 for (int i
= idx
- 1; i
>= 0; i
--) {
208 if (!(siblings
.get(i
) instanceof PsiFieldStub
)) break;
209 PsiFieldStub prevField
= (PsiFieldStub
)siblings
.get(i
);
210 final PsiFieldImpl prevFieldPsi
= (PsiFieldImpl
)prevField
.getPsi();
211 if (prevFieldPsi
.getSelfModifierList() != null) return prevFieldPsi
;
215 return findFirstFieldByTree();
218 private PsiField
findFirstFieldByTree() {
219 CompositeElement treeElement
= getNode();
221 ASTNode modifierList
= treeElement
.findChildByRole(ChildRole
.MODIFIER_LIST
);
222 if (modifierList
== null) {
223 ASTNode prevField
= treeElement
.getTreePrev();
224 while (prevField
!= null && prevField
.getElementType() != JavaElementType
.FIELD
) {
225 prevField
= prevField
.getTreePrev();
227 if (prevField
== null) return this;
228 return ((PsiFieldImpl
)SourceTreeToPsiMap
.treeElementToPsi(prevField
)).findFirstFieldInDeclaration();
235 public PsiExpression
getInitializer() {
236 return (PsiExpression
)getNode().findChildByRoleAsPsiElement(ChildRole
.INITIALIZER
);
239 public boolean hasInitializer() {
240 if (getStub() != null) {
242 return getInitializerText() != null;
244 catch (InitializerTooLongException e
) {
249 return getInitializer() != null;
252 public Icon
getElementIcon(final int flags
) {
253 final RowIcon baseIcon
= createLayeredIcon(Icons
.FIELD_ICON
, ElementPresentationUtil
.getFlags(this, false));
254 return ElementPresentationUtil
.addVisibilityIcon(this, flags
, baseIcon
);
257 private static class OurConstValueComputer
implements JavaResolveCache
.ConstValueComputer
{
258 private static final OurConstValueComputer INSTANCE
= new OurConstValueComputer();
260 public Object
execute(PsiVariable variable
, Set
<PsiVariable
> visitedVars
) {
261 return ((PsiFieldImpl
)variable
)._computeConstantValue(visitedVars
);
265 private Object
_computeConstantValue(Set
<PsiVariable
> visitedVars
) {
266 Object cachedInitializerValue
= myCachedInitializerValue
;
267 if (cachedInitializerValue
!= null && !(cachedInitializerValue
instanceof PsiExpression
)){
268 return cachedInitializerValue
;
271 PsiType type
= getType();
272 // javac rejects all non primitive and non String constants, although JLS states constants "variables whose initializers are constant expressions"
273 if (!(type
instanceof PsiPrimitiveType
) && !type
.equalsToText("java.lang.String")) return null;
275 PsiExpression initializer
;
276 if (cachedInitializerValue
!= null) {
277 initializer
= (PsiExpression
)cachedInitializerValue
;
280 final PsiFieldStub stub
= getStub();
282 initializer
= getInitializer();
283 if (initializer
== null) return null;
287 String initializerText
= getInitializerText();
288 if (initializerText
== null) return null;
290 PsiManager manager
= getManager();
291 final FileElement holderElement
= DummyHolderFactory
.createHolder(manager
, this).getTreeElement();
292 CompositeElement exprElement
= ExpressionParsing
.parseExpressionText(manager
, initializerText
, 0, initializerText
.length(), holderElement
.getCharTable());
293 holderElement
.rawAddChildren(exprElement
);
294 initializer
= (PsiExpression
)SourceTreeToPsiMap
.treeElementToPsi(exprElement
);
296 catch(InitializerTooLongException e
){
298 return computeConstantValue(visitedVars
);
303 Object result
= PsiConstantEvaluationHelperImpl
.computeCastTo(initializer
, type
, visitedVars
);
305 if (initializer
instanceof PsiLiteralExpression
){
306 myCachedInitializerValue
= result
;
309 myCachedInitializerValue
= initializer
;
316 public Object
computeConstantValue() {
317 Object cachedInitializerValue
= myCachedInitializerValue
;
318 if (cachedInitializerValue
!= null && !(cachedInitializerValue
instanceof PsiExpression
)){
319 return cachedInitializerValue
;
322 return computeConstantValue(new HashSet
<PsiVariable
>(2));
325 public Object
computeConstantValue(Set
<PsiVariable
> visitedVars
) {
326 if (!hasModifierProperty(PsiModifier
.FINAL
)) return null;
328 return JavaResolveCache
.getInstance(getProject()).computeConstantValueWithCaching(this, OurConstValueComputer
.INSTANCE
, visitedVars
);
332 private String
getInitializerText() throws InitializerTooLongException
{
333 final PsiFieldStub stub
= getStub();
335 return stub
.getInitializerText();
338 throw new RuntimeException("Shall not be called when in stubless mode");
341 public boolean isDeprecated() {
342 final PsiFieldStub stub
= getStub();
344 return stub
.isDeprecated() || stub
.hasDeprecatedAnnotation() && PsiImplUtil
.isDeprecatedByAnnotation(this);
347 return PsiImplUtil
.isDeprecatedByDocTag(this) || PsiImplUtil
.isDeprecatedByAnnotation(this);
350 public PsiDocComment
getDocComment(){
351 CompositeElement treeElement
= getNode();
352 if (getTypeElement() != null) {
353 return (PsiDocComment
)treeElement
.findChildByRoleAsPsiElement(ChildRole
.DOC_COMMENT
);
356 ASTNode prevField
= treeElement
.getTreePrev();
357 while(prevField
.getElementType() != JavaElementType
.FIELD
){
358 prevField
= prevField
.getTreePrev();
360 return ((PsiField
)SourceTreeToPsiMap
.treeElementToPsi(prevField
)).getDocComment();
364 public void normalizeDeclaration() throws IncorrectOperationException
{
365 CheckUtil
.checkWritable(this);
367 final PsiTypeElement type
= getTypeElement();
368 PsiElement modifierList
= getModifierList();
369 ASTNode field
= SourceTreeToPsiMap
.psiElementToTree(type
.getParent());
371 ASTNode comma
= TreeUtil
.skipElements(field
.getTreeNext(), StdTokenSets
.WHITE_SPACE_OR_COMMENT_BIT_SET
);
372 if (comma
== null || comma
.getElementType() != JavaTokenType
.COMMA
) break;
373 ASTNode nextField
= TreeUtil
.skipElements(comma
.getTreeNext(), StdTokenSets
.WHITE_SPACE_OR_COMMENT_BIT_SET
);
374 if (nextField
== null || nextField
.getElementType() != JavaElementType
.FIELD
) break;
376 TreeElement semicolon
= Factory
.createSingleLeafElement(JavaTokenType
.SEMICOLON
, ";", 0, 1, null, getManager());
377 CodeEditUtil
.addChild((CompositeElement
)field
, semicolon
, null);
379 CodeEditUtil
.removeChild((CompositeElement
)comma
.getTreeParent(), comma
);
381 PsiElement typeClone
= type
.copy();
382 CodeEditUtil
.addChild((CompositeElement
)nextField
, SourceTreeToPsiMap
.psiElementToTree(typeClone
), nextField
.getFirstChildNode());
384 PsiElement modifierListClone
= modifierList
.copy();
385 CodeEditUtil
.addChild((CompositeElement
)nextField
, SourceTreeToPsiMap
.psiElementToTree(modifierListClone
), nextField
.getFirstChildNode());
390 JavaSharedImplUtil
.normalizeBrackets(this);
393 public void accept(@NotNull PsiElementVisitor visitor
){
394 if (visitor
instanceof JavaElementVisitor
) {
395 ((JavaElementVisitor
)visitor
).visitField(this);
398 visitor
.visitElement(this);
402 public boolean processDeclarations(@NotNull PsiScopeProcessor processor
, @NotNull ResolveState state
, PsiElement lastParent
, @NotNull PsiElement place
){
403 processor
.handleEvent(PsiScopeProcessor
.Event
.SET_DECLARATION_HOLDER
, this);
407 public String
toString(){
408 return "PsiField:" + getName();
411 public PsiElement
getOriginalElement() {
412 PsiClass originalClass
= (PsiClass
)getContainingClass().getOriginalElement();
413 PsiField originalField
= originalClass
.findFieldByName(getName(), false);
414 return originalField
!= null ? originalField
: this;
417 public ItemPresentation
getPresentation() {
418 return JavaPresentationUtil
.getFieldPresentation(this);
421 public void setInitializer(PsiExpression initializer
) throws IncorrectOperationException
{
422 JavaSharedImplUtil
.setInitializer(this, initializer
);
426 public boolean isEquivalentTo(final PsiElement another
) {
427 return PsiClassImplUtil
.isFieldEquivalentTo(this, another
);
431 public SearchScope
getUseScope() {
432 return PsiImplUtil
.getMemberUseScope(this);