update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / psi / impl / source / PsiFieldImpl.java
blob5934af0584cf57b8696c81e9a24b6eecad7f8807
1 /*
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;
44 import javax.swing.*;
45 import java.util.HashSet;
46 import java.util.List;
47 import java.util.Set;
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) {
60 super(node);
63 public void subtreeChanged() {
64 super.subtreeChanged();
65 dropCached();
68 private void dropCached() {
69 myCachedInitializerValue = null;
70 myCachedType = null;
73 protected Object clone() {
74 PsiFieldImpl clone = (PsiFieldImpl)super.clone();
75 clone.dropCached();
76 return clone;
79 public PsiClass getContainingClass() {
80 PsiElement parent = getParent();
81 return parent instanceof PsiClass ? (PsiClass)parent : PsiTreeUtil.getParentOfType(this, JspClass.class);
84 @Override
85 public PsiElement getContext() {
86 final PsiClass cc = getContainingClass();
87 return cc != null ? cc : super.getContext();
90 @NotNull
91 public CompositeElement getNode() {
92 return (CompositeElement)super.getNode();
95 @NotNull
96 public final PsiIdentifier getNameIdentifier(){
97 return (PsiIdentifier)getNode().findChildByRoleAsPsiElement(ChildRole.NAME);
100 @NotNull
101 public String getName() {
102 final PsiFieldStub stub = getStub();
103 if (stub != null) {
104 return stub.getName();
106 return getNameIdentifier().getText();
109 public PsiElement setName(@NotNull String name) throws IncorrectOperationException{
110 PsiImplUtil.setName(getNameIdentifier(), name);
111 return this;
114 public PsiType getTypeNoResolve() {
115 final PsiFieldStub stub = getStub();
116 if (stub != null) {
117 String typeText = TypeInfo.createTypeText(stub.getType(false));
118 try {
119 final PsiType type = JavaPsiFacade.getInstance(getProject()).getParserFacade().createTypeFromText(typeText, this);
120 myCachedType = new PatchedSoftReference<PsiType>(type);
121 return type;
123 catch (IncorrectOperationException e) {
124 LOG.error(e);
125 return null;
129 PsiTypeElement typeElement = getTypeElement();
130 PsiIdentifier nameIdentifier = getNameIdentifier();
131 return JavaSharedImplUtil.getTypeNoResolve(typeElement, nameIdentifier, this);
134 @NotNull
135 public PsiType getType(){
136 final PsiFieldStub stub = getStub();
137 if (stub != null) {
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));
145 try {
146 final PsiType type = JavaPsiFacade.getInstance(getProject()).getParserFacade().createTypeFromText(typeText, this);
147 myCachedType = new PatchedSoftReference<PsiType>(type);
148 return type;
150 catch (IncorrectOperationException e) {
151 LOG.error(e);
152 return null;
156 myCachedType = null;
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);
169 @NotNull
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();
190 @Nullable
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();
203 if (stub != null) {
204 final List siblings = stub.getParentStub().getChildrenStubs();
205 final int idx = siblings.indexOf(stub);
206 assert idx >= 0;
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();
230 else {
231 return this;
235 public PsiExpression getInitializer() {
236 return (PsiExpression)getNode().findChildByRoleAsPsiElement(ChildRole.INITIALIZER);
239 public boolean hasInitializer() {
240 if (getStub() != null) {
241 try {
242 return getInitializerText() != null;
244 catch (InitializerTooLongException e) {
245 return true;
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;
279 else{
280 final PsiFieldStub stub = getStub();
281 if (stub == null) {
282 initializer = getInitializer();
283 if (initializer == null) return null;
285 else{
286 try{
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){
297 getNode();
298 return computeConstantValue(visitedVars);
303 Object result = PsiConstantEvaluationHelperImpl.computeCastTo(initializer, type, visitedVars);
305 if (initializer instanceof PsiLiteralExpression){
306 myCachedInitializerValue = result;
308 else{
309 myCachedInitializerValue = initializer;
313 return result;
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);
331 @Nullable
332 private String getInitializerText() throws InitializerTooLongException {
333 final PsiFieldStub stub = getStub();
334 if (stub != null) {
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();
343 if (stub != null) {
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);
355 else{
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());
370 while(true){
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());
387 field = nextField;
390 JavaSharedImplUtil.normalizeBrackets(this);
393 public void accept(@NotNull PsiElementVisitor visitor){
394 if (visitor instanceof JavaElementVisitor) {
395 ((JavaElementVisitor)visitor).visitField(this);
397 else {
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);
404 return true;
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);
425 @Override
426 public boolean isEquivalentTo(final PsiElement another) {
427 return PsiClassImplUtil.isFieldEquivalentTo(this, another);
430 @NotNull
431 public SearchScope getUseScope() {
432 return PsiImplUtil.getMemberUseScope(this);