update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / psi / impl / source / codeStyle / ReferenceAdjuster.java
blob1dc3f02b28d8210f2674c82f52ee3c89d00a930f
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.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;
73 if (rightKind) {
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();
83 else {
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)) {
102 if (!isShort) {
103 return (TreeElement)makeShortReference(
104 (CompositeElement)element,
105 (PsiClass)refElement,
106 addImports);
110 return (TreeElement)replaceReferenceWithFQ(element, (PsiClass)refElement);
112 else {
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);
124 return element;
127 private boolean makeFQ(boolean isInsideDocComment) {
128 if (isInsideDocComment) {
129 return myUseFqClassnamesInJavadoc;
131 else {
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) {
148 array.add(parent);
149 return;
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);
156 return;
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) {
166 int offset = 0;
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) {
174 array.add(child);
175 } else {
176 addReferencesInRangeForComposite(array, child, startOffset - offset, endOffset - offset);
179 offset += length;
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);
187 return reference;
190 @Nullable
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)) {
200 return reference;
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);
208 return null;
212 if (addImports && !((PsiImportHolder) reference.getContainingFile()).importClass(refClass)) return null;
213 if (!isSafeToShortenReference(reference, refClass)) return null;
214 return reference;
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));
227 @NotNull
228 private static ASTNode replaceReferenceWithShort(PsiQualifiedReference reference) {
229 final ASTNode node = reference.getNode();
230 assert node != null;
231 dequalifyImpl((CompositeElement)node);
232 return 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);
244 return reference;