/* * Copyright 2013 Google Inc. * * 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.google.gwt.dev.javac; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.ImportReference; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Removes unused imports from CompilationUnitDeclarations. * * Needed after running GwtIncompatibleRemover to remove imports to GwtIncompatible elements. */ public class UnusedImportsRemover { private Set<String> usedNames = new HashSet<String>(); /** * Accumulate all names that can be brought in by imports. * * This is a conservative pass, i.e. it might leave some unused imports. */ private class AccumulateNamesVisitor extends ASTVisitor { @Override public void endVisit( SingleNameReference singleNameReference, BlockScope scope) { addName(singleNameReference); } @Override public void endVisit( SingleNameReference singleNameReference, ClassScope scope) { addName(singleNameReference); } @Override public void endVisit( SingleTypeReference singleTypeReference, BlockScope scope) { addName(singleTypeReference); } @Override public void endVisit( SingleTypeReference singleTypeReference, ClassScope scope) { addName(singleTypeReference); } @Override public void endVisit(MessageSend messageSend, BlockScope scope) { if (messageSend.receiver instanceof ThisReference) { usedNames.add(new String(messageSend.selector)); } } @Override public void endVisit(ArrayTypeReference arrayTypeReference, BlockScope scope) { addName(arrayTypeReference); } @Override public void endVisit(ArrayTypeReference arrayTypeReference, ClassScope scope) { addName(arrayTypeReference); } @Override public void endVisit( ArrayQualifiedTypeReference arrayQualifiedTypeReference, BlockScope scope) { addName(arrayQualifiedTypeReference); } @Override public void endVisit( ArrayQualifiedTypeReference arrayQualifiedTypeReference, ClassScope scope) { addName(arrayQualifiedTypeReference); } @Override public void endVisit(ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference, BlockScope scope) { addName(parameterizedQualifiedTypeReference); } @Override public void endVisit(ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference, ClassScope scope) { addName(parameterizedQualifiedTypeReference); } @Override public void endVisit(ParameterizedSingleTypeReference parameterizedSingleTypeReference, BlockScope scope) { addName(parameterizedSingleTypeReference); } @Override public void endVisit(ParameterizedSingleTypeReference parameterizedSingleTypeReference, ClassScope scope) { addName(parameterizedSingleTypeReference); } @Override public void endVisit( QualifiedTypeReference qualifiedTypeReference, BlockScope scope) { addName(qualifiedTypeReference); } @Override public void endVisit( QualifiedTypeReference qualifiedTypeReference, ClassScope scope) { addName(qualifiedTypeReference); } @Override public void endVisit( QualifiedNameReference qualifiedNameReference, BlockScope scope) { addName(qualifiedNameReference); } @Override public void endVisit( QualifiedNameReference qualifiedNameReference, ClassScope scope) { addName(qualifiedNameReference); } public void addName(QualifiedNameReference reference) { usedNames.add(new String(reference.tokens[0])); } public void addName(QualifiedTypeReference reference) { usedNames.add(new String(reference.tokens[0])); } public void addName(SingleTypeReference reference) { usedNames.add(new String(reference.token)); } public void addName(SingleNameReference reference) { usedNames.add(new String(reference.token)); } } public static void exec(CompilationUnitDeclaration cud) { new UnusedImportsRemover().execImpl(cud); } void execImpl(CompilationUnitDeclaration cud) { if (cud.imports == null) { return; } AccumulateNamesVisitor astVisitor = new AccumulateNamesVisitor(); if (cud.types != null) { for (TypeDeclaration typeDecl : cud.types) { typeDecl.traverse(astVisitor, cud.scope); } } // for some reason JDT does not traverse package annotations even if the traversal started at // the Compilation unit declaration. Hence we do it manually. if (cud.currentPackage != null && cud.currentPackage.annotations != null) { for (Annotation annotation : cud.currentPackage.annotations) { annotation.traverse(astVisitor, (BlockScope) null); } } List<ImportReference> newImports = new ArrayList<ImportReference>(); for (ImportReference importRef : cud.imports) { String importName = new String(importRef.getImportName()[importRef.getImportName().length - 1]); if (importName.equals("*") || // very hacky it seems that this is the only way // to notice a import static blah.Blah.*; importRef.trailingStarPosition > 0 || usedNames.contains(importName)) { // Either a * or a possible reference, so keep it. newImports.add(importRef); } } if (newImports.size() != cud.imports.length) { cud.imports = newImports.toArray(new ImportReference[newImports.size()]); } } }