2 * Copyright 2003-2008 Dave Griffith, Bas Leijdekkers
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
.siyeh
.ig
.resources
;
18 import com
.intellij
.psi
.*;
19 import com
.intellij
.psi
.util
.PsiTreeUtil
;
20 import com
.siyeh
.HardcodedMethodConstants
;
21 import com
.siyeh
.InspectionGadgetsBundle
;
22 import com
.siyeh
.ig
.BaseInspection
;
23 import com
.siyeh
.ig
.BaseInspectionVisitor
;
24 import com
.siyeh
.ig
.psiutils
.TypeUtils
;
25 import org
.jetbrains
.annotations
.NonNls
;
26 import org
.jetbrains
.annotations
.NotNull
;
28 public class JNDIResourceInspection
extends BaseInspection
{
31 public String
getID(){
32 return "JNDIResourceOpenedButNotSafelyClosed";
36 public String
getDisplayName(){
37 return InspectionGadgetsBundle
.message(
38 "jndi.resource.opened.not.closed.display.name");
42 public String
buildErrorString(Object
... infos
){
43 final PsiExpression expression
= (PsiExpression
) infos
[0];
44 final PsiType type
= expression
.getType();
46 final String text
= type
.getPresentableText();
47 return InspectionGadgetsBundle
.message(
48 "resource.opened.not.closed.problem.descriptor", text
);
51 public BaseInspectionVisitor
buildVisitor(){
52 return new JNDIResourceVisitor();
55 private static class JNDIResourceVisitor
extends BaseInspectionVisitor
{
57 @NonNls private static final String LIST
= "list";
58 @NonNls private static final String LIST_BINDING
= "listBindings";
59 @NonNls private static final String GET_ALL
= "getAll";
61 @Override public void visitMethodCallExpression(
62 @NotNull PsiMethodCallExpression expression
){
63 super.visitMethodCallExpression(expression
);
64 if(!isJNDIFactoryMethod(expression
)){
67 final PsiElement parent
= expression
.getParent();
68 if(!(parent
instanceof PsiAssignmentExpression
)){
69 registerError(expression
, expression
);
72 final PsiAssignmentExpression assignment
=
73 (PsiAssignmentExpression
) parent
;
74 final PsiExpression lhs
= assignment
.getLExpression();
75 if(!(lhs
instanceof PsiReferenceExpression
)){
78 final PsiElement referent
=
79 ((PsiReference
) lhs
).resolve();
80 if(referent
== null || !(referent
instanceof PsiVariable
)){
83 final PsiVariable boundVariable
= (PsiVariable
) referent
;
85 PsiElement currentContext
= expression
;
87 final PsiTryStatement tryStatement
=
88 PsiTreeUtil
.getParentOfType(currentContext
,
89 PsiTryStatement
.class);
90 if(tryStatement
== null){
91 registerError(expression
, expression
);
94 if(resourceIsOpenedInTryAndClosedInFinally(tryStatement
,
99 currentContext
= tryStatement
;
104 @Override public void visitNewExpression(
105 @NotNull PsiNewExpression expression
){
106 super.visitNewExpression(expression
);
107 if(!isJNDIResource(expression
)){
110 if(expression
.getType() == null){
113 final PsiElement parent
= expression
.getParent();
114 if(!(parent
instanceof PsiAssignmentExpression
)){
115 registerError(expression
, expression
);
118 final PsiAssignmentExpression assignment
=
119 (PsiAssignmentExpression
) parent
;
120 final PsiExpression lhs
= assignment
.getLExpression();
121 if(!(lhs
instanceof PsiReferenceExpression
)){
124 final PsiElement referent
=
125 ((PsiReference
) lhs
).resolve();
126 if(referent
== null || !(referent
instanceof PsiVariable
)){
129 final PsiVariable boundVariable
= (PsiVariable
) referent
;
131 PsiElement currentContext
= expression
;
133 final PsiTryStatement tryStatement
=
134 PsiTreeUtil
.getParentOfType(currentContext
,
135 PsiTryStatement
.class);
136 if(tryStatement
== null){
137 registerError(expression
, expression
);
140 if(resourceIsOpenedInTryAndClosedInFinally(tryStatement
,
141 expression
, boundVariable
)) {
144 currentContext
= tryStatement
;
148 private static boolean resourceIsOpenedInTryAndClosedInFinally(
149 PsiTryStatement tryStatement
, PsiExpression resourceExpression
,
150 PsiVariable boundVariable
){
151 final PsiCodeBlock finallyBlock
= tryStatement
.getFinallyBlock();
152 if(finallyBlock
== null){
155 final PsiCodeBlock tryBlock
= tryStatement
.getTryBlock();
156 if(tryBlock
== null){
159 if(!PsiTreeUtil
.isAncestor(tryBlock
, resourceExpression
, true)){
162 return containsResourceClose(finallyBlock
, boundVariable
);
165 private static boolean containsResourceClose(PsiCodeBlock finallyBlock
,
166 PsiVariable boundVariable
){
167 final CloseVisitor visitor
=
168 new CloseVisitor(boundVariable
);
169 finallyBlock
.accept(visitor
);
170 return visitor
.containsStreamClose();
173 private static boolean isJNDIResource(PsiNewExpression expression
){
174 return TypeUtils
.expressionHasTypeOrSubtype(expression
,
175 "javax.naming.InitialContext");
178 private static boolean isJNDIFactoryMethod(
179 PsiMethodCallExpression expression
){
180 final PsiReferenceExpression methodExpression
=
181 expression
.getMethodExpression();
182 final String methodName
= methodExpression
.getReferenceName();
183 if (LIST
.equals(methodName
) || LIST_BINDING
.equals(methodName
)) {
184 final PsiExpression qualifier
=
185 methodExpression
.getQualifierExpression();
186 if (qualifier
== null) {
189 return TypeUtils
.expressionHasTypeOrSubtype(qualifier
,
190 "javax.naming.Context");
191 } else if (GET_ALL
.equals(methodName
)) {
192 final PsiExpression qualifier
=
193 methodExpression
.getQualifierExpression();
194 if (qualifier
== null) {
197 return TypeUtils
.expressionHasTypeOrSubtype(qualifier
,
198 "javax.naming.directory.Attribute") ||
199 TypeUtils
.expressionHasTypeOrSubtype(qualifier
,
200 "javax.naming.directory.Attributes");
207 private static class CloseVisitor
extends JavaRecursiveElementVisitor
{
209 private boolean containsClose
= false;
210 private PsiVariable socketToClose
;
212 private CloseVisitor(PsiVariable socketToClose
){
214 this.socketToClose
= socketToClose
;
217 @Override public void visitElement(@NotNull PsiElement element
){
219 super.visitElement(element
);
223 @Override public void visitMethodCallExpression(
224 @NotNull PsiMethodCallExpression call
){
228 super.visitMethodCallExpression(call
);
229 final PsiReferenceExpression methodExpression
=
230 call
.getMethodExpression();
231 final String methodName
= methodExpression
.getReferenceName();
232 if(!HardcodedMethodConstants
.CLOSE
.equals(methodName
)){
235 final PsiExpression qualifier
=
236 methodExpression
.getQualifierExpression();
237 if(!(qualifier
instanceof PsiReferenceExpression
)){
240 final PsiElement referent
=
241 ((PsiReference
) qualifier
).resolve();
242 if(referent
== null){
245 if(referent
.equals(socketToClose
)){
246 containsClose
= true;
250 public boolean containsStreamClose(){
251 return containsClose
;