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.
18 * Created by IntelliJ IDEA.
22 * To change template for new class use
23 * Code Style | Class Templates options (Tools | IDE Options).
25 package com
.intellij
.codeInspection
.reference
;
27 import com
.intellij
.codeInsight
.TestUtil
;
28 import com
.intellij
.openapi
.application
.ApplicationManager
;
29 import com
.intellij
.openapi
.module
.Module
;
30 import com
.intellij
.openapi
.module
.ModuleUtil
;
31 import com
.intellij
.psi
.*;
32 import com
.intellij
.psi
.util
.ClassUtil
;
33 import com
.intellij
.psi
.util
.PsiFormatUtil
;
34 import gnu
.trove
.THashSet
;
35 import org
.jetbrains
.annotations
.NotNull
;
36 import org
.jetbrains
.annotations
.Nullable
;
38 import java
.util
.ArrayList
;
39 import java
.util
.HashSet
;
40 import java
.util
.List
;
43 public class RefClassImpl
extends RefJavaElementImpl
implements RefClass
{
44 private static final HashSet
<RefElement
> EMPTY_SET
= new HashSet
<RefElement
>(0);
45 private static final HashSet
<RefClass
> EMPTY_CLASS_SET
= new HashSet
<RefClass
>(0);
46 private static final ArrayList
<RefMethod
> EMPTY_METHOD_LIST
= new ArrayList
<RefMethod
>(0);
47 private static final int IS_ANONYMOUS_MASK
= 0x10000;
48 private static final int IS_INTERFACE_MASK
= 0x20000;
49 private static final int IS_UTILITY_MASK
= 0x40000;
50 private static final int IS_ABSTRACT_MASK
= 0x80000;
52 private static final int IS_APPLET_MASK
= 0x200000;
53 private static final int IS_SERVLET_MASK
= 0x400000;
54 private static final int IS_TESTCASE_MASK
= 0x800000;
55 private static final int IS_LOCAL_MASK
= 0x1000000;
57 private HashSet
<RefClass
> myBases
;
58 private HashSet
<RefClass
> mySubClasses
;
59 private ArrayList
<RefMethod
> myConstructors
;
60 private RefMethodImpl myDefaultConstructor
;
61 private ArrayList
<RefMethod
> myOverridingMethods
;
62 private THashSet
<RefElement
> myInTypeReferences
;
63 private THashSet
<RefElement
> myInstanceReferences
;
64 private ArrayList
<RefJavaElement
> myClassExporters
;
66 RefClassImpl(PsiClass psiClass
, RefManager manager
) {
67 super(psiClass
, manager
);
70 protected void initialize() {
71 myDefaultConstructor
= null;
73 final PsiClass psiClass
= getElement();
75 LOG
.assertTrue(psiClass
!= null);
77 PsiElement psiParent
= psiClass
.getParent();
78 if (psiParent
instanceof PsiFile
) {
79 if (isSyntheticJSP()) {
80 final RefFileImpl refFile
= (RefFileImpl
)getRefManager().getReference(JspPsiUtil
.getJspFile(psiClass
));
81 LOG
.assertTrue(refFile
!= null);
83 } else if (psiParent
instanceof PsiJavaFile
) {
84 PsiJavaFile psiFile
= (PsiJavaFile
) psiParent
;
85 String packageName
= psiFile
.getPackageName();
86 if (!"".equals(packageName
)) {
87 ((RefPackageImpl
)getRefJavaManager().getPackage(packageName
)).add(this);
89 ((RefPackageImpl
)getRefJavaManager().getDefaultPackage()).add(this);
92 final Module module
= ModuleUtil
.findModuleForPsiElement(psiClass
);
93 LOG
.assertTrue(module
!= null);
94 final RefModuleImpl refModule
= ((RefModuleImpl
)getRefManager().getRefModule(module
));
95 LOG
.assertTrue(refModule
!= null);
98 while (!(psiParent
instanceof PsiClass
|| psiParent
instanceof PsiMethod
|| psiParent
instanceof PsiField
)) {
99 psiParent
= psiParent
.getParent();
101 RefElement refParent
= getRefManager().getReference(psiParent
);
102 LOG
.assertTrue (refParent
!= null);
103 ((RefElementImpl
)refParent
).add(this);
107 setAbstract(psiClass
.hasModifierProperty(PsiModifier
.ABSTRACT
));
109 setAnonymous(psiClass
instanceof PsiAnonymousClass
);
110 setIsLocal(!(isAnonymous() || psiParent
instanceof PsiClass
|| psiParent
instanceof PsiFile
));
111 setInterface(psiClass
.isInterface());
113 initializeSuperReferences(psiClass
);
115 PsiMethod
[] psiMethods
= psiClass
.getMethods();
116 PsiField
[] psiFields
= psiClass
.getFields();
118 setUtilityClass(psiMethods
.length
> 0 || psiFields
.length
> 0);
120 for (PsiField psiField
: psiFields
) {
121 getRefManager().getReference(psiField
);
125 final PsiClass servlet
= getRefJavaManager().getServlet();
126 setServlet(servlet
!= null && psiClass
.isInheritor(servlet
, true));
128 if (!isApplet() && !isServlet()) {
129 setTestCase(TestUtil
.isTestClass(psiClass
));
130 for (RefClass refBase
: getBaseClasses()) {
131 ((RefClassImpl
)refBase
).setTestCase(true);
135 for (PsiMethod psiMethod
: psiMethods
) {
136 RefMethod refMethod
= (RefMethod
)getRefManager().getReference(psiMethod
);
138 if (refMethod
!= null) {
139 if (psiMethod
.isConstructor()) {
140 if (psiMethod
.getParameterList().getParametersCount() > 0 || !psiMethod
.hasModifierProperty(PsiModifier
.PRIVATE
)) {
141 setUtilityClass(false);
144 addConstructor(refMethod
);
145 if (psiMethod
.getParameterList().getParametersCount() == 0) {
146 setDefaultConstructor((RefMethodImpl
)refMethod
);
150 if (!psiMethod
.hasModifierProperty(PsiModifier
.STATIC
)) {
151 setUtilityClass(false);
157 if (getConstructors().size() == 0 && !isInterface() && !isAnonymous()) {
158 RefImplicitConstructorImpl refImplicitConstructor
= new RefImplicitConstructorImpl(this);
159 setDefaultConstructor(refImplicitConstructor
);
160 addConstructor(refImplicitConstructor
);
164 for (int i
= 0; i
< psiFields
.length
&& isUtilityClass(); i
++) {
165 PsiField psiField
= psiFields
[i
];
166 if (!psiField
.hasModifierProperty(PsiModifier
.STATIC
)) {
167 setUtilityClass(false);
173 final PsiClass applet
= getRefJavaManager().getApplet();
174 setApplet(applet
!= null && psiClass
.isInheritor(applet
, true));
175 getRefManager().fireNodeInitialized(this);
178 private void initializeSuperReferences(PsiClass psiClass
) {
179 if (!isSelfInheritor(psiClass
)) {
180 for (PsiClass psiSuperClass
: psiClass
.getSupers()) {
181 if (getRefManager().belongsToScope(psiSuperClass
)) {
182 RefClassImpl refClass
= (RefClassImpl
)getRefManager().getReference(psiSuperClass
);
183 if (refClass
!= null) {
184 addBaseClass(refClass
);
185 refClass
.addSubClass(this);
192 public boolean isSelfInheritor(PsiClass psiClass
) {
193 return isSelfInheritor(psiClass
, new ArrayList
<PsiClass
>());
196 public PsiClass
getElement() {
197 return (PsiClass
)super.getElement();
200 private static boolean isSelfInheritor(PsiClass psiClass
, ArrayList
<PsiClass
> visited
) {
201 if (visited
.contains(psiClass
)) return true;
203 visited
.add(psiClass
);
204 for (PsiClass aSuper
: psiClass
.getSupers()) {
205 if (isSelfInheritor(aSuper
, visited
)) return true;
207 visited
.remove(psiClass
);
212 private void setDefaultConstructor(RefMethodImpl defaultConstructor
) {
213 if (defaultConstructor
!= null) {
214 for (RefClass superClass
: getBaseClasses()) {
215 RefMethodImpl superDefaultConstructor
= (RefMethodImpl
)superClass
.getDefaultConstructor();
217 if (superDefaultConstructor
!= null) {
218 superDefaultConstructor
.addInReference(defaultConstructor
);
219 defaultConstructor
.addOutReference(superDefaultConstructor
);
224 myDefaultConstructor
= defaultConstructor
;
227 public void buildReferences() {
228 PsiClass psiClass
= getElement();
230 if (psiClass
!= null) {
231 for (PsiClassInitializer classInitializer
: psiClass
.getInitializers()) {
232 RefJavaUtil
.getInstance().addReferences(psiClass
, this, classInitializer
.getBody());
235 RefJavaUtil
.getInstance().addReferences(psiClass
, this, psiClass
.getModifierList());
237 PsiField
[] psiFields
= psiClass
.getFields();
238 for (PsiField psiField
: psiFields
) {
239 getRefManager().getReference(psiField
);
240 final PsiExpression initializer
= psiField
.getInitializer();
241 if (initializer
!= null) {
242 RefJavaUtil
.getInstance().addReferences(psiClass
, this, initializer
);
246 PsiMethod
[] psiMethods
= psiClass
.getMethods();
247 for (PsiMethod psiMethod
: psiMethods
) {
248 getRefManager().getReference(psiMethod
);
250 getRefManager().fireBuildReferences(this);
254 public void accept(final RefVisitor visitor
) {
255 if (visitor
instanceof RefJavaVisitor
) {
256 ApplicationManager
.getApplication().runReadAction(new Runnable() {
258 ((RefJavaVisitor
)visitor
).visitClass(RefClassImpl
.this);
262 super.accept(visitor
);
267 public HashSet
<RefClass
> getBaseClasses() {
268 if (myBases
== null) return EMPTY_CLASS_SET
;
272 private void addBaseClass(RefClass refClass
){
273 if (myBases
== null){
274 myBases
= new HashSet
<RefClass
>(1);
276 myBases
.add(refClass
);
280 public HashSet
<RefClass
> getSubClasses() {
281 if (mySubClasses
== null) return EMPTY_CLASS_SET
;
285 private void addSubClass(RefClass refClass
){
286 if (mySubClasses
== null){
287 mySubClasses
= new HashSet
<RefClass
>(1);
289 mySubClasses
.add(refClass
);
293 public ArrayList
<RefMethod
> getConstructors() {
294 if (myConstructors
== null) return EMPTY_METHOD_LIST
;
295 return myConstructors
;
299 public Set
<RefElement
> getInTypeReferences() {
300 if (myInTypeReferences
== null) return EMPTY_SET
;
301 return myInTypeReferences
;
304 public void addTypeReference(RefJavaElement from
) {
306 if (myInTypeReferences
== null){
307 myInTypeReferences
= new THashSet
<RefElement
>(1);
309 myInTypeReferences
.add(from
);
310 ((RefJavaElementImpl
)from
).addOutTypeRefernce(this);
311 getRefManager().fireNodeMarkedReferenced(this, from
, false, false, false);
316 public Set
<RefElement
> getInstanceReferences() {
317 if (myInstanceReferences
== null) return EMPTY_SET
;
318 return myInstanceReferences
;
321 public void addInstanceReference(RefElement from
) {
322 if (myInstanceReferences
== null){
323 myInstanceReferences
= new THashSet
<RefElement
>(1);
325 myInstanceReferences
.add(from
);
328 public RefMethod
getDefaultConstructor() {
329 return myDefaultConstructor
;
332 private void addConstructor(RefMethod refConstructor
) {
333 if (myConstructors
== null){
334 myConstructors
= new ArrayList
<RefMethod
>(1);
336 myConstructors
.add(refConstructor
);
339 public void addLibraryOverrideMethod(RefMethod refMethod
) {
340 if (myOverridingMethods
== null){
341 myOverridingMethods
= new ArrayList
<RefMethod
>(2);
343 myOverridingMethods
.add(refMethod
);
347 public List
<RefMethod
> getLibraryMethods() {
348 if (myOverridingMethods
== null) return EMPTY_METHOD_LIST
;
349 return myOverridingMethods
;
352 public boolean isAnonymous() {
353 return checkFlag(IS_ANONYMOUS_MASK
);
356 public boolean isInterface() {
357 return checkFlag(IS_INTERFACE_MASK
);
360 public boolean isSuspicious() {
361 return !(isUtilityClass() && getOutReferences().isEmpty()) && super.isSuspicious();
364 public boolean isUtilityClass() {
365 return checkFlag(IS_UTILITY_MASK
);
368 public String
getExternalName() {
369 final String
[] result
= new String
[1];
370 ApplicationManager
.getApplication().runReadAction(new Runnable() {
371 public void run() {//todo synthetic JSP
372 final PsiClass psiClass
= getElement();
373 LOG
.assertTrue(psiClass
!= null);
374 result
[0] = PsiFormatUtil
.getExternalName(psiClass
);
382 public static RefClass
classFromExternalName(RefManager manager
, String externalName
) {
383 return (RefClass
) manager
.getReference(ClassUtil
.findPsiClass(PsiManager
.getInstance(manager
.getProject()), externalName
));
386 public void referenceRemoved() {
387 super.referenceRemoved();
389 for (RefClass subClass
: getSubClasses()) {
390 ((RefClassImpl
)subClass
).removeBase(this);
393 for (RefClass superClass
: getBaseClasses()) {
394 superClass
.getSubClasses().remove(this);
398 private void removeBase(RefClass superClass
) {
399 getBaseClasses().remove(superClass
);
402 protected void methodRemoved(RefMethod method
) {
403 getConstructors().remove(method
);
404 getLibraryMethods().remove(method
);
406 if (getDefaultConstructor() == method
) {
407 setDefaultConstructor(null);
411 public boolean isAbstract() {
412 return checkFlag(IS_ABSTRACT_MASK
);
415 public boolean isApplet() {
416 return checkFlag(IS_APPLET_MASK
);
419 public boolean isServlet() {
420 return checkFlag(IS_SERVLET_MASK
);
423 public boolean isTestCase() {
424 return checkFlag(IS_TESTCASE_MASK
);
427 public boolean isLocalClass() {
428 return checkFlag(IS_LOCAL_MASK
);
432 public boolean isReferenced() {
433 if (super.isReferenced()) return true;
435 if (isInterface() || isAbstract()) {
436 if (getSubClasses().size() > 0) return true;
442 public boolean hasSuspiciousCallers() {
443 if (super.hasSuspiciousCallers()) return true;
445 if (isInterface() || isAbstract()) {
446 if (getSubClasses().size() > 0) return true;
452 public void addClassExporter(RefJavaElement exporter
) {
453 if (myClassExporters
== null) myClassExporters
= new ArrayList
<RefJavaElement
>(1);
454 if (myClassExporters
.contains(exporter
)) return;
455 myClassExporters
.add(exporter
);
458 public List
<RefJavaElement
> getClassExporters() {
459 return myClassExporters
;
462 private void setAnonymous(boolean anonymous
) {
463 setFlag(anonymous
, IS_ANONYMOUS_MASK
);
466 private void setInterface(boolean anInterface
) {
467 setFlag(anInterface
, IS_INTERFACE_MASK
);
470 private void setUtilityClass(boolean utilityClass
) {
471 setFlag(utilityClass
, IS_UTILITY_MASK
);
474 private void setAbstract(boolean anAbstract
) {
475 setFlag(anAbstract
, IS_ABSTRACT_MASK
);
478 private void setApplet(boolean applet
) {
479 setFlag(applet
, IS_APPLET_MASK
);
482 private void setServlet(boolean servlet
) {
483 setFlag(servlet
, IS_SERVLET_MASK
);
486 private void setTestCase(boolean testCase
) {
487 setFlag(testCase
, IS_TESTCASE_MASK
);
490 private void setIsLocal(boolean isLocal
) {
491 setFlag(isLocal
, IS_LOCAL_MASK
);
495 public RefElement
getContainingEntry() {
496 RefElement defaultConstructor
= getDefaultConstructor();
497 if (defaultConstructor
!= null) return defaultConstructor
;
498 return super.getContainingEntry();