blob: 13ac82b52dcb643a506e651ebe25f305caf5e6fc [file] [log] [blame]
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.actions;
import com.intellij.codeInsight.CodeInsightUtilCore;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.LogicalRoot;
import com.intellij.util.LogicalRootsManager;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author yole
*/
public class JavaQualifiedNameProvider implements QualifiedNameProvider {
private static final Logger LOG = Logger.getInstance("#com.intellij.ide.actions.JavaQualifiedNameProvider");
@Nullable
public PsiElement adjustElementToCopy(final PsiElement element) {
if (element instanceof PsiPackage) return element;
if (element instanceof PsiDirectory) {
final PsiPackage psiPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)element);
if (psiPackage != null) return psiPackage;
}
if (element != null && !(element instanceof PsiMember) && element.getParent() instanceof PsiMember) {
return element.getParent();
}
return null;
}
@Nullable
public String getQualifiedName(PsiElement element) {
if (element instanceof PsiPackage) return ((PsiPackage)element).getQualifiedName();
element = getMember(element);
if (element instanceof PsiClass) {
return ((PsiClass)element).getQualifiedName();
}
else if (element instanceof PsiMember) {
final PsiMember member = (PsiMember)element;
PsiClass containingClass = member.getContainingClass();
if (containingClass instanceof PsiAnonymousClass) containingClass = ((PsiAnonymousClass)containingClass).getBaseClassType().resolve();
if (containingClass == null) return null;
String classFqn = containingClass.getQualifiedName();
if (classFqn == null) return member.getName(); // refer to member of anonymous class by simple name
if (member instanceof PsiMethod && containingClass.findMethodsByName(member.getName(), false).length > 1) {
return classFqn + "#" + member.getName() + getParameterString((PsiMethod)member);
}
return classFqn + "#" + member.getName();
}
return null;
}
public PsiElement qualifiedNameToElement(final String fqn, final Project project) {
final PsiPackage psiPackage = JavaPsiFacade.getInstance(project).findPackage(fqn);
if (psiPackage != null) {
return psiPackage;
}
PsiClass aClass = JavaPsiFacade.getInstance(project).findClass(fqn, GlobalSearchScope.allScope(project));
if (aClass != null) {
return aClass;
}
final int endIndex = fqn.indexOf('#');
if (endIndex != -1) {
String className = fqn.substring(0, endIndex);
int paramIndex = fqn.indexOf('(', endIndex);
aClass = JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project));
if (aClass != null) {
String memberName = fqn.substring(endIndex + 1, paramIndex < 0 ? fqn.length() : paramIndex);
PsiField field = aClass.findFieldByName(memberName, false);
if (field != null) {
return field;
}
String paramString = paramIndex < 0 ? "" : fqn.substring(paramIndex);
for (PsiMethod overload : aClass.findMethodsByName(memberName, false)) {
if (StringUtil.isEmpty(paramString) || paramString.equals(getParameterString(overload))) {
return overload;
}
}
}
}
VirtualFile file = findFile(fqn, project);
if (file != null) {
return PsiManager.getInstance(project).findFile(file);
}
return null;
}
private static VirtualFile findFile(String fqn, Project project) {
List<LogicalRoot> lr = LogicalRootsManager.getLogicalRootsManager(project).getLogicalRoots();
for (LogicalRoot root : lr) {
VirtualFile vfr = root.getVirtualFile();
if (vfr == null) continue;
VirtualFile virtualFile = vfr.findFileByRelativePath(fqn);
if (virtualFile != null) return virtualFile;
}
for (VirtualFile root : ProjectRootManager.getInstance(project).getContentRoots()) {
VirtualFile rel = root.findFileByRelativePath(fqn);
if (rel != null) {
return rel;
}
}
VirtualFile file = LocalFileSystem.getInstance().findFileByPath(fqn);
if (file != null) return file;
PsiFile[] files = PsiShortNamesCache.getInstance(project).getFilesByName(fqn);
for (PsiFile psiFile : files) {
VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile != null) return virtualFile;
}
return null;
}
public void insertQualifiedName(String fqn, final PsiElement element, final Editor editor, final Project project) {
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
Document document = editor.getDocument();
final PsiFile file = documentManager.getPsiFile(document);
final int offset = editor.getCaretModel().getOffset();
PsiElement elementAtCaret = file.findElementAt(offset);
fqn = fqn.replace('#', '.');
String toInsert;
String suffix = "";
if (!(element instanceof PsiMember)) {
toInsert = fqn;
}
else if (elementAtCaret != null && (element instanceof PsiMethod || element instanceof PsiField) && PsiUtil.isInsideJavadocComment(elementAtCaret)) {
// use fqn#methodName(ParamType)
PsiMember member = (PsiMember)element;
PsiClass aClass = member.getContainingClass();
String className = aClass == null ? "" : aClass.getQualifiedName();
toInsert = className == null ? "" : className;
if (toInsert.length() != 0) toInsert += "#";
toInsert += member.getName();
if (member instanceof PsiMethod) {
toInsert += getParameterString((PsiMethod)member);
}
}
else if (elementAtCaret == null ||
PsiTreeUtil.getNonStrictParentOfType(elementAtCaret, PsiLiteralExpression.class, PsiComment.class) != null ||
PsiTreeUtil.getNonStrictParentOfType(elementAtCaret, PsiJavaFile.class) == null) {
toInsert = fqn;
}
else {
PsiMember targetElement = (PsiMember)element;
toInsert = targetElement.getName();
if (targetElement instanceof PsiMethod) {
suffix = "()";
if (((PsiMethod)targetElement).isConstructor()) {
targetElement = targetElement.getContainingClass();
}
}
else if (targetElement instanceof PsiClass) {
if (isAfterNew(file, elementAtCaret)) {
// pasting reference to default constructor of the class after new
suffix = "()";
}
else if (toInsert != null && toInsert.length() != 0 && Character.isJavaIdentifierPart(toInsert.charAt(toInsert.length()-1)) && Character.isJavaIdentifierPart(elementAtCaret.getText().charAt(0))) {
//separate identifiers with space
suffix = " ";
}
}
final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
final PsiExpression expression;
try {
expression = factory.createExpressionFromText(toInsert + suffix, elementAtCaret);
}
catch (IncorrectOperationException e) {
LOG.error(e);
return;
}
final PsiReferenceExpression referenceExpression = expression instanceof PsiMethodCallExpression
? ((PsiMethodCallExpression)expression).getMethodExpression()
: expression instanceof PsiReferenceExpression
? (PsiReferenceExpression)expression
: null;
if (referenceExpression == null || !referenceExpression.isValid()) {
toInsert = fqn;
}
else if (!isReferencedTo(referenceExpression, targetElement)) {
try {
referenceExpression.bindToElement(targetElement);
}
catch (IncorrectOperationException e) {
// failed to bind
}
if (!referenceExpression.isValid() || !isReferencedTo(referenceExpression, targetElement)) {
toInsert = fqn;
}
}
}
if (toInsert == null) toInsert = "";
document.insertString(offset, toInsert+suffix);
documentManager.commitAllDocuments();
int endOffset = offset + toInsert.length() + suffix.length();
RangeMarker rangeMarker = document.createRangeMarker(endOffset, endOffset);
elementAtCaret = file.findElementAt(offset);
if (elementAtCaret != null && elementAtCaret.isValid()) {
try {
shortenReference(elementAtCaret, element);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(file);
try {
CodeStyleManager.getInstance(project).adjustLineIndent(file, offset);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
int caretOffset = rangeMarker.getEndOffset();
if (element instanceof PsiMethod && ((PsiMethod)element).getParameterList().getParametersCount() != 0 && StringUtil.endsWithChar(suffix,')')) {
caretOffset --;
}
editor.getCaretModel().moveToOffset(caretOffset);
}
private static String getParameterString(PsiMethod method) {
String toInsert = "(";
PsiParameter[] parameters = method.getParameterList().getParameters();
for (int i = 0; i < parameters.length; i++) {
PsiParameter parameter = parameters[i];
if (i != 0) toInsert += ", ";
toInsert += parameter.getType().getCanonicalText();
}
toInsert += ")";
return toInsert;
}
private static boolean isReferencedTo(PsiReferenceExpression referenceExpression, PsiMember targetElement) {
PsiElement resolved = referenceExpression.advancedResolve(true).getElement();
if (!(resolved instanceof PsiMember)) return false;
PsiClass aClass = ((PsiMember)resolved).getContainingClass();
if (aClass instanceof PsiAnonymousClass) aClass = ((PsiAnonymousClass)aClass).getBaseClassType().resolve();
return aClass == targetElement.getContainingClass();
}
@Nullable
private static PsiElement getMember(final PsiElement element) {
if (element instanceof PsiMember) return element;
if (element instanceof PsiReference) {
PsiElement resolved = ((PsiReference)element).resolve();
if (resolved instanceof PsiMember) return resolved;
}
if (!(element instanceof PsiIdentifier)) return null;
final PsiElement parent = element.getParent();
PsiMember member = null;
if (parent instanceof PsiJavaCodeReferenceElement) {
PsiElement resolved = ((PsiJavaCodeReferenceElement)parent).resolve();
if (resolved instanceof PsiMember) {
member = (PsiMember)resolved;
}
}
else if (parent instanceof PsiMember) {
member = (PsiMember)parent;
}
else {
//todo show error
//return;
}
return member;
}
private static boolean isAfterNew(PsiFile file, PsiElement elementAtCaret) {
PsiElement prevSibling = elementAtCaret.getPrevSibling();
if (prevSibling == null) return false;
int offset = prevSibling.getTextRange().getStartOffset();
PsiElement prevElement = file.findElementAt(offset);
return PsiTreeUtil.getParentOfType(prevElement, PsiNewExpression.class) != null;
}
private static void shortenReference(PsiElement element, PsiElement elementToInsert) throws IncorrectOperationException {
while (element.getParent() instanceof PsiJavaCodeReferenceElement) {
element = element.getParent();
if (element == null) return;
}
//if (element instanceof PsiJavaCodeReferenceElement && elementToInsert != null) {
// try {
// element = ((PsiJavaCodeReferenceElement)element).bindToElement(elementToInsert);
// }
// catch (IncorrectOperationException e) {
// // failed to bind
// }
//}
final JavaCodeStyleManager codeStyleManagerEx = JavaCodeStyleManager.getInstance(element.getProject());
codeStyleManagerEx.shortenClassReferences(element, JavaCodeStyleManager.UNCOMPLETE_CODE);
}
}