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
.codeStyle
;
18 import com
.intellij
.lang
.ASTNode
;
19 import com
.intellij
.openapi
.project
.Project
;
20 import com
.intellij
.psi
.*;
21 import com
.intellij
.psi
.codeStyle
.CodeStyleSettings
;
22 import com
.intellij
.psi
.codeStyle
.CodeStyleSettingsManager
;
23 import com
.intellij
.psi
.impl
.source
.Constants
;
24 import com
.intellij
.psi
.impl
.source
.PsiJavaCodeReferenceElementImpl
;
25 import com
.intellij
.psi
.impl
.source
.SourceJavaCodeReference
;
26 import com
.intellij
.psi
.impl
.source
.SourceTreeToPsiMap
;
27 import com
.intellij
.psi
.impl
.source
.jsp
.jspJava
.JspClass
;
28 import com
.intellij
.psi
.impl
.source
.tree
.*;
29 import com
.intellij
.psi
.jsp
.JspFile
;
30 import com
.intellij
.psi
.tree
.IElementType
;
31 import org
.jetbrains
.annotations
.NotNull
;
32 import org
.jetbrains
.annotations
.Nullable
;
34 import java
.util
.ArrayList
;
36 public class ReferenceAdjuster
implements Constants
{
37 private final boolean myUseFqClassnamesInJavadoc
;
38 private final boolean myUseFqClassNames
;
40 public ReferenceAdjuster(boolean useFqInJavadoc
, boolean useFqInCode
) {
41 myUseFqClassnamesInJavadoc
= useFqInJavadoc
;
42 myUseFqClassNames
= useFqInCode
;
45 public ReferenceAdjuster(Project project
) {
46 this(CodeStyleSettingsManager
.getSettings(project
));
49 public ReferenceAdjuster(CodeStyleSettings settings
) {
50 this(settings
.USE_FQ_CLASS_NAMES_IN_JAVADOC
, settings
.USE_FQ_CLASS_NAMES
);
53 public TreeElement
process(TreeElement element
, boolean addImports
, boolean uncompleteCode
) {
54 IElementType elementType
= element
.getElementType();
55 if (elementType
== JAVA_CODE_REFERENCE
|| elementType
== REFERENCE_EXPRESSION
) {
56 if (elementType
== JAVA_CODE_REFERENCE
|| element
.getTreeParent().getElementType() == REFERENCE_EXPRESSION
|| uncompleteCode
) {
57 final PsiJavaCodeReferenceElement ref
= (PsiJavaCodeReferenceElement
)SourceTreeToPsiMap
.treeElementToPsi(element
);
58 final PsiReferenceParameterList parameterList
= ref
.getParameterList();
59 if (parameterList
!= null) {
60 final PsiTypeElement
[] typeParameters
= parameterList
.getTypeParameterElements();
61 for (PsiTypeElement typeParameter
: typeParameters
) {
62 process((TreeElement
)SourceTreeToPsiMap
.psiElementToTree(typeParameter
), addImports
, uncompleteCode
);
66 boolean rightKind
= true;
67 if (elementType
== JAVA_CODE_REFERENCE
) {
68 int kind
= ((PsiJavaCodeReferenceElementImpl
)element
).getKind();
69 rightKind
= kind
== PsiJavaCodeReferenceElementImpl
.CLASS_NAME_KIND
||
70 kind
== PsiJavaCodeReferenceElementImpl
.CLASS_OR_PACKAGE_NAME_KIND
;
74 boolean isInsideDocComment
= TreeUtil
.findParent(element
, JavaDocElementType
.DOC_COMMENT
) != null;
75 boolean isShort
= !((SourceJavaCodeReference
)element
).isQualified();
76 if (!makeFQ(isInsideDocComment
)) {
77 if (isShort
) return element
; // short name already, no need to change
79 PsiElement refElement
;
80 if (!uncompleteCode
) {
81 refElement
= ref
.resolve();
84 PsiResolveHelper helper
= JavaPsiFacade
.getInstance(element
.getManager().getProject()).getResolveHelper();
85 refElement
= helper
.resolveReferencedClass(
86 ((SourceJavaCodeReference
)element
).getClassNameText(),
87 SourceTreeToPsiMap
.treeElementToPsi(element
)
90 if (refElement
instanceof PsiClass
) {
91 if (makeFQ(isInsideDocComment
)) {
92 String qName
= ((PsiClass
)refElement
).getQualifiedName();
93 if (qName
== null) return element
;
94 PsiImportHolder file
= (PsiImportHolder
) SourceTreeToPsiMap
.treeElementToPsi(element
).getContainingFile();
95 if (file
instanceof PsiJavaFile
&& ImportHelper
.isImplicitlyImported(qName
, (PsiJavaFile
) file
)) {
96 if (isShort
) return element
;
97 return (TreeElement
)makeShortReference((CompositeElement
)element
, (PsiClass
)refElement
, addImports
);
99 if (file
instanceof PsiJavaFile
) {
100 String thisPackageName
= ((PsiJavaFile
)file
).getPackageName();
101 if (ImportHelper
.hasPackage(qName
, thisPackageName
)) {
103 return (TreeElement
)makeShortReference(
104 (CompositeElement
)element
,
105 (PsiClass
)refElement
,
110 return (TreeElement
)replaceReferenceWithFQ(element
, (PsiClass
)refElement
);
113 return (TreeElement
)makeShortReference((CompositeElement
)element
, (PsiClass
)refElement
, addImports
);
120 for (TreeElement child
= element
.getFirstChildNode(); child
!= null; child
= child
.getTreeNext()) {
121 child
= process(child
, addImports
, uncompleteCode
);
127 private boolean makeFQ(boolean isInsideDocComment
) {
128 if (isInsideDocComment
) {
129 return myUseFqClassnamesInJavadoc
;
132 return myUseFqClassNames
;
136 public void processRange(TreeElement element
, int startOffset
, int endOffset
) {
137 ArrayList
<ASTNode
> array
= new ArrayList
<ASTNode
>();
138 addReferencesInRange(array
, element
, startOffset
, endOffset
);
139 for (ASTNode ref
: array
) {
140 if (SourceTreeToPsiMap
.treeElementToPsi(ref
).isValid()) {
141 process((TreeElement
)ref
, true, true);
146 private static void addReferencesInRange(ArrayList
<ASTNode
> array
, TreeElement parent
, int startOffset
, int endOffset
) {
147 if (parent
.getElementType() == ElementType
.JAVA_CODE_REFERENCE
|| parent
.getElementType() == ElementType
.REFERENCE_EXPRESSION
) {
152 if (parent
.getPsi() instanceof PsiFile
&& JspPsiUtil
.isInJspFile(parent
.getPsi())) {
153 final JspFile jspFile
= (JspPsiUtil
.getJspFile(parent
.getPsi()));
154 JspClass jspClass
= (JspClass
) jspFile
.getJavaClass();
155 addReferencesInRange(array
, (TreeElement
)jspClass
.getNode(), startOffset
, endOffset
);
159 addReferencesInRangeForComposite(array
, parent
, startOffset
, endOffset
);
162 private static void addReferencesInRangeForComposite(final ArrayList
<ASTNode
> array
,
163 final TreeElement parent
,
164 final int startOffset
,
165 final int endOffset
) {
167 for (TreeElement child
= parent
.getFirstChildNode(); child
!= null; child
= child
.getTreeNext()) {
168 int length
= child
.getTextLength();
170 if (startOffset
<= offset
+ length
&& offset
<= endOffset
) {
171 final IElementType type
= child
.getElementType();
173 if (type
== ElementType
.JAVA_CODE_REFERENCE
|| type
== ElementType
.REFERENCE_EXPRESSION
) {
176 addReferencesInRangeForComposite(array
, child
, startOffset
- offset
, endOffset
- offset
);
183 private static ASTNode
makeShortReference(@NotNull CompositeElement reference
, @NotNull PsiClass refClass
, boolean addImports
) {
184 @NotNull final PsiJavaCodeReferenceElement psiReference
= (PsiJavaCodeReferenceElement
)reference
.getPsi();
185 final PsiQualifiedReference reference1
= getClassReferenceToShorten(refClass
, addImports
, psiReference
);
186 if (reference1
!= null) replaceReferenceWithShort(reference1
);
191 public static PsiQualifiedReference
getClassReferenceToShorten(@NotNull final PsiClass refClass
,
192 final boolean addImports
,
193 @NotNull final PsiQualifiedReference reference
) {
194 PsiClass parentClass
= refClass
.getContainingClass();
195 if (parentClass
!= null) {
196 JavaPsiFacade facade
= JavaPsiFacade
.getInstance(parentClass
.getProject());
197 final PsiResolveHelper resolveHelper
= facade
.getResolveHelper();
198 if (resolveHelper
.isAccessible(refClass
, reference
, null) &&
199 isSafeToShortenReference(reference
.getReferenceName(), reference
, refClass
)) {
203 if (!CodeStyleSettingsManager
.getSettings(reference
.getProject()).INSERT_INNER_CLASS_IMPORTS
) {
204 final PsiElement qualifier
= reference
.getQualifier();
205 if (qualifier
instanceof PsiQualifiedReference
) {
206 return getClassReferenceToShorten(parentClass
, addImports
, (PsiQualifiedReference
)qualifier
);
212 if (addImports
&& !((PsiImportHolder
) reference
.getContainingFile()).importClass(refClass
)) return null;
213 if (!isSafeToShortenReference(reference
, refClass
)) return null;
217 private static boolean isSafeToShortenReference(@NotNull PsiElement psiReference
, @NotNull PsiClass refClass
) {
218 return isSafeToShortenReference(refClass
.getName(), psiReference
, refClass
);
221 private static boolean isSafeToShortenReference(final String referenceText
, final PsiElement psiReference
, final PsiClass refClass
) {
222 final PsiManager manager
= refClass
.getManager();
223 final JavaPsiFacade facade
= JavaPsiFacade
.getInstance(manager
.getProject());
224 return manager
.areElementsEquivalent(refClass
, facade
.getResolveHelper().resolveReferencedClass(referenceText
, psiReference
));
228 private static ASTNode
replaceReferenceWithShort(PsiQualifiedReference reference
) {
229 final ASTNode node
= reference
.getNode();
231 dequalifyImpl((CompositeElement
)node
);
235 private static void dequalifyImpl(@NotNull CompositeElement reference
) {
236 final ASTNode qualifier
= reference
.findChildByRole(ChildRole
.QUALIFIER
);
237 if (qualifier
!= null) {
238 reference
.deleteChildInternal(qualifier
);
242 private static ASTNode
replaceReferenceWithFQ(ASTNode reference
, PsiClass refClass
) {
243 ((SourceJavaCodeReference
)reference
).fullyQualify(refClass
);