blob: 5b580dce2ced956cc1e2aa396da1a6618d021ae0 [file] [log] [blame]
/*
* Copyright 2000-2014 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 org.jetbrains.plugins.groovy.lang.psi.util;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyImportHelper;
import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint;
import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassResolverProcessor;
import java.util.Map;
import java.util.Set;
public class GroovyImportUtil {
public static void processFile(@Nullable final PsiFile file,
@Nullable final Set<String> importedClasses,
@Nullable final Set<String> staticallyImportedMembers,
@Nullable final Set<GrImportStatement> usedImports,
@Nullable final Set<GrImportStatement> unresolvedOnDemandImports,
@Nullable final Set<String> implicitlyImported,
@Nullable final Set<String> innerClasses,
@Nullable final Map<String, String> aliased,
@Nullable final Map<String, String> annotations) {
if (!(file instanceof GroovyFile)) return;
final Set<String> unresolvedReferenceNames = ContainerUtil.newLinkedHashSet();
file.accept(new PsiRecursiveElementWalkingVisitor() {
@Override
public void visitElement(PsiElement element) {
super.visitElement(element);
if (element instanceof GrReferenceElement) {
visitRefElement((GrReferenceElement)element);
}
}
private void visitRefElement(GrReferenceElement refElement) {
final String refName = refElement.getReferenceName();
if ("super".equals(refName)) return;
final GroovyResolveResult[] resolveResults = refElement.multiResolve(false);
if (resolveResults.length == 0 && refName != null) {
if (PsiTreeUtil.getParentOfType(refElement, GrImportStatement.class) == null) {
unresolvedReferenceNames.add(refName);
}
}
for (GroovyResolveResult resolveResult : resolveResults) {
final PsiElement context = resolveResult.getCurrentFileResolveContext();
final PsiElement resolved = resolveResult.getElement();
if (resolved == null) return;
if (context instanceof GrImportStatement) {
final GrImportStatement importStatement = (GrImportStatement)context;
if (usedImports != null && isImportUsed(refElement, resolved)) {
usedImports.add(importStatement);
}
if (GroovyImportHelper.isImplicitlyImported(resolved, refName, (GroovyFile)file)) {
addImplicitClass(resolved);
}
if (!importStatement.isAliasedImport() && !isAnnotatedImport(importStatement)) {
String importedName = null;
if (importStatement.isOnDemand()) {
if (importStatement.isStatic()) {
if (resolved instanceof PsiMember) {
final PsiMember member = (PsiMember)resolved;
final PsiClass clazz = member.getContainingClass();
if (clazz != null) {
final String classQName = clazz.getQualifiedName();
if (classQName != null) {
final String name = member.getName();
if (name != null) {
importedName = classQName + "." + name;
}
}
}
}
}
else {
importedName = getTargetQualifiedName(resolved);
}
}
else {
final GrCodeReferenceElement importReference = importStatement.getImportReference();
if (importReference != null) {
importedName = PsiUtil.getQualifiedReferenceText(importReference);
}
}
if (importedName == null) return;
final String importRef = getImportReferenceText(importStatement);
if (importStatement.isAliasedImport()) {
if (aliased != null) {
aliased.put(importRef, importedName);
}
return;
}
if (importStatement.isStatic()) {
if (staticallyImportedMembers != null) {
staticallyImportedMembers.add(importedName);
}
}
else {
if (importedClasses != null) {
importedClasses.add(importedName);
}
if (resolved instanceof PsiClass && ((PsiClass)resolved).getContainingClass() != null && innerClasses != null) {
innerClasses.add(importedName);
}
}
}
}
else if (context == null && !(refElement.getParent() instanceof GrImportStatement) && refElement.getQualifier() == null) {
addImplicitClass(resolved);
}
}
}
private void addImplicitClass(PsiElement element) {
final String qname = getTargetQualifiedName(element);
if (qname != null) {
if (implicitlyImported != null) {
implicitlyImported.add(qname);
}
if (importedClasses != null) {
importedClasses.add(qname);
}
}
}
/**
* checks if import for implicitly imported class is needed
*/
private boolean isImportUsed(GrReferenceElement refElement, PsiElement resolved) {
if (GroovyImportHelper.isImplicitlyImported(resolved, refElement.getReferenceName(), (GroovyFile)file)) {
final ClassResolverProcessor processor =
new ClassResolverProcessor(refElement.getReferenceName(), refElement, ClassHint.RESOLVE_KINDS_CLASS);
GroovyImportHelper
.processImports(ResolveState.initial(), null, refElement, processor, ((GroovyFile)file).getImportStatements(), true);
if (!processor.hasCandidates()) {
return false;
}
}
return true;
}
});
final Set<GrImportStatement> importsToCheck = ContainerUtil.newLinkedHashSet(PsiUtil.getValidImportStatements((GroovyFile)file));
for (GrImportStatement anImport : importsToCheck) {
if (usedImports != null && usedImports.contains(anImport)) continue;
final GrCodeReferenceElement ref = anImport.getImportReference();
assert ref != null : "invalid import!";
if (ref.resolve() == null) {
if (anImport.isOnDemand()) {
if (usedImports != null) {
usedImports.add(anImport);
}
if (unresolvedOnDemandImports != null) {
unresolvedOnDemandImports.add(anImport);
}
}
else {
String importedName = anImport.getImportedName();
if (importedName != null && unresolvedReferenceNames.contains(importedName)) {
if (usedImports != null) {
usedImports.add(anImport);
}
final String symbolName = getImportReferenceText(anImport);
if (anImport.isAliasedImport()) {
if (aliased != null) {
aliased.put(symbolName, importedName);
}
}
else {
if (anImport.isStatic()) {
if (staticallyImportedMembers != null) {
staticallyImportedMembers.add(symbolName);
}
}
else {
if (importedClasses != null) {
importedClasses.add(symbolName);
}
}
}
}
}
}
}
if (annotations != null) {
((GroovyFile)file).acceptChildren(new GroovyElementVisitor() {
@Override
public void visitImportStatement(GrImportStatement importStatement) {
final String annotationText = importStatement.getAnnotationList().getText();
if (!StringUtil.isEmptyOrSpaces(annotationText)) {
final String importRef = getImportReferenceText(importStatement);
annotations.put(importRef, annotationText);
}
}
});
}
}
@Nullable
private static String getTargetQualifiedName(PsiElement element) {
if (element instanceof PsiClass) {
return ((PsiClass)element).getQualifiedName();
}
if (element instanceof PsiMethod && ((PsiMethod)element).isConstructor()) {
return ((PsiMethod)element).getContainingClass().getQualifiedName();
}
return null;
}
public static boolean isAnnotatedImport(GrImportStatement anImport) {
return !StringUtil.isEmptyOrSpaces(anImport.getAnnotationList().getText());
}
@Nullable
public static String getImportReferenceText(GrImportStatement statement) {
GrCodeReferenceElement importReference = statement.getImportReference();
if (importReference != null) {
return importReference.getClassNameText();
}
return null;
}
public static Set<GrImportStatement> findUsedImports(GroovyFile file) {
Set<GrImportStatement> usedImports = new HashSet<GrImportStatement>();
processFile(file, null, null, usedImports, null, null, null, null, null);
return usedImports;
}
}