diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java | 424 |
1 files changed, 0 insertions, 424 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java deleted file mode 100644 index 1a7fe5697..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.android.ide.eclipse.adt.internal.lint; - -import static com.android.SdkConstants.FQCN_SUPPRESS_LINT; -import static com.android.SdkConstants.FQCN_TARGET_API; -import static com.android.SdkConstants.SUPPRESS_LINT; -import static com.android.SdkConstants.TARGET_API; -import static org.eclipse.jdt.core.dom.ArrayInitializer.EXPRESSIONS_PROPERTY; -import static org.eclipse.jdt.core.dom.SingleMemberAnnotation.VALUE_PROPERTY; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.sdklib.SdkVersionInfo; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.AdtUtils; -import com.android.ide.eclipse.adt.internal.editors.IconFactory; -import com.android.tools.lint.checks.AnnotationDetector; -import com.android.tools.lint.checks.ApiDetector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.Scope; - -import org.eclipse.core.resources.IMarker; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.dom.AST; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; -import org.eclipse.jdt.core.dom.ArrayInitializer; -import org.eclipse.jdt.core.dom.BodyDeclaration; -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.Expression; -import org.eclipse.jdt.core.dom.FieldDeclaration; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.NodeFinder; -import org.eclipse.jdt.core.dom.SingleMemberAnnotation; -import org.eclipse.jdt.core.dom.StringLiteral; -import org.eclipse.jdt.core.dom.TypeDeclaration; -import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; -import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; -import org.eclipse.jdt.core.dom.rewrite.ListRewrite; -import org.eclipse.jdt.ui.IWorkingCopyManager; -import org.eclipse.jdt.ui.JavaUI; -import org.eclipse.jdt.ui.SharedASTProvider; -import org.eclipse.jface.text.IDocument; -import org.eclipse.swt.graphics.Image; -import org.eclipse.text.edits.MultiTextEdit; -import org.eclipse.text.edits.TextEdit; -import org.eclipse.ui.IEditorInput; -import org.eclipse.ui.IMarkerResolution; -import org.eclipse.ui.IMarkerResolution2; -import org.eclipse.ui.texteditor.IDocumentProvider; -import org.eclipse.ui.texteditor.ITextEditor; - -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Marker resolution for adding {@code @SuppressLint} annotations in Java files. - * It can also add {@code @TargetApi} annotations. - */ -class AddSuppressAnnotation implements IMarkerResolution2 { - private final IMarker mMarker; - private final String mId; - private final BodyDeclaration mNode; - private final String mDescription; - /** - * Should it create a {@code @TargetApi} annotation instead of - * {@code SuppressLint} ? If so pass a non null API level - */ - private final String mTargetApi; - - private AddSuppressAnnotation( - @NonNull String id, - @NonNull IMarker marker, - @NonNull BodyDeclaration node, - @NonNull String description, - @Nullable String targetApi) { - mId = id; - mMarker = marker; - mNode = node; - mDescription = description; - mTargetApi = targetApi; - } - - @Override - public String getLabel() { - return mDescription; - } - - @Override - public String getDescription() { - return null; - } - - @Override - public Image getImage() { - return IconFactory.getInstance().getIcon("newannotation"); //$NON-NLS-1$ - } - - @Override - public void run(IMarker marker) { - ITextEditor textEditor = AdtUtils.getActiveTextEditor(); - IDocumentProvider provider = textEditor.getDocumentProvider(); - IEditorInput editorInput = textEditor.getEditorInput(); - IDocument document = provider.getDocument(editorInput); - if (document == null) { - return; - } - IWorkingCopyManager manager = JavaUI.getWorkingCopyManager(); - ICompilationUnit compilationUnit = manager.getWorkingCopy(editorInput); - try { - MultiTextEdit edit; - if (mTargetApi == null) { - edit = addSuppressAnnotation(document, compilationUnit, mNode); - } else { - edit = addTargetApiAnnotation(document, compilationUnit, mNode); - } - if (edit != null) { - edit.apply(document); - - // Remove the marker now that the suppress annotation has been added - // (so the user doesn't have to re-run lint just to see it disappear, - // and besides we don't want to keep offering marker resolutions on this - // marker which could lead to duplicate annotations since the above code - // assumes that the current id isn't in the list of values, since otherwise - // lint shouldn't have complained here. - mMarker.delete(); - } - } catch (Exception ex) { - AdtPlugin.log(ex, "Could not add suppress annotation"); - } - } - - @SuppressWarnings({"rawtypes"}) // Java AST API has raw types - private MultiTextEdit addSuppressAnnotation( - IDocument document, - ICompilationUnit compilationUnit, - BodyDeclaration declaration) throws CoreException { - List modifiers = declaration.modifiers(); - SingleMemberAnnotation existing = null; - for (Object o : modifiers) { - if (o instanceof SingleMemberAnnotation) { - SingleMemberAnnotation annotation = (SingleMemberAnnotation) o; - String type = annotation.getTypeName().getFullyQualifiedName(); - if (type.equals(FQCN_SUPPRESS_LINT) || type.endsWith(SUPPRESS_LINT)) { - existing = annotation; - break; - } - } - } - - ImportRewrite importRewrite = ImportRewrite.create(compilationUnit, true); - String local = importRewrite.addImport(FQCN_SUPPRESS_LINT); - AST ast = declaration.getAST(); - ASTRewrite rewriter = ASTRewrite.create(ast); - if (existing == null) { - SingleMemberAnnotation newAnnotation = ast.newSingleMemberAnnotation(); - newAnnotation.setTypeName(ast.newSimpleName(local)); - StringLiteral value = ast.newStringLiteral(); - value.setLiteralValue(mId); - newAnnotation.setValue(value); - ListRewrite listRewrite = rewriter.getListRewrite(declaration, - declaration.getModifiersProperty()); - listRewrite.insertFirst(newAnnotation, null); - } else { - Expression existingValue = existing.getValue(); - if (existingValue instanceof StringLiteral) { - StringLiteral stringLiteral = (StringLiteral) existingValue; - if (mId.equals(stringLiteral.getLiteralValue())) { - // Already contains the id - return null; - } - // Create a new array initializer holding the old string plus the new id - ArrayInitializer array = ast.newArrayInitializer(); - StringLiteral old = ast.newStringLiteral(); - old.setLiteralValue(stringLiteral.getLiteralValue()); - array.expressions().add(old); - StringLiteral value = ast.newStringLiteral(); - value.setLiteralValue(mId); - array.expressions().add(value); - rewriter.set(existing, VALUE_PROPERTY, array, null); - } else if (existingValue instanceof ArrayInitializer) { - // Existing array: just append the new string - ArrayInitializer array = (ArrayInitializer) existingValue; - List expressions = array.expressions(); - if (expressions != null) { - for (Object o : expressions) { - if (o instanceof StringLiteral) { - if (mId.equals(((StringLiteral)o).getLiteralValue())) { - // Already contains the id - return null; - } - } - } - } - StringLiteral value = ast.newStringLiteral(); - value.setLiteralValue(mId); - ListRewrite listRewrite = rewriter.getListRewrite(array, EXPRESSIONS_PROPERTY); - listRewrite.insertLast(value, null); - } else { - assert false : existingValue; - return null; - } - } - - TextEdit importEdits = importRewrite.rewriteImports(new NullProgressMonitor()); - TextEdit annotationEdits = rewriter.rewriteAST(document, null); - - // Apply to the document - MultiTextEdit edit = new MultiTextEdit(); - // Create the edit to change the imports, only if - // anything changed - if (importEdits.hasChildren()) { - edit.addChild(importEdits); - } - edit.addChild(annotationEdits); - - return edit; - } - - @SuppressWarnings({"rawtypes"}) // Java AST API has raw types - private MultiTextEdit addTargetApiAnnotation( - IDocument document, - ICompilationUnit compilationUnit, - BodyDeclaration declaration) throws CoreException { - List modifiers = declaration.modifiers(); - SingleMemberAnnotation existing = null; - for (Object o : modifiers) { - if (o instanceof SingleMemberAnnotation) { - SingleMemberAnnotation annotation = (SingleMemberAnnotation) o; - String type = annotation.getTypeName().getFullyQualifiedName(); - if (type.equals(FQCN_TARGET_API) || type.endsWith(TARGET_API)) { - existing = annotation; - break; - } - } - } - - ImportRewrite importRewrite = ImportRewrite.create(compilationUnit, true); - importRewrite.addImport("android.os.Build"); //$NON-NLS-1$ - String local = importRewrite.addImport(FQCN_TARGET_API); - AST ast = declaration.getAST(); - ASTRewrite rewriter = ASTRewrite.create(ast); - if (existing == null) { - SingleMemberAnnotation newAnnotation = ast.newSingleMemberAnnotation(); - newAnnotation.setTypeName(ast.newSimpleName(local)); - Expression value = createLiteral(ast); - newAnnotation.setValue(value); - ListRewrite listRewrite = rewriter.getListRewrite(declaration, - declaration.getModifiersProperty()); - listRewrite.insertFirst(newAnnotation, null); - } else { - Expression value = createLiteral(ast); - rewriter.set(existing, VALUE_PROPERTY, value, null); - } - - TextEdit importEdits = importRewrite.rewriteImports(new NullProgressMonitor()); - TextEdit annotationEdits = rewriter.rewriteAST(document, null); - MultiTextEdit edit = new MultiTextEdit(); - if (importEdits.hasChildren()) { - edit.addChild(importEdits); - } - edit.addChild(annotationEdits); - - return edit; - } - - private Expression createLiteral(AST ast) { - Expression value; - if (!isCodeName()) { - value = ast.newQualifiedName( - ast.newQualifiedName(ast.newSimpleName("Build"), //$NON-NLS-1$ - ast.newSimpleName("VERSION_CODES")), //$NON-NLS-1$ - ast.newSimpleName(mTargetApi)); - } else { - value = ast.newNumberLiteral(mTargetApi); - } - return value; - } - - private boolean isCodeName() { - return Character.isDigit(mTargetApi.charAt(0)); - } - - /** - * Adds any applicable suppress lint fix resolutions into the given list - * - * @param marker the marker to create fixes for - * @param id the issue id - * @param resolutions a list to add the created resolutions into, if any - */ - public static void createFixes(IMarker marker, String id, - List<IMarkerResolution> resolutions) { - ITextEditor textEditor = AdtUtils.getActiveTextEditor(); - IDocumentProvider provider = textEditor.getDocumentProvider(); - IEditorInput editorInput = textEditor.getEditorInput(); - IDocument document = provider.getDocument(editorInput); - if (document == null) { - return; - } - - IWorkingCopyManager manager = JavaUI.getWorkingCopyManager(); - ICompilationUnit compilationUnit = manager.getWorkingCopy(editorInput); - int offset = 0; - int length = 0; - int start = marker.getAttribute(IMarker.CHAR_START, -1); - int end = marker.getAttribute(IMarker.CHAR_END, -1); - offset = start; - length = end - start; - CompilationUnit root = SharedASTProvider.getAST(compilationUnit, - SharedASTProvider.WAIT_YES, null); - if (root == null) { - return; - } - - int api = -1; - if (id.equals(ApiDetector.UNSUPPORTED.getId()) || - id.equals(ApiDetector.INLINED.getId())) { - String message = marker.getAttribute(IMarker.MESSAGE, null); - if (message != null) { - Pattern pattern = Pattern.compile("\\s(\\d+)\\s"); //$NON-NLS-1$ - Matcher matcher = pattern.matcher(message); - if (matcher.find()) { - api = Integer.parseInt(matcher.group(1)); - } - } - } - - Issue issue = EclipseLintClient.getRegistry().getIssue(id); - boolean isClassDetector = issue != null && issue.getImplementation().getScope().contains( - Scope.CLASS_FILE); - - // Don't offer to suppress (with an annotation) the annotation checks - if (issue == AnnotationDetector.ISSUE) { - return; - } - - NodeFinder nodeFinder = new NodeFinder(root, offset, length); - ASTNode coveringNode; - if (offset <= 0) { - // Error added on the first line of a Java class: typically from a class-based - // detector which lacks line information. Map this to the top level class - // in the file instead. - coveringNode = root; - if (root.types() != null && root.types().size() > 0) { - Object type = root.types().get(0); - if (type instanceof ASTNode) { - coveringNode = (ASTNode) type; - } - } - } else { - coveringNode = nodeFinder.getCoveringNode(); - } - for (ASTNode body = coveringNode; body != null; body = body.getParent()) { - if (body instanceof BodyDeclaration) { - BodyDeclaration declaration = (BodyDeclaration) body; - - String target = null; - if (body instanceof MethodDeclaration) { - target = ((MethodDeclaration) body).getName().toString() + "()"; //$NON-NLS-1$ - } else if (body instanceof FieldDeclaration) { - target = "field"; - FieldDeclaration field = (FieldDeclaration) body; - if (field.fragments() != null && field.fragments().size() > 0) { - ASTNode first = (ASTNode) field.fragments().get(0); - if (first instanceof VariableDeclarationFragment) { - VariableDeclarationFragment decl = (VariableDeclarationFragment) first; - target = decl.getName().toString(); - } - } - } else if (body instanceof AnonymousClassDeclaration) { - target = "anonymous class"; - } else if (body instanceof TypeDeclaration) { - target = ((TypeDeclaration) body).getName().toString(); - } else { - target = body.getClass().getSimpleName(); - } - - // In class files, detectors can only find annotations on methods - // and on classes, not on variable declarations - if (isClassDetector && !(body instanceof MethodDeclaration - || body instanceof TypeDeclaration - || body instanceof AnonymousClassDeclaration - || body instanceof FieldDeclaration)) { - continue; - } - - String desc = String.format("Add @SuppressLint '%1$s\' to '%2$s'", id, target); - resolutions.add(new AddSuppressAnnotation(id, marker, declaration, desc, null)); - - if (api != -1 - // @TargetApi is only valid on methods and classes, not fields etc - && (body instanceof MethodDeclaration - || body instanceof TypeDeclaration)) { - String apiString = SdkVersionInfo.getBuildCode(api); - if (apiString == null) { - apiString = Integer.toString(api); - } - desc = String.format("Add @TargetApi(%1$s) to '%2$s'", apiString, target); - resolutions.add(new AddSuppressAnnotation(id, marker, declaration, desc, - apiString)); - } - } - } - } -} |