diff options
Diffstat (limited to 'lint/libs/lint_api')
36 files changed, 0 insertions, 11429 deletions
diff --git a/lint/libs/lint_api/.classpath b/lint/libs/lint_api/.classpath deleted file mode 100644 index 0381385..0000000 --- a/lint/libs/lint_api/.classpath +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<classpath> - <classpathentry excluding="Android.mk" kind="src" path="src"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry combineaccessrules="false" kind="src" path="/common"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src.zip"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-tree-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src.zip"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/guava-tools/guava-13.0.1.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/guava-tools/src.zip"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/lombok-ast/lombok-ast-0.2.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/lombok-ast/src.zip"/> - <classpathentry combineaccessrules="false" kind="src" path="/layoutlib_api"/> - <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/> - <classpathentry kind="output" path="bin"/> -</classpath> diff --git a/lint/libs/lint_api/.project b/lint/libs/lint_api/.project deleted file mode 100644 index 7c50676..0000000 --- a/lint/libs/lint_api/.project +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<projectDescription> - <name>lint-api</name> - <comment></comment> - <projects> - </projects> - <buildSpec> - <buildCommand> - <name>org.eclipse.jdt.core.javabuilder</name> - <arguments> - </arguments> - </buildCommand> - </buildSpec> - <natures> - <nature>org.eclipse.jdt.core.javanature</nature> - </natures> -</projectDescription> diff --git a/lint/libs/lint_api/.settings/org.eclipse.jdt.core.prefs b/lint/libs/lint_api/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index d11c211..0000000 --- a/lint/libs/lint_api/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,98 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore -org.eclipse.jdt.core.compiler.annotation.nonnull=com.android.annotations.NonNull -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=com.android.annotations.NonNullByDefault -org.eclipse.jdt.core.compiler.annotation.nonnullisdefault=disabled -org.eclipse.jdt.core.compiler.annotation.nullable=com.android.annotations.Nullable -org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.6 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.autoboxing=ignore -org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning -org.eclipse.jdt.core.compiler.problem.deadCode=warning -org.eclipse.jdt.core.compiler.problem.deprecation=warning -org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled -org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled -org.eclipse.jdt.core.compiler.problem.discouragedReference=warning -org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore -org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning -org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled -org.eclipse.jdt.core.compiler.problem.fieldHiding=warning -org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning -org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning -org.eclipse.jdt.core.compiler.problem.forbiddenReference=error -org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning -org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled -org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning -org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore -org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore -org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning -org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning -org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore -org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning -org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled -org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled -org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning -org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore -org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning -org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning -org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore -org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error -org.eclipse.jdt.core.compiler.problem.nullReference=error -org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo=warning -org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error -org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=ignore -org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning -org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore -org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning -org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning -org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation=error -org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning -org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning -org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning -org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore -org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore -org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning -org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore -org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore -org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled -org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning -org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled -org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled -org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore -org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning -org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled -org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning -org.eclipse.jdt.core.compiler.problem.unclosedCloseable=error -org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore -org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning -org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore -org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning -org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled -org.eclipse.jdt.core.compiler.problem.unusedImport=warning -org.eclipse.jdt.core.compiler.problem.unusedLabel=warning -org.eclipse.jdt.core.compiler.problem.unusedLocal=warning -org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning -org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled -org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning -org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning -org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=1.6 diff --git a/lint/libs/lint_api/.settings/org.moreunit.prefs b/lint/libs/lint_api/.settings/org.moreunit.prefs deleted file mode 100644 index 73d4d8e..0000000 --- a/lint/libs/lint_api/.settings/org.moreunit.prefs +++ /dev/null @@ -1,6 +0,0 @@ -#Tue Oct 18 10:20:08 PDT 2011 -eclipse.preferences.version=1 -org.moreunit.extendedTestMethodSearch=true -org.moreunit.prefixes= -org.moreunit.unitsourcefolder=lint-api\:src\:lint_check-tests\:src -org.moreunit.useprojectsettings=true diff --git a/lint/libs/lint_api/Android.mk b/lint/libs/lint_api/Android.mk deleted file mode 100644 index 9e2e678..0000000 --- a/lint/libs/lint_api/Android.mk +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (C) 2011 The Android Open Source Project -# -# 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. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -# Only compile source java files in this lib. -LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_JAVA_RESOURCE_DIRS := src -LOCAL_JAVA_LIBRARIES := \ - lombok-ast-0.2 \ - common \ - sdklib \ - layoutlib_api \ - asm-tools \ - asm-tree-tools \ - guava-tools - -LOCAL_MODULE := lint_api -LOCAL_MODULE_TAGS := optional - -include $(BUILD_HOST_JAVA_LIBRARY) - -# Build all sub-directories -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/lint/libs/lint_api/NOTICE b/lint/libs/lint_api/NOTICE deleted file mode 100644 index becc120..0000000 --- a/lint/libs/lint_api/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2011, The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - - 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. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/AsmVisitor.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/AsmVisitor.java deleted file mode 100644 index 81e2934..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/AsmVisitor.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * 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.android.tools.lint.client.api; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.ClassContext; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Detector.ClassScanner; -import com.google.common.annotations.Beta; - -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Specialized visitor for running detectors on a class object model. - * <p> - * It operates in two phases: - * <ol> - * <li> First, it computes a set of maps where it generates a map from each - * significant method name to a list of detectors to consult for that method - * name. The set of method names that a detector is interested in is provided - * by the detectors themselves. - * <li> Second, it iterates over the DOM a single time. For each method call it finds, - * it dispatches to any check that has registered interest in that method name. - * <li> Finally, it runs a full check on those class scanners that do not register - * specific method names to be checked. This is intended for those detectors - * that do custom work, not related specifically to method calls. - * </ol> - * It also notifies all the detectors before and after the document is processed - * such that they can do pre- and post-processing. - * <p> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -class AsmVisitor { - /** - * Number of distinct node types specified in {@link AbstractInsnNode}. Sadly - * there isn't a max-constant there, so update this along with ASM library - * updates. - */ - public final static int TYPE_COUNT = AbstractInsnNode.LINE + 1; - private final Map<String, List<ClassScanner>> mMethodNameToChecks = - new HashMap<String, List<ClassScanner>>(); - private final Map<String, List<ClassScanner>> mMethodOwnerToChecks = - new HashMap<String, List<ClassScanner>>(); - private final List<Detector> mFullClassChecks = new ArrayList<Detector>(); - - private final LintClient mClient; - private final List<? extends Detector> mAllDetectors; - private List<ClassScanner>[] mNodeTypeDetectors; - - // Really want this: - //<T extends List<Detector> & Detector.ClassScanner> ClassVisitor(T xmlDetectors) { - // but it makes client code tricky and ugly. - @SuppressWarnings("unchecked") - AsmVisitor(@NonNull LintClient client, @NonNull List<? extends Detector> classDetectors) { - mClient = client; - mAllDetectors = classDetectors; - - // TODO: Check appliesTo() for files, and find a quick way to enable/disable - // rules when running through a full project! - for (Detector detector : classDetectors) { - Detector.ClassScanner scanner = (Detector.ClassScanner) detector; - - boolean checkFullClass = true; - - Collection<String> names = scanner.getApplicableCallNames(); - if (names != null) { - checkFullClass = false; - for (String element : names) { - List<Detector.ClassScanner> list = mMethodNameToChecks.get(element); - if (list == null) { - list = new ArrayList<Detector.ClassScanner>(); - mMethodNameToChecks.put(element, list); - } - list.add(scanner); - } - } - - Collection<String> owners = scanner.getApplicableCallOwners(); - if (owners != null) { - checkFullClass = false; - for (String element : owners) { - List<Detector.ClassScanner> list = mMethodOwnerToChecks.get(element); - if (list == null) { - list = new ArrayList<Detector.ClassScanner>(); - mMethodOwnerToChecks.put(element, list); - } - list.add(scanner); - } - } - - int[] types = scanner.getApplicableAsmNodeTypes(); - if (types != null) { - checkFullClass = false; - for (int i = 0, n = types.length; i < n; i++) { - int type = types[i]; - if (type < 0 || type >= TYPE_COUNT) { - // Can't support this node type: looks like ASM wasn't updated correctly. - mClient.log(null, "Out of range node type %1$d from detector %2$s", - type, scanner); - continue; - } - if (mNodeTypeDetectors == null) { - mNodeTypeDetectors = new List[TYPE_COUNT]; - } - List<ClassScanner> checks = mNodeTypeDetectors[type]; - if (checks == null) { - checks = new ArrayList<ClassScanner>(); - mNodeTypeDetectors[type] = checks; - } - checks.add(scanner); - } - } - - if (checkFullClass) { - mFullClassChecks.add(detector); - } - } - } - - @SuppressWarnings("rawtypes") // ASM API uses raw types - void runClassDetectors(ClassContext context) { - ClassNode classNode = context.getClassNode(); - - for (Detector detector : mAllDetectors) { - detector.beforeCheckFile(context); - } - - for (Detector detector : mFullClassChecks) { - Detector.ClassScanner scanner = (Detector.ClassScanner) detector; - scanner.checkClass(context, classNode); - detector.afterCheckFile(context); - } - - if (!mMethodNameToChecks.isEmpty() || !mMethodOwnerToChecks.isEmpty() || - mNodeTypeDetectors != null && mNodeTypeDetectors.length > 0) { - List methodList = classNode.methods; - for (Object m : methodList) { - MethodNode method = (MethodNode) m; - InsnList nodes = method.instructions; - for (int i = 0, n = nodes.size(); i < n; i++) { - AbstractInsnNode instruction = nodes.get(i); - int type = instruction.getType(); - if (type == AbstractInsnNode.METHOD_INSN) { - MethodInsnNode call = (MethodInsnNode) instruction; - - String owner = call.owner; - List<ClassScanner> scanners = mMethodOwnerToChecks.get(owner); - if (scanners != null) { - for (ClassScanner scanner : scanners) { - scanner.checkCall(context, classNode, method, call); - } - } - - String name = call.name; - scanners = mMethodNameToChecks.get(name); - if (scanners != null) { - for (ClassScanner scanner : scanners) { - scanner.checkCall(context, classNode, method, call); - } - } - } - - if (mNodeTypeDetectors != null && type < mNodeTypeDetectors.length) { - List<ClassScanner> scanners = mNodeTypeDetectors[type]; - if (scanners != null) { - for (ClassScanner scanner : scanners) { - scanner.checkInstruction(context, classNode, method, instruction); - } - } - } - } - } - } - - for (Detector detector : mAllDetectors) { - detector.afterCheckFile(context); - } - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/Configuration.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/Configuration.java deleted file mode 100644 index ad1c7e1..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/Configuration.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.client.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Severity; -import com.google.common.annotations.Beta; - -/** - * Lint configuration for an Android project such as which specific rules to include, - * which specific rules to exclude, and which specific errors to ignore. - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public abstract class Configuration { - /** - * Checks whether this issue should be ignored because the user has already - * suppressed the error? Note that this refers to individual issues being - * suppressed/ignored, not a whole detector being disabled via something - * like {@link #isEnabled(Issue)}. - * - * @param context the context used by the detector when the issue was found - * @param issue the issue that was found - * @param location the location of the issue - * @param message the associated user message - * @param data additional information about an issue (see - * {@link LintClient#report(Context, Issue, Severity, Location, String, Object)} - * for more information - * @return true if this issue should be suppressed - */ - public boolean isIgnored( - @NonNull Context context, - @NonNull Issue issue, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - return false; - } - - /** - * Returns false if the given issue has been disabled. This is just - * a convenience method for {@code getSeverity(issue) != Severity.IGNORE}. - * - * @param issue the issue to check - * @return false if the issue has been disabled - */ - public boolean isEnabled(@NonNull Issue issue) { - return getSeverity(issue) != Severity.IGNORE; - } - - /** - * Returns the severity for a given issue. This is the same as the - * {@link Issue#getDefaultSeverity()} unless the user has selected a custom - * severity (which is tool context dependent). - * - * @param issue the issue to look up the severity from - * @return the severity use for issues for the given detector - */ - public Severity getSeverity(@NonNull Issue issue) { - return issue.getDefaultSeverity(); - } - - // Editing configurations - - /** - * Marks the given warning as "ignored". - * - * @param context The scanning context - * @param issue the issue to be ignored - * @param location The location to ignore the warning at, if any - * @param message The message for the warning - * @param data The corresponding data, or null - */ - public abstract void ignore( - @NonNull Context context, - @NonNull Issue issue, - @Nullable Location location, - @NonNull String message, - @Nullable Object data); - - /** - * Sets the severity to be used for this issue. - * - * @param issue the issue to set the severity for - * @param severity the severity to associate with this issue, or null to - * reset the severity to the default - */ - public abstract void setSeverity(@NonNull Issue issue, @Nullable Severity severity); - - // Bulk editing support - - /** - * Marks the beginning of a "bulk" editing operation with repeated calls to - * {@link #setSeverity} or {@link #ignore}. After all the values haver been - * set, the client <b>must</b> call {@link #finishBulkEditing()}. This - * allows configurations to avoid doing expensive I/O (such as writing out a - * config XML file) for each and every editing operation when they are - * applied in bulk, such as from a configuration dialog's "Apply" action. - */ - public void startBulkEditing() { - } - - /** - * Marks the end of a "bulk" editing operation, where values should be - * committed to persistent storage. See {@link #startBulkEditing()} for - * details. - */ - public void finishBulkEditing() { - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultConfiguration.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultConfiguration.java deleted file mode 100644 index 5a8a973..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultConfiguration.java +++ /dev/null @@ -1,453 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.client.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Project; -import com.android.tools.lint.detector.api.Severity; -import com.google.common.annotations.Beta; -import com.google.common.io.Closeables; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; -import org.xml.sax.SAXParseException; - -import java.io.BufferedInputStream; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.io.Writer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -/** - * Default implementation of a {@link Configuration} which reads and writes - * configuration data into {@code lint.xml} in the project directory. - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public class DefaultConfiguration extends Configuration { - private final LintClient mClient; - private static final String CONFIG_FILE_NAME = "lint.xml"; //$NON-NLS-1$ - - // Lint XML File - @NonNull - private static final String TAG_ISSUE = "issue"; //$NON-NLS-1$ - @NonNull - private static final String ATTR_ID = "id"; //$NON-NLS-1$ - @NonNull - private static final String ATTR_SEVERITY = "severity"; //$NON-NLS-1$ - @NonNull - private static final String ATTR_PATH = "path"; //$NON-NLS-1$ - @NonNull - private static final String TAG_IGNORE = "ignore"; //$NON-NLS-1$ - - private final Configuration mParent; - protected final Project mProject; - private final File mConfigFile; - private boolean mBulkEditing; - - /** Map from id to list of project-relative paths for suppressed warnings */ - private Map<String, List<String>> mSuppressed; - - /** - * Map from id to custom {@link Severity} override - */ - private Map<String, Severity> mSeverity; - - protected DefaultConfiguration( - @NonNull LintClient client, - @Nullable Project project, - @Nullable Configuration parent, - @NonNull File configFile) { - mClient = client; - mProject = project; - mParent = parent; - mConfigFile = configFile; - } - - protected DefaultConfiguration( - @NonNull LintClient client, - @NonNull Project project, - @Nullable Configuration parent) { - this(client, project, parent, new File(project.getDir(), CONFIG_FILE_NAME)); - } - - /** - * Creates a new {@link DefaultConfiguration} - * - * @param client the client to report errors to etc - * @param project the associated project - * @param parent the parent/fallback configuration or null - * @return a new configuration - */ - @NonNull - public static DefaultConfiguration create( - @NonNull LintClient client, - @NonNull Project project, - @Nullable Configuration parent) { - return new DefaultConfiguration(client, project, parent); - } - - /** - * Creates a new {@link DefaultConfiguration} for the given lint config - * file, not affiliated with a project. This is used for global - * configurations. - * - * @param client the client to report errors to etc - * @param lintFile the lint file containing the configuration - * @return a new configuration - */ - @NonNull - public static DefaultConfiguration create(@NonNull LintClient client, @NonNull File lintFile) { - return new DefaultConfiguration(client, null /*project*/, null /*parent*/, lintFile); - } - - @Override - public boolean isIgnored( - @NonNull Context context, - @NonNull Issue issue, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - ensureInitialized(); - - String id = issue.getId(); - List<String> paths = mSuppressed.get(id); - if (paths != null && location != null) { - File file = location.getFile(); - String relativePath = context.getProject().getRelativePath(file); - for (String suppressedPath : paths) { - if (suppressedPath.equals(relativePath)) { - return true; - } - } - } - - if (mParent != null) { - return mParent.isIgnored(context, issue, location, message, data); - } - - return false; - } - - @NonNull - protected Severity getDefaultSeverity(@NonNull Issue issue) { - if (!issue.isEnabledByDefault()) { - return Severity.IGNORE; - } - - return issue.getDefaultSeverity(); - } - - @Override - @NonNull - public Severity getSeverity(@NonNull Issue issue) { - ensureInitialized(); - - Severity severity = mSeverity.get(issue.getId()); - if (severity != null) { - return severity; - } - - if (mParent != null) { - return mParent.getSeverity(issue); - } - - return getDefaultSeverity(issue); - } - - private void ensureInitialized() { - if (mSuppressed == null) { - readConfig(); - } - } - - private void formatError(String message, Object... args) { - if (args != null && args.length > 0) { - message = String.format(message, args); - } - message = "Failed to parse lint.xml configuration file: " + message; - LintDriver driver = new LintDriver(new IssueRegistry() { - @Override @NonNull public List<Issue> getIssues() { - return Collections.emptyList(); - } - }, mClient); - mClient.report(new Context(driver, mProject, mProject, mConfigFile), - IssueRegistry.LINT_ERROR, - mProject.getConfiguration().getSeverity(IssueRegistry.LINT_ERROR), - Location.create(mConfigFile), message, null); - } - - private void readConfig() { - mSuppressed = new HashMap<String, List<String>>(); - mSeverity = new HashMap<String, Severity>(); - - if (!mConfigFile.exists()) { - return; - } - - @SuppressWarnings("resource") // Eclipse doesn't know about Closeables.closeQuietly - BufferedInputStream input = null; - try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - input = new BufferedInputStream(new FileInputStream(mConfigFile)); - InputSource source = new InputSource(input); - factory.setNamespaceAware(false); - factory.setValidating(false); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document document = builder.parse(source); - NodeList issues = document.getElementsByTagName(TAG_ISSUE); - for (int i = 0, count = issues.getLength(); i < count; i++) { - Node node = issues.item(i); - Element element = (Element) node; - String id = element.getAttribute(ATTR_ID); - if (id.length() == 0) { - formatError("Invalid lint config file: Missing required issue id attribute"); - continue; - } - - NamedNodeMap attributes = node.getAttributes(); - for (int j = 0, n = attributes.getLength(); j < n; j++) { - Node attribute = attributes.item(j); - String name = attribute.getNodeName(); - String value = attribute.getNodeValue(); - if (ATTR_ID.equals(name)) { - // already handled - } else if (ATTR_SEVERITY.equals(name)) { - for (Severity severity : Severity.values()) { - if (value.equalsIgnoreCase(severity.name())) { - mSeverity.put(id, severity); - break; - } - } - } else { - formatError("Unexpected attribute \"%1$s\"", name); - } - } - - // Look up ignored errors - NodeList childNodes = element.getChildNodes(); - if (childNodes.getLength() > 0) { - for (int j = 0, n = childNodes.getLength(); j < n; j++) { - Node child = childNodes.item(j); - if (child.getNodeType() == Node.ELEMENT_NODE) { - Element ignore = (Element) child; - String path = ignore.getAttribute(ATTR_PATH); - if (path.length() == 0) { - formatError("Missing required %1$s attribute under %2$s", - ATTR_PATH, id); - } else { - List<String> paths = mSuppressed.get(id); - if (paths == null) { - paths = new ArrayList<String>(n / 2 + 1); - mSuppressed.put(id, paths); - } - paths.add(path); - } - } - } - } - } - } catch (SAXParseException e) { - formatError(e.getMessage()); - } catch (Exception e) { - mClient.log(e, null); - } finally { - Closeables.closeQuietly(input); - } - } - - private void writeConfig() { - try { - // Write the contents to a new file first such that we don't clobber the - // existing file if some I/O error occurs. - File file = new File(mConfigFile.getParentFile(), - mConfigFile.getName() + ".new"); //$NON-NLS-1$ - - Writer writer = new BufferedWriter(new FileWriter(file)); - writer.write( - "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + //$NON-NLS-1$ - "<lint>\n"); //$NON-NLS-1$ - - if (mSuppressed.size() > 0 || mSeverity.size() > 0) { - // Process the maps in a stable sorted order such that if the - // files are checked into version control with the project, - // there are no random diffs just because hashing algorithms - // differ: - Set<String> idSet = new HashSet<String>(); - for (String id : mSuppressed.keySet()) { - idSet.add(id); - } - for (String id : mSeverity.keySet()) { - idSet.add(id); - } - List<String> ids = new ArrayList<String>(idSet); - Collections.sort(ids); - - for (String id : ids) { - writer.write(" <"); //$NON-NLS-1$ - writer.write(TAG_ISSUE); - writeAttribute(writer, ATTR_ID, id); - Severity severity = mSeverity.get(id); - if (severity != null) { - writeAttribute(writer, ATTR_SEVERITY, - severity.name().toLowerCase(Locale.US)); - } - - List<String> paths = mSuppressed.get(id); - if (paths != null && paths.size() > 0) { - writer.write('>'); - writer.write('\n'); - // The paths are already kept in sorted order when they are modified - // by ignore(...) - for (String path : paths) { - writer.write(" <"); //$NON-NLS-1$ - writer.write(TAG_IGNORE); - writeAttribute(writer, ATTR_PATH, path); - writer.write(" />\n"); //$NON-NLS-1$ - } - writer.write(" </"); //$NON-NLS-1$ - writer.write(TAG_ISSUE); - writer.write('>'); - writer.write('\n'); - } else { - writer.write(" />\n"); //$NON-NLS-1$ - } - } - } - - writer.write("</lint>"); //$NON-NLS-1$ - writer.close(); - - // Move file into place: move current version to lint.xml~ (removing the old ~ file - // if it exists), then move the new version to lint.xml. - File oldFile = new File(mConfigFile.getParentFile(), - mConfigFile.getName() + "~"); //$NON-NLS-1$ - if (oldFile.exists()) { - oldFile.delete(); - } - if (mConfigFile.exists()) { - mConfigFile.renameTo(oldFile); - } - boolean ok = file.renameTo(mConfigFile); - if (ok && oldFile.exists()) { - oldFile.delete(); - } - } catch (Exception e) { - mClient.log(e, null); - } - } - - private static void writeAttribute( - @NonNull Writer writer, @NonNull String name, @NonNull String value) - throws IOException { - writer.write(' '); - writer.write(name); - writer.write('='); - writer.write('"'); - writer.write(value); - writer.write('"'); - } - - @Override - public void ignore( - @NonNull Context context, - @NonNull Issue issue, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - // This configuration only supports suppressing warnings on a per-file basis - if (location != null) { - ignore(issue, location.getFile()); - } - } - - /** - * Marks the given issue and file combination as being ignored. - * - * @param issue the issue to be ignored in the given file - * @param file the file to ignore the issue in - */ - public void ignore(@NonNull Issue issue, @NonNull File file) { - ensureInitialized(); - - String path = mProject != null ? mProject.getRelativePath(file) : file.getPath(); - - List<String> paths = mSuppressed.get(issue.getId()); - if (paths == null) { - paths = new ArrayList<String>(); - mSuppressed.put(issue.getId(), paths); - } - paths.add(path); - - // Keep paths sorted alphabetically; makes XML output stable - Collections.sort(paths); - - if (!mBulkEditing) { - writeConfig(); - } - } - - @Override - public void setSeverity(@NonNull Issue issue, @Nullable Severity severity) { - ensureInitialized(); - - String id = issue.getId(); - if (severity == null) { - mSeverity.remove(id); - } else { - mSeverity.put(id, severity); - } - - if (!mBulkEditing) { - writeConfig(); - } - } - - @Override - public void startBulkEditing() { - mBulkEditing = true; - } - - @Override - public void finishBulkEditing() { - mBulkEditing = false; - writeConfig(); - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultSdkInfo.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultSdkInfo.java deleted file mode 100644 index b5ae26d..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultSdkInfo.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.client.api; - -import static com.android.SdkConstants.ABSOLUTE_LAYOUT; -import static com.android.SdkConstants.ABS_LIST_VIEW; -import static com.android.SdkConstants.ABS_SEEK_BAR; -import static com.android.SdkConstants.ABS_SPINNER; -import static com.android.SdkConstants.ADAPTER_VIEW; -import static com.android.SdkConstants.AUTO_COMPLETE_TEXT_VIEW; -import static com.android.SdkConstants.BUTTON; -import static com.android.SdkConstants.CHECKED_TEXT_VIEW; -import static com.android.SdkConstants.CHECK_BOX; -import static com.android.SdkConstants.COMPOUND_BUTTON; -import static com.android.SdkConstants.EDIT_TEXT; -import static com.android.SdkConstants.EXPANDABLE_LIST_VIEW; -import static com.android.SdkConstants.FRAME_LAYOUT; -import static com.android.SdkConstants.GALLERY; -import static com.android.SdkConstants.GRID_VIEW; -import static com.android.SdkConstants.HORIZONTAL_SCROLL_VIEW; -import static com.android.SdkConstants.IMAGE_BUTTON; -import static com.android.SdkConstants.IMAGE_VIEW; -import static com.android.SdkConstants.LINEAR_LAYOUT; -import static com.android.SdkConstants.LIST_VIEW; -import static com.android.SdkConstants.MULTI_AUTO_COMPLETE_TEXT_VIEW; -import static com.android.SdkConstants.PROGRESS_BAR; -import static com.android.SdkConstants.RADIO_BUTTON; -import static com.android.SdkConstants.RADIO_GROUP; -import static com.android.SdkConstants.RELATIVE_LAYOUT; -import static com.android.SdkConstants.SCROLL_VIEW; -import static com.android.SdkConstants.SEEK_BAR; -import static com.android.SdkConstants.SPINNER; -import static com.android.SdkConstants.SURFACE_VIEW; -import static com.android.SdkConstants.SWITCH; -import static com.android.SdkConstants.TABLE_LAYOUT; -import static com.android.SdkConstants.TABLE_ROW; -import static com.android.SdkConstants.TAB_HOST; -import static com.android.SdkConstants.TAB_WIDGET; -import static com.android.SdkConstants.TEXT_VIEW; -import static com.android.SdkConstants.TOGGLE_BUTTON; -import static com.android.SdkConstants.VIEW; -import static com.android.SdkConstants.VIEW_ANIMATOR; -import static com.android.SdkConstants.VIEW_GROUP; -import static com.android.SdkConstants.VIEW_PKG_PREFIX; -import static com.android.SdkConstants.VIEW_STUB; -import static com.android.SdkConstants.VIEW_SWITCHER; -import static com.android.SdkConstants.WEB_VIEW; -import static com.android.SdkConstants.WIDGET_PKG_PREFIX; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.google.common.annotations.Beta; - -import java.util.HashMap; -import java.util.Map; - -/** - * Default simple implementation of an {@link SdkInfo} - * <p> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -class DefaultSdkInfo extends SdkInfo { - @Override - @Nullable - public String getParentViewName(@NonNull String name) { - name = getRawType(name); - - return PARENTS.get(name); - } - - @Override - @Nullable - public String getParentViewClass(@NonNull String fqcn) { - int index = fqcn.lastIndexOf('.'); - if (index != -1) { - fqcn = fqcn.substring(index + 1); - } - - String parent = PARENTS.get(fqcn); - if (parent == null) { - return null; - } - // The map only stores class names internally; correct for full package paths - if (parent.equals(VIEW) || parent.equals(VIEW_GROUP) || parent.equals(SURFACE_VIEW)) { - return VIEW_PKG_PREFIX + parent; - } else { - return WIDGET_PKG_PREFIX + parent; - } - } - - @Override - public boolean isSubViewOf(@NonNull String parentType, @NonNull String childType) { - String parent = getRawType(parentType); - String child = getRawType(childType); - - // Do analysis just on non-fqcn paths - if (parent.indexOf('.') != -1) { - parent = parent.substring(parent.lastIndexOf('.') + 1); - } - if (child.indexOf('.') != -1) { - child = child.substring(child.lastIndexOf('.') + 1); - } - - if (parent.equals(VIEW)) { - return true; - } - - while (!child.equals(VIEW)) { - if (parent.equals(child)) { - return true; - } - child = PARENTS.get(child); - if (child == null) { - // Unknown view - err on the side of caution - return true; - } - } - - return false; - } - - // Strip off type parameters, e.g. AdapterView<?> => AdapterView - private static String getRawType(String type) { - if (type != null) { - int index = type.indexOf('<'); - if (index != -1) { - type = type.substring(0, index); - } - } - - return type; - } - - private static final int CLASS_COUNT = 59; - - @NonNull - private static final Map<String, String> PARENTS = new HashMap<String, String>(CLASS_COUNT); - - static { - PARENTS.put(COMPOUND_BUTTON, BUTTON); - PARENTS.put(ABS_SPINNER, ADAPTER_VIEW); - PARENTS.put(ABS_LIST_VIEW, ADAPTER_VIEW); - PARENTS.put(ABS_SEEK_BAR, ADAPTER_VIEW); - PARENTS.put(ADAPTER_VIEW, VIEW_GROUP); - PARENTS.put(VIEW_GROUP, VIEW); - - PARENTS.put(TEXT_VIEW, VIEW); - PARENTS.put(CHECKED_TEXT_VIEW, TEXT_VIEW); - PARENTS.put(RADIO_BUTTON, COMPOUND_BUTTON); - PARENTS.put(SPINNER, ABS_SPINNER); - PARENTS.put(IMAGE_BUTTON, IMAGE_VIEW); - PARENTS.put(IMAGE_VIEW, VIEW); - PARENTS.put(EDIT_TEXT, TEXT_VIEW); - PARENTS.put(PROGRESS_BAR, VIEW); - PARENTS.put(TOGGLE_BUTTON, COMPOUND_BUTTON); - PARENTS.put(VIEW_STUB, VIEW); - PARENTS.put(BUTTON, TEXT_VIEW); - PARENTS.put(SEEK_BAR, ABS_SEEK_BAR); - PARENTS.put(CHECK_BOX, COMPOUND_BUTTON); - PARENTS.put(SWITCH, COMPOUND_BUTTON); - PARENTS.put(GALLERY, ABS_SPINNER); - PARENTS.put(SURFACE_VIEW, VIEW); - PARENTS.put(ABSOLUTE_LAYOUT, VIEW_GROUP); - PARENTS.put(LINEAR_LAYOUT, VIEW_GROUP); - PARENTS.put(RELATIVE_LAYOUT, VIEW_GROUP); - PARENTS.put(LIST_VIEW, ABS_LIST_VIEW); - PARENTS.put(VIEW_SWITCHER, VIEW_ANIMATOR); - PARENTS.put(FRAME_LAYOUT, VIEW_GROUP); - PARENTS.put(HORIZONTAL_SCROLL_VIEW, FRAME_LAYOUT); - PARENTS.put(VIEW_ANIMATOR, FRAME_LAYOUT); - PARENTS.put(TAB_HOST, FRAME_LAYOUT); - PARENTS.put(TABLE_ROW, LINEAR_LAYOUT); - PARENTS.put(RADIO_GROUP, LINEAR_LAYOUT); - PARENTS.put(TAB_WIDGET, LINEAR_LAYOUT); - PARENTS.put(EXPANDABLE_LIST_VIEW, LIST_VIEW); - PARENTS.put(TABLE_LAYOUT, LINEAR_LAYOUT); - PARENTS.put(SCROLL_VIEW, FRAME_LAYOUT); - PARENTS.put(GRID_VIEW, ABS_LIST_VIEW); - PARENTS.put(WEB_VIEW, ABSOLUTE_LAYOUT); - PARENTS.put(AUTO_COMPLETE_TEXT_VIEW, EDIT_TEXT); - PARENTS.put(MULTI_AUTO_COMPLETE_TEXT_VIEW, AUTO_COMPLETE_TEXT_VIEW); - - PARENTS.put("CheckedTextView", TEXT_VIEW); //$NON-NLS-1$ - PARENTS.put("MediaController", FRAME_LAYOUT); //$NON-NLS-1$ - PARENTS.put("SlidingDrawer", VIEW_GROUP); //$NON-NLS-1$ - PARENTS.put("DialerFilter", RELATIVE_LAYOUT); //$NON-NLS-1$ - PARENTS.put("DigitalClock", TEXT_VIEW); //$NON-NLS-1$ - PARENTS.put("Chronometer", TEXT_VIEW); //$NON-NLS-1$ - PARENTS.put("ImageSwitcher", VIEW_SWITCHER); //$NON-NLS-1$ - PARENTS.put("TextSwitcher", VIEW_SWITCHER); //$NON-NLS-1$ - PARENTS.put("AnalogClock", VIEW); //$NON-NLS-1$ - PARENTS.put("TwoLineListItem", RELATIVE_LAYOUT); //$NON-NLS-1$ - PARENTS.put("ZoomControls", LINEAR_LAYOUT); //$NON-NLS-1$ - PARENTS.put("DatePicker", FRAME_LAYOUT); //$NON-NLS-1$ - PARENTS.put("TimePicker", FRAME_LAYOUT); //$NON-NLS-1$ - PARENTS.put("VideoView", SURFACE_VIEW); //$NON-NLS-1$ - PARENTS.put("ZoomButton", IMAGE_BUTTON); //$NON-NLS-1$ - PARENTS.put("RatingBar", ABS_SEEK_BAR); //$NON-NLS-1$ - PARENTS.put("ViewFlipper", VIEW_ANIMATOR); //$NON-NLS-1$ - PARENTS.put("NumberPicker", LINEAR_LAYOUT); //$NON-NLS-1$ - - assert PARENTS.size() <= CLASS_COUNT : PARENTS.size(); - - /* - // Check that all widgets lead to the root view - if (LintUtils.assertionsEnabled()) { - for (String key : PARENTS.keySet()) { - String parent = PARENTS.get(key); - if (!parent.equals(VIEW)) { - String grandParent = PARENTS.get(parent); - assert grandParent != null : parent; - } - } - } - */ - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/IDomParser.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/IDomParser.java deleted file mode 100644 index 1a70fac..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/IDomParser.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.client.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.XmlContext; -import com.google.common.annotations.Beta; - -import org.w3c.dom.Document; -import org.w3c.dom.Node; - -/** - * A wrapper for an XML parser. This allows tools integrating lint to map directly - * to builtin services, such as already-parsed data structures in XML editors. - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public interface IDomParser { - /** - * Parse the file pointed to by the given context and return as a Document - * - * @param context the context pointing to the file to be parsed, typically - * via {@link Context#getContents()} but the file handle ( - * {@link Context#file} can also be used to map to an existing - * editor buffer in the surrounding tool, etc) - * @return the parsed DOM document, or null if parsing fails - */ - @Nullable - Document parseXml(@NonNull XmlContext context); - - /** - * Returns a {@link Location} for the given DOM node - * - * @param context information about the file being parsed - * @param node the node to create a location for - * @return a location for the given node - */ - @NonNull - Location getLocation(@NonNull XmlContext context, @NonNull Node node); - - /** - * Returns a {@link Location} for the given DOM node. Like - * {@link #getLocation(XmlContext, Node)}, but allows a position range that - * is a subset of the node range. - * - * @param context information about the file being parsed - * @param node the node to create a location for - * @param start the starting position within the node, inclusive - * @param end the ending position within the node, exclusive - * @return a location for the given node - */ - @NonNull - Location getLocation(@NonNull XmlContext context, @NonNull Node node, int start, int end); - - /** - * Creates a light-weight handle to a location for the given node. It can be - * turned into a full fledged location by - * {@link com.android.tools.lint.detector.api.Location.Handle#resolve()}. - * - * @param context the context providing the node - * @param node the node (element or attribute) to create a location handle - * for - * @return a location handle - */ - @NonNull - Location.Handle createLocationHandle(@NonNull XmlContext context, @NonNull Node node); - - /** - * Dispose any data structures held for the given context. - * @param context information about the file previously parsed - * @param document the document that was parsed and is now being disposed - */ - void dispose(@NonNull XmlContext context, @NonNull Document document); -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/IJavaParser.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/IJavaParser.java deleted file mode 100644 index 9b74f16..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/IJavaParser.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.client.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.Location; - -import lombok.ast.Node; - -/** - * A wrapper for a Java parser. This allows tools integrating lint to map directly - * to builtin services, such as already-parsed data structures in Java editors. - * <p/> - * <b>NOTE: This is not a or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -public interface IJavaParser { - /** - * Parse the file pointed to by the given context. - * - * @param context the context pointing to the file to be parsed, typically - * via {@link Context#getContents()} but the file handle ( - * {@link Context#file} can also be used to map to an existing - * editor buffer in the surrounding tool, etc) - * @return the compilation unit node for the file - */ - @Nullable - Node parseJava(@NonNull JavaContext context); - - /** - * Returns a {@link Location} for the given node - * - * @param context information about the file being parsed - * @param node the node to create a location for - * @return a location for the given node - */ - @NonNull - Location getLocation(@NonNull JavaContext context, @NonNull Node node); - - /** - * Creates a light-weight handle to a location for the given node. It can be - * turned into a full fledged location by - * {@link com.android.tools.lint.detector.api.Location.Handle#resolve()}. - * - * @param context the context providing the node - * @param node the node (element or attribute) to create a location handle - * for - * @return a location handle - */ - @NonNull - Location.Handle createLocationHandle(@NonNull JavaContext context, @NonNull Node node); - - /** - * Dispose any data structures held for the given context. - * @param context information about the file previously parsed - * @param compilationUnit the compilation unit being disposed - */ - void dispose(@NonNull JavaContext context, @NonNull Node compilationUnit); -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/IssueRegistry.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/IssueRegistry.java deleted file mode 100644 index e780d79..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/IssueRegistry.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.client.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.annotations.VisibleForTesting; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.Scope; -import com.android.tools.lint.detector.api.Severity; -import com.google.common.annotations.Beta; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** Registry which provides a list of checks to be performed on an Android project - * <p> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public abstract class IssueRegistry { - private static List<Category> sCategories; - private static Map<String, Issue> sIdToIssue; - - /** - * Issue reported by lint (not a specific detector) when it cannot even - * parse an XML file prior to analysis - */ - @NonNull - public static final Issue PARSER_ERROR = Issue.create( - "ParserError", //$NON-NLS-1$ - "Finds files that contain fatal parser errors", - "Lint will ignore any files that contain fatal parsing errors. These may contain " + - "other errors, or contain code which affects issues in other files.", - Category.CORRECTNESS, - 10, - Severity.ERROR, - Detector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** - * Issue reported by lint for various other issues which prevents lint from - * running normally when it's not necessarily an error in the user's code base. - */ - @NonNull - public static final Issue LINT_ERROR = Issue.create( - "LintError", //$NON-NLS-1$ - "Isues related to running lint itself, such as failure to read files, etc", - "This issue type represents a problem running lint itself. Examples include " + - "failure to find bytecode for source files (which means certain detectors " + - "could not be run), parsing errors in lint configuration files, etc." + - "\n" + - "These errors are not errors in your own code, but they are shown to make " + - "it clear that some checks were not completed.", - - Category.LINT, - 10, - Severity.ERROR, - Detector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** - * Returns the list of issues that can be found by all known detectors. - * - * @return the list of issues to be checked (including those that may be - * disabled!) - */ - @NonNull - public abstract List<Issue> getIssues(); - - /** - * Creates a list of detectors applicable to the given cope, and with the - * given configuration. - * - * @param client the client to report errors to - * @param configuration the configuration to look up which issues are - * enabled etc from - * @param scope the scope for the analysis, to filter out detectors that - * require wider analysis than is currently being performed - * @param scopeToDetectors an optional map which (if not null) will be - * filled by this method to contain mappings from each scope to - * the applicable detectors for that scope - * @return a list of new detector instances - */ - @NonNull - final List<? extends Detector> createDetectors( - @NonNull LintClient client, - @NonNull Configuration configuration, - @NonNull EnumSet<Scope> scope, - @Nullable Map<Scope, List<Detector>> scopeToDetectors) { - List<Issue> issues = getIssues(); - Set<Class<? extends Detector>> detectorClasses = new HashSet<Class<? extends Detector>>(); - Map<Class<? extends Detector>, EnumSet<Scope>> detectorToScope = - new HashMap<Class<? extends Detector>, EnumSet<Scope>>(); - for (Issue issue : issues) { - Class<? extends Detector> detectorClass = issue.getDetectorClass(); - EnumSet<Scope> issueScope = issue.getScope(); - if (!detectorClasses.contains(detectorClass)) { - // Determine if the issue is enabled - if (!configuration.isEnabled(issue)) { - continue; - } - - // Determine if the scope matches - if (!issue.isAdequate(scope)) { - continue; - } - - detectorClass = client.replaceDetector(detectorClass); - - assert detectorClass != null : issue.getId(); - detectorClasses.add(detectorClass); - } - - if (scopeToDetectors != null) { - EnumSet<Scope> s = detectorToScope.get(detectorClass); - if (s == null) { - detectorToScope.put(detectorClass, issueScope); - } else if (!s.containsAll(issueScope)) { - EnumSet<Scope> union = EnumSet.copyOf(s); - union.addAll(issueScope); - detectorToScope.put(detectorClass, union); - } - } - } - - List<Detector> detectors = new ArrayList<Detector>(detectorClasses.size()); - for (Class<? extends Detector> clz : detectorClasses) { - try { - Detector detector = clz.newInstance(); - detectors.add(detector); - - if (scopeToDetectors != null) { - EnumSet<Scope> union = detectorToScope.get(clz); - for (Scope s : union) { - List<Detector> list = scopeToDetectors.get(s); - if (list == null) { - list = new ArrayList<Detector>(); - scopeToDetectors.put(s, list); - } - list.add(detector); - } - - } - } catch (Throwable t) { - client.log(t, "Can't initialize detector %1$s", clz.getName()); //$NON-NLS-1$ - } - } - - return detectors; - } - - /** - * Returns true if the given id represents a valid issue id - * - * @param id the id to be checked - * @return true if the given id is valid - */ - public final boolean isIssueId(@NonNull String id) { - return getIssue(id) != null; - } - - /** - * Returns true if the given category is a valid category - * - * @param name the category name to be checked - * @return true if the given string is a valid category - */ - public final boolean isCategoryName(@NonNull String name) { - for (Category c : getCategories()) { - if (c.getName().equals(name) || c.getFullName().equals(name)) { - return true; - } - } - - return false; - } - - /** - * Returns the available categories - * - * @return an iterator for all the categories, never null - */ - @NonNull - public List<Category> getCategories() { - if (sCategories == null) { - final Set<Category> categories = new HashSet<Category>(); - for (Issue issue : getIssues()) { - categories.add(issue.getCategory()); - } - List<Category> sorted = new ArrayList<Category>(categories); - Collections.sort(sorted); - sCategories = Collections.unmodifiableList(sorted); - } - - return sCategories; - } - - /** - * Returns the issue for the given id, or null if it's not a valid id - * - * @param id the id to be checked - * @return the corresponding issue, or null - */ - @Nullable - public final Issue getIssue(@NonNull String id) { - if (sIdToIssue == null) { - List<Issue> issues = getIssues(); - sIdToIssue = new HashMap<String, Issue>(issues.size()); - for (Issue issue : issues) { - sIdToIssue.put(issue.getId(), issue); - } - - sIdToIssue.put(PARSER_ERROR.getId(), PARSER_ERROR); - sIdToIssue.put(LINT_ERROR.getId(), LINT_ERROR); - } - return sIdToIssue.get(id); - } - - /** - * Reset the registry such that it recomputes its available issues. - * <p> - * NOTE: This is only intended for testing purposes. - */ - @VisibleForTesting - protected static void reset() { - sIdToIssue = null; - sCategories = null; - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/JavaVisitor.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/JavaVisitor.java deleted file mode 100644 index b1d8832..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/JavaVisitor.java +++ /dev/null @@ -1,1192 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.client.api; - -import static com.android.SdkConstants.ANDROID_PKG; -import static com.android.SdkConstants.R_CLASS; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Detector.JavaScanner; -import com.android.tools.lint.detector.api.Detector.XmlScanner; -import com.android.tools.lint.detector.api.JavaContext; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import lombok.ast.AlternateConstructorInvocation; -import lombok.ast.Annotation; -import lombok.ast.AnnotationDeclaration; -import lombok.ast.AnnotationElement; -import lombok.ast.AnnotationMethodDeclaration; -import lombok.ast.AnnotationValueArray; -import lombok.ast.ArrayAccess; -import lombok.ast.ArrayCreation; -import lombok.ast.ArrayDimension; -import lombok.ast.ArrayInitializer; -import lombok.ast.Assert; -import lombok.ast.AstVisitor; -import lombok.ast.BinaryExpression; -import lombok.ast.Block; -import lombok.ast.BooleanLiteral; -import lombok.ast.Break; -import lombok.ast.Case; -import lombok.ast.Cast; -import lombok.ast.Catch; -import lombok.ast.CharLiteral; -import lombok.ast.ClassDeclaration; -import lombok.ast.ClassLiteral; -import lombok.ast.Comment; -import lombok.ast.CompilationUnit; -import lombok.ast.ConstructorDeclaration; -import lombok.ast.ConstructorInvocation; -import lombok.ast.Continue; -import lombok.ast.Default; -import lombok.ast.DoWhile; -import lombok.ast.EmptyDeclaration; -import lombok.ast.EmptyStatement; -import lombok.ast.EnumConstant; -import lombok.ast.EnumDeclaration; -import lombok.ast.EnumTypeBody; -import lombok.ast.Expression; -import lombok.ast.ExpressionStatement; -import lombok.ast.FloatingPointLiteral; -import lombok.ast.For; -import lombok.ast.ForEach; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.Identifier; -import lombok.ast.If; -import lombok.ast.ImportDeclaration; -import lombok.ast.InlineIfExpression; -import lombok.ast.InstanceInitializer; -import lombok.ast.InstanceOf; -import lombok.ast.IntegralLiteral; -import lombok.ast.InterfaceDeclaration; -import lombok.ast.KeywordModifier; -import lombok.ast.LabelledStatement; -import lombok.ast.MethodDeclaration; -import lombok.ast.MethodInvocation; -import lombok.ast.Modifiers; -import lombok.ast.Node; -import lombok.ast.NormalTypeBody; -import lombok.ast.NullLiteral; -import lombok.ast.PackageDeclaration; -import lombok.ast.Return; -import lombok.ast.Select; -import lombok.ast.StaticInitializer; -import lombok.ast.StringLiteral; -import lombok.ast.Super; -import lombok.ast.SuperConstructorInvocation; -import lombok.ast.Switch; -import lombok.ast.Synchronized; -import lombok.ast.This; -import lombok.ast.Throw; -import lombok.ast.Try; -import lombok.ast.TypeReference; -import lombok.ast.TypeReferencePart; -import lombok.ast.TypeVariable; -import lombok.ast.UnaryExpression; -import lombok.ast.VariableDeclaration; -import lombok.ast.VariableDefinition; -import lombok.ast.VariableDefinitionEntry; -import lombok.ast.VariableReference; -import lombok.ast.While; - - -/** - * Specialized visitor for running detectors on a Java AST. - * It operates in three phases: - * <ol> - * <li> First, it computes a set of maps where it generates a map from each - * significant AST attribute (such as method call names) to a list - * of detectors to consult whenever that attribute is encountered. - * Examples of "attributes" are method names, Android resource identifiers, - * and general AST node types such as "cast" nodes etc. These are - * defined on the {@link JavaScanner} interface. - * <li> Second, it iterates over the document a single time, delegating to - * the detectors found at each relevant AST attribute. - * <li> Finally, it calls the remaining visitors (those that need to process a - * whole document on their own). - * </ol> - * It also notifies all the detectors before and after the document is processed - * such that they can do pre- and post-processing. - */ -public class JavaVisitor { - /** Default size of lists holding detectors of the same type for a given node type */ - private static final int SAME_TYPE_COUNT = 8; - - private final Map<String, List<VisitingDetector>> mMethodDetectors = - new HashMap<String, List<VisitingDetector>>(); - private final List<VisitingDetector> mResourceFieldDetectors = - new ArrayList<VisitingDetector>(); - private final List<VisitingDetector> mAllDetectors; - private final List<VisitingDetector> mFullTreeDetectors; - private Map<Class<? extends Node>, List<VisitingDetector>> mNodeTypeDetectors = - new HashMap<Class<? extends Node>, List<VisitingDetector>>(); - private final IJavaParser mParser; - - JavaVisitor(@NonNull IJavaParser parser, @NonNull List<Detector> detectors) { - mParser = parser; - mAllDetectors = new ArrayList<VisitingDetector>(detectors.size()); - mFullTreeDetectors = new ArrayList<VisitingDetector>(detectors.size()); - - for (Detector detector : detectors) { - VisitingDetector v = new VisitingDetector(detector, (JavaScanner) detector); - mAllDetectors.add(v); - - List<Class<? extends Node>> nodeTypes = detector.getApplicableNodeTypes(); - if (nodeTypes != null) { - for (Class<? extends Node> type : nodeTypes) { - List<VisitingDetector> list = mNodeTypeDetectors.get(type); - if (list == null) { - list = new ArrayList<VisitingDetector>(SAME_TYPE_COUNT); - mNodeTypeDetectors.put(type, list); - } - list.add(v); - } - } - - List<String> names = detector.getApplicableMethodNames(); - if (names != null) { - // not supported in Java visitors; adding a method invocation node is trivial - // for that case. - assert names != XmlScanner.ALL; - - for (String name : names) { - List<VisitingDetector> list = mMethodDetectors.get(name); - if (list == null) { - list = new ArrayList<VisitingDetector>(SAME_TYPE_COUNT); - mMethodDetectors.put(name, list); - } - list.add(v); - } - } - - if (detector.appliesToResourceRefs()) { - mResourceFieldDetectors.add(v); - } else if ((names == null || names.size() == 0) - && (nodeTypes == null || nodeTypes.size() ==0)) { - mFullTreeDetectors.add(v); - } - } - } - - void visitFile(@NonNull JavaContext context, @NonNull File file) { - context.parser = mParser; - - Node compilationUnit = null; - try { - compilationUnit = mParser.parseJava(context); - if (compilationUnit == null) { - // No need to log this; the parser should be reporting - // a full warning (such as IssueRegistry#PARSER_ERROR) - // with details, location, etc. - return; - } - context.compilationUnit = compilationUnit; - - for (VisitingDetector v : mAllDetectors) { - v.setContext(context); - v.getDetector().beforeCheckFile(context); - } - - for (VisitingDetector v : mFullTreeDetectors) { - AstVisitor visitor = v.getVisitor(); - if (visitor != null) { - compilationUnit.accept(visitor); - } - } - - if (mMethodDetectors.size() > 0 || mResourceFieldDetectors.size() > 0) { - AstVisitor visitor = new DelegatingJavaVisitor(context); - compilationUnit.accept(visitor); - } else if (mNodeTypeDetectors.size() > 0) { - AstVisitor visitor = new DispatchVisitor(); - compilationUnit.accept(visitor); - } - - for (VisitingDetector v : mAllDetectors) { - v.getDetector().afterCheckFile(context); - } - } finally { - if (compilationUnit != null) { - mParser.dispose(context, compilationUnit); - } - } - } - - private static class VisitingDetector { - private AstVisitor mVisitor; // construct lazily, and clear out on context switch! - private JavaContext mContext; - public final Detector mDetector; - public final JavaScanner mJavaScanner; - - public VisitingDetector(@NonNull Detector detector, @NonNull JavaScanner javaScanner) { - mDetector = detector; - mJavaScanner = javaScanner; - } - - @NonNull - public Detector getDetector() { - return mDetector; - } - - @NonNull - public JavaScanner getJavaScanner() { - return mJavaScanner; - } - - public void setContext(@NonNull JavaContext context) { - mContext = context; - - // The visitors are one-per-context, so clear them out here and construct - // lazily only if needed - mVisitor = null; - } - - @NonNull - AstVisitor getVisitor() { - if (mVisitor == null) { - mVisitor = mDetector.createJavaVisitor(mContext); - if (mVisitor == null) { - mVisitor = new ForwardingAstVisitor() { - }; - } - } - return mVisitor; - } - } - - /** - * Generic dispatcher which visits all nodes (once) and dispatches to - * specific visitors for each node. Each visitor typically only wants to - * look at a small part of a tree, such as a method call or a class - * declaration, so this means we avoid visiting all "uninteresting" nodes in - * the tree repeatedly. - */ - private class DispatchVisitor extends AstVisitor { - @Override - public void endVisit(Node node) { - } - - @Override - public boolean visitAlternateConstructorInvocation(AlternateConstructorInvocation node) { - List<VisitingDetector> list = - mNodeTypeDetectors.get(AlternateConstructorInvocation.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitAlternateConstructorInvocation(node); - } - } - return false; - } - - @Override - public boolean visitAnnotation(Annotation node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Annotation.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitAnnotation(node); - } - } - return false; - } - - @Override - public boolean visitAnnotationDeclaration(AnnotationDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(AnnotationDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitAnnotationDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitAnnotationElement(AnnotationElement node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(AnnotationElement.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitAnnotationElement(node); - } - } - return false; - } - - @Override - public boolean visitAnnotationMethodDeclaration(AnnotationMethodDeclaration node) { - List<VisitingDetector> list = - mNodeTypeDetectors.get(AnnotationMethodDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitAnnotationMethodDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitAnnotationValueArray(AnnotationValueArray node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(AnnotationValueArray.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitAnnotationValueArray(node); - } - } - return false; - } - - @Override - public boolean visitArrayAccess(ArrayAccess node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ArrayAccess.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitArrayAccess(node); - } - } - return false; - } - - @Override - public boolean visitArrayCreation(ArrayCreation node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ArrayCreation.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitArrayCreation(node); - } - } - return false; - } - - @Override - public boolean visitArrayDimension(ArrayDimension node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ArrayDimension.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitArrayDimension(node); - } - } - return false; - } - - @Override - public boolean visitArrayInitializer(ArrayInitializer node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ArrayInitializer.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitArrayInitializer(node); - } - } - return false; - } - - @Override - public boolean visitAssert(Assert node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Assert.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitAssert(node); - } - } - return false; - } - - @Override - public boolean visitBinaryExpression(BinaryExpression node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(BinaryExpression.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitBinaryExpression(node); - } - } - return false; - } - - @Override - public boolean visitBlock(Block node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Block.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitBlock(node); - } - } - return false; - } - - @Override - public boolean visitBooleanLiteral(BooleanLiteral node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(BooleanLiteral.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitBooleanLiteral(node); - } - } - return false; - } - - @Override - public boolean visitBreak(Break node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Break.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitBreak(node); - } - } - return false; - } - - @Override - public boolean visitCase(Case node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Case.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitCase(node); - } - } - return false; - } - - @Override - public boolean visitCast(Cast node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Cast.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitCast(node); - } - } - return false; - } - - @Override - public boolean visitCatch(Catch node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Catch.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitCatch(node); - } - } - return false; - } - - @Override - public boolean visitCharLiteral(CharLiteral node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(CharLiteral.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitCharLiteral(node); - } - } - return false; - } - - @Override - public boolean visitClassDeclaration(ClassDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ClassDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitClassDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitClassLiteral(ClassLiteral node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ClassLiteral.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitClassLiteral(node); - } - } - return false; - } - - @Override - public boolean visitComment(Comment node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Comment.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitComment(node); - } - } - return false; - } - - @Override - public boolean visitCompilationUnit(CompilationUnit node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(CompilationUnit.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitCompilationUnit(node); - } - } - return false; - } - - @Override - public boolean visitConstructorDeclaration(ConstructorDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ConstructorDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitConstructorDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitConstructorInvocation(ConstructorInvocation node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ConstructorInvocation.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitConstructorInvocation(node); - } - } - return false; - } - - @Override - public boolean visitContinue(Continue node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Continue.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitContinue(node); - } - } - return false; - } - - @Override - public boolean visitDefault(Default node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Default.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitDefault(node); - } - } - return false; - } - - @Override - public boolean visitDoWhile(DoWhile node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(DoWhile.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitDoWhile(node); - } - } - return false; - } - - @Override - public boolean visitEmptyDeclaration(EmptyDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(EmptyDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitEmptyDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitEmptyStatement(EmptyStatement node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(EmptyStatement.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitEmptyStatement(node); - } - } - return false; - } - - @Override - public boolean visitEnumConstant(EnumConstant node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(EnumConstant.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitEnumConstant(node); - } - } - return false; - } - - @Override - public boolean visitEnumDeclaration(EnumDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(EnumDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitEnumDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitEnumTypeBody(EnumTypeBody node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(EnumTypeBody.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitEnumTypeBody(node); - } - } - return false; - } - - @Override - public boolean visitExpressionStatement(ExpressionStatement node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ExpressionStatement.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitExpressionStatement(node); - } - } - return false; - } - - @Override - public boolean visitFloatingPointLiteral(FloatingPointLiteral node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(FloatingPointLiteral.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitFloatingPointLiteral(node); - } - } - return false; - } - - @Override - public boolean visitFor(For node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(For.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitFor(node); - } - } - return false; - } - - @Override - public boolean visitForEach(ForEach node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ForEach.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitForEach(node); - } - } - return false; - } - - @Override - public boolean visitIdentifier(Identifier node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Identifier.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitIdentifier(node); - } - } - return false; - } - - @Override - public boolean visitIf(If node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(If.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitIf(node); - } - } - return false; - } - - @Override - public boolean visitImportDeclaration(ImportDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ImportDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitImportDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitInlineIfExpression(InlineIfExpression node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(InlineIfExpression.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitInlineIfExpression(node); - } - } - return false; - } - - @Override - public boolean visitInstanceInitializer(InstanceInitializer node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(InstanceInitializer.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitInstanceInitializer(node); - } - } - return false; - } - - @Override - public boolean visitInstanceOf(InstanceOf node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(InstanceOf.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitInstanceOf(node); - } - } - return false; - } - - @Override - public boolean visitIntegralLiteral(IntegralLiteral node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(IntegralLiteral.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitIntegralLiteral(node); - } - } - return false; - } - - @Override - public boolean visitInterfaceDeclaration(InterfaceDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(InterfaceDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitInterfaceDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitKeywordModifier(KeywordModifier node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(KeywordModifier.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitKeywordModifier(node); - } - } - return false; - } - - @Override - public boolean visitLabelledStatement(LabelledStatement node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(LabelledStatement.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitLabelledStatement(node); - } - } - return false; - } - - @Override - public boolean visitMethodDeclaration(MethodDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(MethodDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitMethodDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitMethodInvocation(MethodInvocation node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(MethodInvocation.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitMethodInvocation(node); - } - } - return false; - } - - @Override - public boolean visitModifiers(Modifiers node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Modifiers.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitModifiers(node); - } - } - return false; - } - - @Override - public boolean visitNormalTypeBody(NormalTypeBody node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(NormalTypeBody.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitNormalTypeBody(node); - } - } - return false; - } - - @Override - public boolean visitNullLiteral(NullLiteral node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(NullLiteral.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitNullLiteral(node); - } - } - return false; - } - - @Override - public boolean visitPackageDeclaration(PackageDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(PackageDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitPackageDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitParseArtefact(Node node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Node.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitParseArtefact(node); - } - } - return false; - } - - @Override - public boolean visitReturn(Return node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Return.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitReturn(node); - } - } - return false; - } - - @Override - public boolean visitSelect(Select node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Select.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitSelect(node); - } - } - return false; - } - - @Override - public boolean visitStaticInitializer(StaticInitializer node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(StaticInitializer.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitStaticInitializer(node); - } - } - return false; - } - - @Override - public boolean visitStringLiteral(StringLiteral node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(StringLiteral.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitStringLiteral(node); - } - } - return false; - } - - @Override - public boolean visitSuper(Super node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Super.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitSuper(node); - } - } - return false; - } - - @Override - public boolean visitSuperConstructorInvocation(SuperConstructorInvocation node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(SuperConstructorInvocation.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitSuperConstructorInvocation(node); - } - } - return false; - } - - @Override - public boolean visitSwitch(Switch node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Switch.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitSwitch(node); - } - } - return false; - } - - @Override - public boolean visitSynchronized(Synchronized node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Synchronized.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitSynchronized(node); - } - } - return false; - } - - @Override - public boolean visitThis(This node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(This.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitThis(node); - } - } - return false; - } - - @Override - public boolean visitThrow(Throw node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Throw.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitThrow(node); - } - } - return false; - } - - @Override - public boolean visitTry(Try node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Try.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitTry(node); - } - } - return false; - } - - @Override - public boolean visitTypeReference(TypeReference node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(TypeReference.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitTypeReference(node); - } - } - return false; - } - - @Override - public boolean visitTypeReferencePart(TypeReferencePart node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(TypeReferencePart.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitTypeReferencePart(node); - } - } - return false; - } - - @Override - public boolean visitTypeVariable(TypeVariable node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(TypeVariable.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitTypeVariable(node); - } - } - return false; - } - - @Override - public boolean visitUnaryExpression(UnaryExpression node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(UnaryExpression.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitUnaryExpression(node); - } - } - return false; - } - - @Override - public boolean visitVariableDeclaration(VariableDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(VariableDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitVariableDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitVariableDefinition(VariableDefinition node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(VariableDefinition.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitVariableDefinition(node); - } - } - return false; - } - - @Override - public boolean visitVariableDefinitionEntry(VariableDefinitionEntry node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(VariableDefinitionEntry.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitVariableDefinitionEntry(node); - } - } - return false; - } - - @Override - public boolean visitVariableReference(VariableReference node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(VariableReference.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitVariableReference(node); - } - } - return false; - } - - @Override - public boolean visitWhile(While node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(While.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitWhile(node); - } - } - return false; - } - } - - /** Performs common AST searches for method calls and R-type-field references. - * Note that this is a specialized form of the {@link DispatchVisitor}. */ - private class DelegatingJavaVisitor extends DispatchVisitor { - private final JavaContext mContext; - private final boolean mVisitResources; - private final boolean mVisitMethods; - - public DelegatingJavaVisitor(JavaContext context) { - mContext = context; - - mVisitMethods = mMethodDetectors.size() > 0; - mVisitResources = mResourceFieldDetectors.size() > 0; - } - - @Override - public boolean visitSelect(Select node) { - if (mVisitResources) { - // R.type.name - if (node.astOperand() instanceof Select) { - Select select = (Select) node.astOperand(); - if (select.astOperand() instanceof VariableReference) { - VariableReference reference = (VariableReference) select.astOperand(); - if (reference.astIdentifier().astValue().equals(R_CLASS)) { - String type = select.astIdentifier().astValue(); - String name = node.astIdentifier().astValue(); - - // R -could- be referenced locally and really have been - // imported as "import android.R;" in the import statements, - // but this is not recommended (and in fact there's a specific - // lint rule warning against it) - boolean isFramework = false; - - for (VisitingDetector v : mResourceFieldDetectors) { - JavaScanner detector = v.getJavaScanner(); - detector.visitResourceReference(mContext, v.getVisitor(), - node, type, name, isFramework); - } - - return super.visitSelect(node); - } - } - } - - // Arbitrary packages -- android.R.type.name, foo.bar.R.type.name - if (node.astIdentifier().astValue().equals(R_CLASS)) { - Node parent = node.getParent(); - if (parent instanceof Select) { - Node grandParent = parent.getParent(); - if (grandParent instanceof Select) { - Select select = (Select) grandParent; - String name = select.astIdentifier().astValue(); - Expression typeOperand = select.astOperand(); - if (typeOperand instanceof Select) { - Select typeSelect = (Select) typeOperand; - String type = typeSelect.astIdentifier().astValue(); - boolean isFramework = - node.astIdentifier().astValue().equals(ANDROID_PKG); - for (VisitingDetector v : mResourceFieldDetectors) { - JavaScanner detector = v.getJavaScanner(); - detector.visitResourceReference(mContext, v.getVisitor(), - node, type, name, isFramework); - } - } - } - } - } - } - - return super.visitSelect(node); - } - - @Override - public boolean visitMethodInvocation(MethodInvocation node) { - if (mVisitMethods) { - String methodName = node.astName().getDescription(); - List<VisitingDetector> list = mMethodDetectors.get(methodName); - if (list != null) { - for (VisitingDetector v : list) { - v.getJavaScanner().visitMethod(mContext, v.getVisitor(), node); - } - } - } - - return super.visitMethodInvocation(node); - } - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintClient.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintClient.java deleted file mode 100644 index c15b284..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintClient.java +++ /dev/null @@ -1,602 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.client.api; - -import static com.android.SdkConstants.CLASS_FOLDER; -import static com.android.SdkConstants.DOT_JAR; -import static com.android.SdkConstants.GEN_FOLDER; -import static com.android.SdkConstants.LIBS_FOLDER; -import static com.android.SdkConstants.SRC_FOLDER; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.SdkManager; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Project; -import com.android.tools.lint.detector.api.Severity; -import com.android.utils.StdLogger; -import com.android.utils.StdLogger.Level; -import com.google.common.annotations.Beta; -import com.google.common.collect.Maps; -import com.google.common.io.Files; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; - -import java.io.File; -import java.io.IOException; -import java.io.StringReader; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -/** - * Information about the tool embedding the lint analyzer. IDEs and other tools - * implementing lint support will extend this to integrate logging, displaying errors, - * etc. - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public abstract class LintClient { - private static final String PROP_BIN_DIR = "com.android.tools.lint.bindir"; //$NON-NLS-1$ - - /** - * Returns a configuration for use by the given project. The configuration - * provides information about which issues are enabled, any customizations - * to the severity of an issue, etc. - * <p> - * By default this method returns a {@link DefaultConfiguration}. - * - * @param project the project to obtain a configuration for - * @return a configuration, never null. - */ - public Configuration getConfiguration(@NonNull Project project) { - return DefaultConfiguration.create(this, project, null); - } - - /** - * Report the given issue. This method will only be called if the configuration - * provided by {@link #getConfiguration(Project)} has reported the corresponding - * issue as enabled and has not filtered out the issue with its - * {@link Configuration#ignore(Context, Issue, Location, String, Object)} method. - * <p> - * - * @param context the context used by the detector when the issue was found - * @param issue the issue that was found - * @param severity the severity of the issue - * @param location the location of the issue - * @param message the associated user message - * @param data optional extra data for a discovered issue, or null. The - * content depends on the specific issue. Detectors can pass - * extra info here which automatic fix tools etc can use to - * extract relevant information instead of relying on parsing the - * error message text. See each detector for details on which - * data if any is supplied for a given issue. - */ - public abstract void report( - @NonNull Context context, - @NonNull Issue issue, - @NonNull Severity severity, - @Nullable Location location, - @NonNull String message, - @Nullable Object data); - - /** - * Send an exception or error message (with warning severity) to the log - * - * @param exception the exception, possibly null - * @param format the error message using {@link String#format} syntax, possibly null - * (though in that case the exception should not be null) - * @param args any arguments for the format string - */ - public void log( - @Nullable Throwable exception, - @Nullable String format, - @Nullable Object... args) { - log(Severity.WARNING, exception, format, args); - } - - /** - * Send an exception or error message to the log - * - * @param severity the severity of the warning - * @param exception the exception, possibly null - * @param format the error message using {@link String#format} syntax, possibly null - * (though in that case the exception should not be null) - * @param args any arguments for the format string - */ - public abstract void log( - @NonNull Severity severity, - @Nullable Throwable exception, - @Nullable String format, - @Nullable Object... args); - - /** - * Returns a {@link IDomParser} to use to parse XML - * - * @return a new {@link IDomParser}, or null if this client does not support - * XML analysis - */ - @Nullable - public abstract IDomParser getDomParser(); - - /** - * Returns a {@link IJavaParser} to use to parse Java - * - * @return a new {@link IJavaParser}, or null if this client does not - * support Java analysis - */ - @Nullable - public abstract IJavaParser getJavaParser(); - - /** - * Returns an optimal detector, if applicable. By default, just returns the - * original detector, but tools can replace detectors using this hook with a version - * that takes advantage of native capabilities of the tool. - * - * @param detectorClass the class of the detector to be replaced - * @return the new detector class, or just the original detector (not null) - */ - @NonNull - public Class<? extends Detector> replaceDetector( - @NonNull Class<? extends Detector> detectorClass) { - return detectorClass; - } - - /** - * Reads the given text file and returns the content as a string - * - * @param file the file to read - * @return the string to return, never null (will be empty if there is an - * I/O error) - */ - @NonNull - public abstract String readFile(@NonNull File file); - - /** - * Reads the given binary file and returns the content as a byte array. - * By default this method will read the bytes from the file directly, - * but this can be customized by a client if for example I/O could be - * held in memory and not flushed to disk yet. - * - * @param file the file to read - * @return the bytes in the file, never null - * @throws IOException if the file does not exist, or if the file cannot be - * read for some reason - */ - @NonNull - public byte[] readBytes(@NonNull File file) throws IOException { - return Files.toByteArray(file); - } - - /** - * Returns the list of source folders for Java source files - * - * @param project the project to look up Java source file locations for - * @return a list of source folders to search for .java files - */ - @NonNull - public List<File> getJavaSourceFolders(@NonNull Project project) { - return getClassPath(project).getSourceFolders(); - } - - /** - * Returns the list of output folders for class files - * - * @param project the project to look up class file locations for - * @return a list of output folders to search for .class files - */ - @NonNull - public List<File> getJavaClassFolders(@NonNull Project project) { - return getClassPath(project).getClassFolders(); - - } - - /** - * Returns the list of Java libraries - * - * @param project the project to look up jar dependencies for - * @return a list of jar dependencies containing .class files - */ - @NonNull - public List<File> getJavaLibraries(@NonNull Project project) { - return getClassPath(project).getLibraries(); - } - - /** - * Returns the {@link SdkInfo} to use for the given project. - * - * @param project the project to look up an {@link SdkInfo} for - * @return an {@link SdkInfo} for the project - */ - @NonNull - public SdkInfo getSdkInfo(@NonNull Project project) { - // By default no per-platform SDK info - return new DefaultSdkInfo(); - } - - /** - * Returns a suitable location for storing cache files. Note that the - * directory may not exist. - * - * @param create if true, attempt to create the cache dir if it does not - * exist - * @return a suitable location for storing cache files, which may be null if - * the create flag was false, or if for some reason the directory - * could not be created - */ - @Nullable - public File getCacheDir(boolean create) { - String home = System.getProperty("user.home"); - String relative = ".android" + File.separator + "cache"; //$NON-NLS-1$ //$NON-NLS-2$ - File dir = new File(home, relative); - if (create && !dir.exists()) { - if (!dir.mkdirs()) { - return null; - } - } - return dir; - } - - /** - * Returns the File corresponding to the system property or the environment variable - * for {@link #PROP_BIN_DIR}. - * This property is typically set by the SDK/tools/lint[.bat] wrapper. - * It denotes the path of the wrapper on disk. - * - * @return A new File corresponding to {@link LintClient#PROP_BIN_DIR} or null. - */ - @Nullable - private File getLintBinDir() { - // First check the Java properties (e.g. set using "java -jar ... -Dname=value") - String path = System.getProperty(PROP_BIN_DIR); - if (path == null || path.length() == 0) { - // If not found, check environment variables. - path = System.getenv(PROP_BIN_DIR); - } - if (path != null && path.length() > 0) { - return new File(path); - } - return null; - } - - /** - * Returns the File pointing to the user's SDK install area. This is generally - * the root directory containing the lint tool (but also platforms/ etc). - * - * @return a file pointing to the user's install area - */ - @Nullable - public File getSdkHome() { - File binDir = getLintBinDir(); - if (binDir != null) { - assert binDir.getName().equals("tools"); - - File root = binDir.getParentFile(); - if (root != null && root.isDirectory()) { - return root; - } - } - - String home = System.getenv("ANDROID_HOME"); //$NON-NLS-1$ - if (home != null) { - return new File(home); - } - - return null; - } - - /** - * Locates an SDK resource (relative to the SDK root directory). - * <p> - * TODO: Consider switching to a {@link URL} return type instead. - * - * @param relativePath A relative path (using {@link File#separator} to - * separate path components) to the given resource - * @return a {@link File} pointing to the resource, or null if it does not - * exist - */ - @Nullable - public File findResource(@NonNull String relativePath) { - File dir = getLintBinDir(); - if (dir == null) { - throw new IllegalArgumentException("Lint must be invoked with the System property " - + PROP_BIN_DIR + " pointing to the ANDROID_SDK tools directory"); - } - - File top = dir.getParentFile(); - File file = new File(top, relativePath); - if (file.exists()) { - return file; - } else { - return null; - } - } - - private Map<Project, ClassPathInfo> mProjectInfo; - - /** - * Information about class paths (sources, class files and libraries) - * usually associated with a project. - */ - protected static class ClassPathInfo { - private final List<File> mClassFolders; - private final List<File> mSourceFolders; - private final List<File> mLibraries; - - public ClassPathInfo( - @NonNull List<File> sourceFolders, - @NonNull List<File> classFolders, - @NonNull List<File> libraries) { - mSourceFolders = sourceFolders; - mClassFolders = classFolders; - mLibraries = libraries; - } - - @NonNull - public List<File> getSourceFolders() { - return mSourceFolders; - } - - @NonNull - public List<File> getClassFolders() { - return mClassFolders; - } - - @NonNull - public List<File> getLibraries() { - return mLibraries; - } - } - - /** - * Considers the given project as an Eclipse project and returns class path - * information for the project - the source folder(s), the output folder and - * any libraries. - * <p> - * Callers will not cache calls to this method, so if it's expensive to compute - * the classpath info, this method should perform its own caching. - * - * @param project the project to look up class path info for - * @return a class path info object, never null - */ - @NonNull - protected ClassPathInfo getClassPath(@NonNull Project project) { - ClassPathInfo info; - if (mProjectInfo == null) { - mProjectInfo = Maps.newHashMap(); - info = null; - } else { - info = mProjectInfo.get(project); - } - - if (info == null) { - List<File> sources = new ArrayList<File>(2); - List<File> classes = new ArrayList<File>(1); - List<File> libraries = new ArrayList<File>(); - - File projectDir = project.getDir(); - File classpathFile = new File(projectDir, ".classpath"); //$NON-NLS-1$ - if (classpathFile.exists()) { - String classpathXml = readFile(classpathFile); - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - InputSource is = new InputSource(new StringReader(classpathXml)); - factory.setNamespaceAware(false); - factory.setValidating(false); - try { - DocumentBuilder builder = factory.newDocumentBuilder(); - Document document = builder.parse(is); - NodeList tags = document.getElementsByTagName("classpathentry"); //$NON-NLS-1$ - for (int i = 0, n = tags.getLength(); i < n; i++) { - Element element = (Element) tags.item(i); - String kind = element.getAttribute("kind"); //$NON-NLS-1$ - List<File> addTo = null; - if (kind.equals("src")) { //$NON-NLS-1$ - addTo = sources; - } else if (kind.equals("output")) { //$NON-NLS-1$ - addTo = classes; - } else if (kind.equals("lib")) { //$NON-NLS-1$ - addTo = libraries; - } - if (addTo != null) { - String path = element.getAttribute("path"); //$NON-NLS-1$ - File folder = new File(projectDir, path); - if (folder.exists()) { - addTo.add(folder); - } - } - } - } catch (Exception e) { - log(null, null); - } - } - - // Add in libraries that aren't specified in the .classpath file - File libs = new File(project.getDir(), LIBS_FOLDER); - if (libs.isDirectory()) { - File[] jars = libs.listFiles(); - if (jars != null) { - for (File jar : jars) { - if (LintUtils.endsWith(jar.getPath(), DOT_JAR) - && !libraries.contains(jar)) { - libraries.add(jar); - } - } - } - } - - if (classes.size() == 0) { - File folder = new File(projectDir, CLASS_FOLDER); - if (folder.exists()) { - classes.add(folder); - } else { - // Maven checks - folder = new File(projectDir, - "target" + File.separator + "classes"); //$NON-NLS-1$ //$NON-NLS-2$ - if (folder.exists()) { - classes.add(folder); - - // If it's maven, also correct the source path, "src" works but - // it's in a more specific subfolder - if (sources.size() == 0) { - File src = new File(projectDir, - "src" + File.separator //$NON-NLS-1$ - + "main" + File.separator //$NON-NLS-1$ - + "java"); //$NON-NLS-1$ - if (src.exists()) { - sources.add(src); - } else { - src = new File(projectDir, SRC_FOLDER); - if (src.exists()) { - sources.add(src); - } - } - - File gen = new File(projectDir, - "target" + File.separator //$NON-NLS-1$ - + "generated-sources" + File.separator //$NON-NLS-1$ - + "r"); //$NON-NLS-1$ - if (gen.exists()) { - sources.add(gen); - } - } - } - } - } - - // Fallback, in case there is no Eclipse project metadata here - if (sources.size() == 0) { - File src = new File(projectDir, SRC_FOLDER); - if (src.exists()) { - sources.add(src); - } - File gen = new File(projectDir, GEN_FOLDER); - if (gen.exists()) { - sources.add(gen); - } - } - - info = new ClassPathInfo(sources, classes, libraries); - mProjectInfo.put(project, info); - } - - return info; - } - - /** - * A map from directory to existing projects, or null. Used to ensure that - * projects are unique for a directory (in case we process a library project - * before its including project for example) - */ - private Map<File, Project> mDirToProject; - - /** - * Returns a project for the given directory. This should return the same - * project for the same directory if called repeatedly. - * - * @param dir the directory containing the project - * @param referenceDir See {@link Project#getReferenceDir()}. - * @return a project, never null - */ - @NonNull - public Project getProject(@NonNull File dir, @NonNull File referenceDir) { - if (mDirToProject == null) { - mDirToProject = new HashMap<File, Project>(); - } - - File canonicalDir = dir; - try { - // Attempt to use the canonical handle for the file, in case there - // are symlinks etc present (since when handling library projects, - // we also call getCanonicalFile to compute the result of appending - // relative paths, which can then resolve symlinks and end up with - // a different prefix) - canonicalDir = dir.getCanonicalFile(); - } catch (IOException ioe) { - // pass - } - - Project project = mDirToProject.get(canonicalDir); - if (project != null) { - return project; - } - - - project = Project.create(this, dir, referenceDir); - mDirToProject.put(canonicalDir, project); - return project; - } - - private IAndroidTarget[] mTargets; - - /** - * Returns all the {@link IAndroidTarget} versions installed in the user's SDK install - * area. - * - * @return all the installed targets - */ - @NonNull - public IAndroidTarget[] getTargets() { - if (mTargets == null) { - File sdkHome = getSdkHome(); - if (sdkHome != null) { - StdLogger log = new StdLogger(Level.WARNING); - SdkManager manager = SdkManager.createManager(sdkHome.getPath(), log); - mTargets = manager.getTargets(); - } else { - mTargets = new IAndroidTarget[0]; - } - } - - return mTargets; - } - - /** - * Returns the highest known API level. - * - * @return the highest known API level - */ - public int getHighestKnownApiLevel() { - int max = SdkConstants.HIGHEST_KNOWN_API; - - for (IAndroidTarget target : getTargets()) { - if (target.isPlatform()) { - int api = target.getVersion().getApiLevel(); - if (api > max && !target.getVersion().isPreview()) { - max = api; - } - } - } - - return max; - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java deleted file mode 100644 index a833dae..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java +++ /dev/null @@ -1,2061 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.client.api; - -import static com.android.SdkConstants.ANDROID_MANIFEST_XML; -import static com.android.SdkConstants.ATTR_IGNORE; -import static com.android.SdkConstants.DOT_CLASS; -import static com.android.SdkConstants.DOT_JAR; -import static com.android.SdkConstants.DOT_JAVA; -import static com.android.SdkConstants.DOT_XML; -import static com.android.SdkConstants.FN_PROJECT_PROGUARD_FILE; -import static com.android.SdkConstants.OLD_PROGUARD_FILE; -import static com.android.SdkConstants.RES_FOLDER; -import static com.android.SdkConstants.SUPPRESS_ALL; -import static com.android.SdkConstants.SUPPRESS_LINT; -import static com.android.SdkConstants.TOOLS_URI; -import static org.objectweb.asm.Opcodes.ASM4; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.annotations.VisibleForTesting; -import com.android.resources.ResourceFolderType; -import com.android.sdklib.IAndroidTarget; -import com.android.tools.lint.client.api.LintListener.EventType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.ClassContext; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Project; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -import com.android.tools.lint.detector.api.Scope; -import com.android.tools.lint.detector.api.Severity; -import com.android.tools.lint.detector.api.XmlContext; -import com.google.common.annotations.Beta; -import com.google.common.base.CharMatcher; -import com.google.common.base.Splitter; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; -import com.google.common.io.ByteStreams; -import com.google.common.io.Closeables; - -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.tree.AnnotationNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldNode; -import org.objectweb.asm.tree.MethodNode; -import org.w3c.dom.Attr; -import org.w3c.dom.Element; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Deque; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import lombok.ast.Annotation; -import lombok.ast.AnnotationElement; -import lombok.ast.AnnotationValue; -import lombok.ast.ArrayInitializer; -import lombok.ast.ClassDeclaration; -import lombok.ast.ConstructorDeclaration; -import lombok.ast.Expression; -import lombok.ast.MethodDeclaration; -import lombok.ast.Modifiers; -import lombok.ast.Node; -import lombok.ast.StrictListAccessor; -import lombok.ast.StringLiteral; -import lombok.ast.TypeReference; -import lombok.ast.VariableDefinition; - -/** - * Analyzes Android projects and files - * <p> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public class LintDriver { - /** - * Max number of passes to run through the lint runner if requested by - * {@link #requestRepeat} - */ - private static final int MAX_PHASES = 3; - private static final String SUPPRESS_LINT_VMSIG = '/' + SUPPRESS_LINT + ';'; - - private final LintClient mClient; - private volatile boolean mCanceled; - private IssueRegistry mRegistry; - private EnumSet<Scope> mScope; - private List<? extends Detector> mApplicableDetectors; - private Map<Scope, List<Detector>> mScopeDetectors; - private List<LintListener> mListeners; - private int mPhase; - private List<Detector> mRepeatingDetectors; - private EnumSet<Scope> mRepeatScope; - private Project[] mCurrentProjects; - private Project mCurrentProject; - private boolean mAbbreviating = true; - private boolean mParserErrors; - - /** - * Creates a new {@link LintDriver} - * - * @param registry The registry containing issues to be checked - * @param client the tool wrapping the analyzer, such as an IDE or a CLI - */ - public LintDriver(@NonNull IssueRegistry registry, @NonNull LintClient client) { - mRegistry = registry; - mClient = new LintClientWrapper(client); - } - - /** Cancels the current lint run as soon as possible */ - public void cancel() { - mCanceled = true; - } - - /** - * Returns the scope for the lint job - * - * @return the scope, never null - */ - @NonNull - public EnumSet<Scope> getScope() { - return mScope; - } - - /** - * Returns the lint client requesting the lint check - * - * @return the client, never null - */ - @NonNull - public LintClient getClient() { - return mClient; - } - - /** - * Returns the current phase number. The first pass is numbered 1. Only one pass - * will be performed, unless a {@link Detector} calls {@link #requestRepeat}. - * - * @return the current phase, usually 1 - */ - public int getPhase() { - return mPhase; - } - - /** - * Returns the current {@link IssueRegistry}. - * - * @return the current {@link IssueRegistry} - */ - @NonNull - public IssueRegistry getRegistry() { - return mRegistry; - } - - /** - * Returns the project containing a given file, or null if not found. This searches - * only among the currently checked project and its library projects, not among all - * possible projects being scanned sequentially. - * - * @param file the file to be checked - * @return the corresponding project, or null if not found - */ - @Nullable - public Project findProjectFor(@NonNull File file) { - if (mCurrentProjects != null) { - if (mCurrentProjects.length == 1) { - return mCurrentProjects[0]; - } - String path = file.getPath(); - for (Project project : mCurrentProjects) { - if (path.startsWith(project.getDir().getPath())) { - return project; - } - } - } - - return null; - } - - /** - * Sets whether lint should abbreviate output when appropriate. - * - * @param abbreviating true to abbreviate output, false to include everything - */ - public void setAbbreviating(boolean abbreviating) { - mAbbreviating = abbreviating; - } - - /** - * Returns whether lint should abbreviate output when appropriate. - * - * @return true if lint should abbreviate output, false when including everything - */ - public boolean isAbbreviating() { - return mAbbreviating; - } - - /** - * Returns whether lint has encountered any files with fatal parser errors - * (e.g. broken source code, or even broken parsers) - * <p> - * This is useful for checks that need to make sure they've seen all data in - * order to be conclusive (such as an unused resource check). - * - * @return true if any files were not properly processed because they - * contained parser errors - */ - public boolean hasParserErrors() { - return mParserErrors; - } - - /** - * Sets whether lint has encountered files with fatal parser errors. - * - * @see #hasParserErrors() - * @param hasErrors whether parser errors have been encountered - */ - public void setHasParserErrors(boolean hasErrors) { - mParserErrors = hasErrors; - } - - /** - * Returns the projects being analyzed - * - * @return the projects being analyzed - */ - @NonNull - public List<Project> getProjects() { - if (mCurrentProjects != null) { - return Arrays.asList(mCurrentProjects); - } - return Collections.emptyList(); - } - - /** - * Analyze the given file (which can point to an Android project). Issues found - * are reported to the associated {@link LintClient}. - * - * @param files the files and directories to be analyzed - * @param scope the scope of the analysis; detectors with a wider scope will - * not be run. If null, the scope will be inferred from the files. - */ - public void analyze(@NonNull List<File> files, @Nullable EnumSet<Scope> scope) { - mCanceled = false; - mScope = scope; - - Collection<Project> projects = computeProjects(files); - if (projects.size() == 0) { - mClient.log(null, "No projects found for %1$s", files.toString()); - return; - } - if (mCanceled) { - return; - } - - if (mScope == null) { - // Infer the scope - mScope = EnumSet.noneOf(Scope.class); - for (Project project : projects) { - List<File> subset = project.getSubset(); - if (subset != null) { - for (File file : subset) { - String name = file.getName(); - if (name.equals(ANDROID_MANIFEST_XML)) { - mScope.add(Scope.MANIFEST); - } else if (name.endsWith(DOT_XML)) { - mScope.add(Scope.RESOURCE_FILE); - } else if (name.equals(RES_FOLDER) - || file.getParent().equals(RES_FOLDER)) { - mScope.add(Scope.ALL_RESOURCE_FILES); - mScope.add(Scope.RESOURCE_FILE); - } else if (name.endsWith(DOT_JAVA)) { - mScope.add(Scope.JAVA_FILE); - } else if (name.endsWith(DOT_CLASS)) { - mScope.add(Scope.CLASS_FILE); - } else if (name.equals(OLD_PROGUARD_FILE) - || name.equals(FN_PROJECT_PROGUARD_FILE)) { - mScope.add(Scope.PROGUARD_FILE); - } - } - } else { - // Specified a full project: just use the full project scope - mScope = Scope.ALL; - break; - } - } - } - - fireEvent(EventType.STARTING, null); - - for (Project project : projects) { - mPhase = 1; - - // The set of available detectors varies between projects - computeDetectors(project); - - if (mApplicableDetectors.size() == 0) { - // No detectors enabled in this project: skip it - continue; - } - - checkProject(project); - if (mCanceled) { - break; - } - - runExtraPhases(project); - } - - fireEvent(mCanceled ? EventType.CANCELED : EventType.COMPLETED, null); - } - - private void runExtraPhases(Project project) { - // Did any detectors request another phase? - if (mRepeatingDetectors != null) { - // Yes. Iterate up to MAX_PHASES times. - - // During the extra phases, we might be narrowing the scope, and setting it in the - // scope field such that detectors asking about the available scope will get the - // correct result. However, we need to restore it to the original scope when this - // is done in case there are other projects that will be checked after this, since - // the repeated phases is done *per project*, not after all projects have been - // processed. - EnumSet<Scope> oldScope = mScope; - - do { - mPhase++; - fireEvent(EventType.NEW_PHASE, - new Context(this, project, null, project.getDir())); - - // Narrow the scope down to the set of scopes requested by - // the rules. - if (mRepeatScope == null) { - mRepeatScope = Scope.ALL; - } - mScope = Scope.intersect(mScope, mRepeatScope); - if (mScope.isEmpty()) { - break; - } - - // Compute the detectors to use for this pass. - // Unlike the normal computeDetectors(project) call, - // this is going to use the existing instances, and include - // those that apply for the configuration. - computeRepeatingDetectors(mRepeatingDetectors, project); - - if (mApplicableDetectors.size() == 0) { - // No detectors enabled in this project: skip it - continue; - } - - checkProject(project); - if (mCanceled) { - break; - } - } while (mPhase < MAX_PHASES && mRepeatingDetectors != null); - - mScope = oldScope; - } - } - - private void computeRepeatingDetectors(List<Detector> detectors, Project project) { - // Ensure that the current visitor is recomputed - mCurrentFolderType = null; - mCurrentVisitor = null; - - // Create map from detector class to issue such that we can - // compute applicable issues for each detector in the list of detectors - // to be repeated - List<Issue> issues = mRegistry.getIssues(); - Multimap<Class<? extends Detector>, Issue> issueMap = - ArrayListMultimap.create(issues.size(), 3); - for (Issue issue : issues) { - issueMap.put(issue.getDetectorClass(), issue); - } - - Map<Class<? extends Detector>, EnumSet<Scope>> detectorToScope = - new HashMap<Class<? extends Detector>, EnumSet<Scope>>(); - Map<Scope, List<Detector>> scopeToDetectors = - new HashMap<Scope, List<Detector>>(); - - List<Detector> detectorList = new ArrayList<Detector>(); - // Compute the list of detectors (narrowed down from mRepeatingDetectors), - // and simultaneously build up the detectorToScope map which tracks - // the scopes each detector is affected by (this is used to populate - // the mScopeDetectors map which is used during iteration). - Configuration configuration = project.getConfiguration(); - for (Detector detector : detectors) { - Class<? extends Detector> detectorClass = detector.getClass(); - Collection<Issue> detectorIssues = issueMap.get(detectorClass); - if (issues != null) { - boolean add = false; - for (Issue issue : detectorIssues) { - // The reason we have to check whether the detector is enabled - // is that this is a per-project property, so when running lint in multiple - // projects, a detector enabled only in a different project could have - // requested another phase, and we end up in this project checking whether - // the detector is enabled here. - if (!configuration.isEnabled(issue)) { - continue; - } - - add = true; // Include detector if any of its issues are enabled - - EnumSet<Scope> s = detectorToScope.get(detectorClass); - EnumSet<Scope> issueScope = issue.getScope(); - if (s == null) { - detectorToScope.put(detectorClass, issueScope); - } else if (!s.containsAll(issueScope)) { - EnumSet<Scope> union = EnumSet.copyOf(s); - union.addAll(issueScope); - detectorToScope.put(detectorClass, union); - } - } - - if (add) { - detectorList.add(detector); - EnumSet<Scope> union = detectorToScope.get(detector.getClass()); - for (Scope s : union) { - List<Detector> list = scopeToDetectors.get(s); - if (list == null) { - list = new ArrayList<Detector>(); - scopeToDetectors.put(s, list); - } - list.add(detector); - } - } - } - } - - mApplicableDetectors = detectorList; - mScopeDetectors = scopeToDetectors; - mRepeatingDetectors = null; - mRepeatScope = null; - - validateScopeList(); - } - - private void computeDetectors(@NonNull Project project) { - // Ensure that the current visitor is recomputed - mCurrentFolderType = null; - mCurrentVisitor = null; - - Configuration configuration = project.getConfiguration(); - mScopeDetectors = new HashMap<Scope, List<Detector>>(); - mApplicableDetectors = mRegistry.createDetectors(mClient, configuration, - mScope, mScopeDetectors); - - validateScopeList(); - } - - /** Development diagnostics only, run with assertions on */ - @SuppressWarnings("all") // Turn off warnings for the intentional assertion side effect below - private void validateScopeList() { - boolean assertionsEnabled = false; - assert assertionsEnabled = true; // Intentional side-effect - if (assertionsEnabled) { - List<Detector> resourceFileDetectors = mScopeDetectors.get(Scope.RESOURCE_FILE); - if (resourceFileDetectors != null) { - for (Detector detector : resourceFileDetectors) { - assert detector instanceof ResourceXmlDetector : detector; - } - } - - List<Detector> manifestDetectors = mScopeDetectors.get(Scope.MANIFEST); - if (manifestDetectors != null) { - for (Detector detector : manifestDetectors) { - assert detector instanceof Detector.XmlScanner : detector; - } - } - List<Detector> javaCodeDetectors = mScopeDetectors.get(Scope.ALL_JAVA_FILES); - if (javaCodeDetectors != null) { - for (Detector detector : javaCodeDetectors) { - assert detector instanceof Detector.JavaScanner : detector; - } - } - List<Detector> javaFileDetectors = mScopeDetectors.get(Scope.JAVA_FILE); - if (javaFileDetectors != null) { - for (Detector detector : javaFileDetectors) { - assert detector instanceof Detector.JavaScanner : detector; - } - } - - List<Detector> classDetectors = mScopeDetectors.get(Scope.CLASS_FILE); - if (classDetectors != null) { - for (Detector detector : classDetectors) { - assert detector instanceof Detector.ClassScanner : detector; - } - } - } - } - - private void registerProjectFile( - @NonNull Map<File, Project> fileToProject, - @NonNull File file, - @NonNull File projectDir, - @NonNull File rootDir) { - fileToProject.put(file, mClient.getProject(projectDir, rootDir)); - } - - private Collection<Project> computeProjects(@NonNull List<File> files) { - // Compute list of projects - Map<File, Project> fileToProject = new HashMap<File, Project>(); - - File sharedRoot = null; - - // Ensure that we have absolute paths such that if you lint - // "foo bar" in "baz" we can show baz/ as the root - if (files.size() > 1) { - List<File> absolute = new ArrayList<File>(files.size()); - for (File file : files) { - absolute.add(file.getAbsoluteFile()); - } - files = absolute; - - sharedRoot = LintUtils.getCommonParent(files); - if (sharedRoot != null && sharedRoot.getParentFile() == null) { // "/" ? - sharedRoot = null; - } - } - - - for (File file : files) { - if (file.isDirectory()) { - File rootDir = sharedRoot; - if (rootDir == null) { - rootDir = file; - if (files.size() > 1) { - rootDir = file.getParentFile(); - if (rootDir == null) { - rootDir = file; - } - } - } - - // Figure out what to do with a directory. Note that the meaning of the - // directory can be ambiguous: - // If you pass a directory which is unknown, we don't know if we should - // search upwards (in case you're pointing at a deep java package folder - // within the project), or if you're pointing at some top level directory - // containing lots of projects you want to scan. We attempt to do the - // right thing, which is to see if you're pointing right at a project or - // right within it (say at the src/ or res/) folder, and if not, you're - // hopefully pointing at a project tree that you want to scan recursively. - if (LintUtils.isProjectDir(file)) { - registerProjectFile(fileToProject, file, file, rootDir); - continue; - } else { - File parent = file.getParentFile(); - if (parent != null) { - if (LintUtils.isProjectDir(parent)) { - registerProjectFile(fileToProject, file, parent, parent); - continue; - } else { - parent = parent.getParentFile(); - if (parent != null && LintUtils.isProjectDir(parent)) { - registerProjectFile(fileToProject, file, parent, parent); - continue; - } - } - } - - // Search downwards for nested projects - addProjects(file, fileToProject, rootDir); - } - } else { - // Pointed at a file: Search upwards for the containing project - File parent = file.getParentFile(); - while (parent != null) { - if (LintUtils.isProjectDir(parent)) { - registerProjectFile(fileToProject, file, parent, parent); - break; - } - parent = parent.getParentFile(); - } - } - - if (mCanceled) { - return Collections.emptySet(); - } - } - - for (Map.Entry<File, Project> entry : fileToProject.entrySet()) { - File file = entry.getKey(); - Project project = entry.getValue(); - if (!file.equals(project.getDir())) { - if (file.isDirectory()) { - try { - File dir = file.getCanonicalFile(); - if (dir.equals(project.getDir())) { - continue; - } - } catch (IOException ioe) { - // pass - } - } - - project.addFile(file); - } - } - - // Partition the projects up such that we only return projects that aren't - // included by other projects (e.g. because they are library projects) - - Collection<Project> allProjects = fileToProject.values(); - Set<Project> roots = new HashSet<Project>(allProjects); - for (Project project : allProjects) { - roots.removeAll(project.getAllLibraries()); - } - - // Report issues for all projects that are explicitly referenced. We need to - // do this here, since the project initialization will mark all library - // projects as no-report projects by default. - for (Project project : allProjects) { - // Report issues for all projects explicitly listed or found via a directory - // traversal -- including library projects. - project.setReportIssues(true); - } - - if (LintUtils.assertionsEnabled()) { - // Make sure that all the project directories are unique. This ensures - // that we didn't accidentally end up with different project instances - // for a library project discovered as a directory as well as one - // initialized from the library project dependency list - IdentityHashMap<Project, Project> projects = - new IdentityHashMap<Project, Project>(); - for (Project project : roots) { - projects.put(project, project); - for (Project library : project.getAllLibraries()) { - projects.put(library, library); - } - } - Set<File> dirs = new HashSet<File>(); - for (Project project : projects.keySet()) { - assert !dirs.contains(project.getDir()); - dirs.add(project.getDir()); - } - } - - return roots; - } - - private void addProjects( - @NonNull File dir, - @NonNull Map<File, Project> fileToProject, - @NonNull File rootDir) { - if (mCanceled) { - return; - } - - if (LintUtils.isProjectDir(dir)) { - registerProjectFile(fileToProject, dir, dir, rootDir); - } else { - File[] files = dir.listFiles(); - if (files != null) { - for (File file : files) { - if (file.isDirectory()) { - addProjects(file, fileToProject, rootDir); - } - } - } - } - } - - private void checkProject(@NonNull Project project) { - File projectDir = project.getDir(); - - Context projectContext = new Context(this, project, null, projectDir); - fireEvent(EventType.SCANNING_PROJECT, projectContext); - - List<Project> allLibraries = project.getAllLibraries(); - Set<Project> allProjects = new HashSet<Project>(allLibraries.size() + 1); - allProjects.add(project); - allProjects.addAll(allLibraries); - mCurrentProjects = allProjects.toArray(new Project[allProjects.size()]); - - mCurrentProject = project; - - for (Detector check : mApplicableDetectors) { - check.beforeCheckProject(projectContext); - if (mCanceled) { - return; - } - } - - assert mCurrentProject == project; - runFileDetectors(project, project); - - if (!Scope.checkSingleFile(mScope)) { - List<Project> libraries = project.getDirectLibraries(); - for (Project library : libraries) { - Context libraryContext = new Context(this, library, project, projectDir); - fireEvent(EventType.SCANNING_LIBRARY_PROJECT, libraryContext); - mCurrentProject = library; - - for (Detector check : mApplicableDetectors) { - check.beforeCheckLibraryProject(libraryContext); - if (mCanceled) { - return; - } - } - assert mCurrentProject == library; - - runFileDetectors(library, project); - if (mCanceled) { - return; - } - - assert mCurrentProject == library; - - for (Detector check : mApplicableDetectors) { - check.afterCheckLibraryProject(libraryContext); - if (mCanceled) { - return; - } - } - } - } - - mCurrentProject = project; - - for (Detector check : mApplicableDetectors) { - check.afterCheckProject(projectContext); - if (mCanceled) { - return; - } - } - - if (mCanceled) { - mClient.report( - projectContext, - // Must provide an issue since API guarantees that the issue parameter - // is valid - Issue.create("Lint", "", "", Category.PERFORMANCE, 0, Severity.INFORMATIONAL, //$NON-NLS-1$ - Detector.class, EnumSet.noneOf(Scope.class)), - Severity.INFORMATIONAL, - null /*range*/, - "Lint canceled by user", null); - } - - mCurrentProjects = null; - } - - private void runFileDetectors(@NonNull Project project, @Nullable Project main) { - // Look up manifest information (but not for library projects) - File manifestFile = project.getManifestFile(); - if (manifestFile != null) { - XmlContext context = new XmlContext(this, project, main, manifestFile, null); - IDomParser parser = mClient.getDomParser(); - if (parser != null) { - context.document = parser.parseXml(context); - if (context.document != null) { - project.readManifest(context.document); - - if ((!project.isLibrary() || (main != null && main.isMergingManifests())) - && mScope.contains(Scope.MANIFEST)) { - List<Detector> detectors = mScopeDetectors.get(Scope.MANIFEST); - if (detectors != null) { - XmlVisitor v = new XmlVisitor(parser, detectors); - fireEvent(EventType.SCANNING_FILE, context); - v.visitFile(context, manifestFile); - } - } - } - } - } - - // Process both Scope.RESOURCE_FILE and Scope.ALL_RESOURCE_FILES detectors together - // in a single pass through the resource directories. - if (mScope.contains(Scope.ALL_RESOURCE_FILES) || mScope.contains(Scope.RESOURCE_FILE)) { - List<Detector> checks = union(mScopeDetectors.get(Scope.RESOURCE_FILE), - mScopeDetectors.get(Scope.ALL_RESOURCE_FILES)); - if (checks != null && checks.size() > 0) { - List<ResourceXmlDetector> xmlDetectors = - new ArrayList<ResourceXmlDetector>(checks.size()); - for (Detector detector : checks) { - if (detector instanceof ResourceXmlDetector) { - xmlDetectors.add((ResourceXmlDetector) detector); - } - } - if (xmlDetectors.size() > 0) { - List<File> files = project.getSubset(); - if (files != null) { - checkIndividualResources(project, main, xmlDetectors, files); - } else { - File res = new File(project.getDir(), RES_FOLDER); - if (res.exists() && xmlDetectors.size() > 0) { - checkResFolder(project, main, res, xmlDetectors); - } - } - } - } - } - - if (mCanceled) { - return; - } - - if (mScope.contains(Scope.JAVA_FILE) || mScope.contains(Scope.ALL_JAVA_FILES)) { - List<Detector> checks = union(mScopeDetectors.get(Scope.JAVA_FILE), - mScopeDetectors.get(Scope.ALL_JAVA_FILES)); - if (checks != null && checks.size() > 0) { - List<File> files = project.getSubset(); - if (files != null) { - checkIndividualJavaFiles(project, main, checks, files); - } else { - List<File> sourceFolders = project.getJavaSourceFolders(); - checkJava(project, main, sourceFolders, checks); - } - } - } - - if (mCanceled) { - return; - } - - if (mScope.contains(Scope.CLASS_FILE) || mScope.contains(Scope.JAVA_LIBRARIES)) { - checkClasses(project, main); - } - - if (mCanceled) { - return; - } - - if (project == main && mScope.contains(Scope.PROGUARD_FILE)) { - checkProGuard(project, main); - } - } - private void checkProGuard(Project project, Project main) { - List<Detector> detectors = mScopeDetectors.get(Scope.PROGUARD_FILE); - if (detectors != null) { - Project p = main != null ? main : project; - List<File> files = new ArrayList<File>(); - String paths = p.getProguardPath(); - if (paths != null) { - Splitter splitter = Splitter.on(CharMatcher.anyOf(":;")); //$NON-NLS-1$ - for (String path : splitter.split(paths)) { - if (path.contains("${")) { //$NON-NLS-1$ - // Don't analyze the global/user proguard files - continue; - } - File file = new File(path); - if (!file.isAbsolute()) { - file = new File(project.getDir(), path); - } - if (file.exists()) { - files.add(file); - } - } - } - if (files.isEmpty()) { - File file = new File(project.getDir(), OLD_PROGUARD_FILE); - if (file.exists()) { - files.add(file); - } - file = new File(project.getDir(), FN_PROJECT_PROGUARD_FILE); - if (file.exists()) { - files.add(file); - } - } - for (File file : files) { - Context context = new Context(this, project, main, file); - fireEvent(EventType.SCANNING_FILE, context); - for (Detector detector : detectors) { - if (detector.appliesTo(context, file)) { - detector.beforeCheckFile(context); - detector.run(context); - detector.afterCheckFile(context); - } - } - } - } - } - - /** - * Map from VM class name to corresponding super class VM name, if available. - * This map is typically null except <b>during</b> class processing. - */ - private Map<String, String> mSuperClassMap; - - /** - * Returns the super class for the given class name, - * which should be in VM format (e.g. java/lang/Integer, not java.lang.Integer). - * If the super class is not known, returns null. This can happen if - * the given class is not a known class according to the project or its - * libraries, for example because it refers to one of the core libraries which - * are not analyzed by lint. - * - * @param name the fully qualified class name - * @return the corresponding super class name (in VM format), or null if not known - */ - @Nullable - public String getSuperClass(@NonNull String name) { - if (mSuperClassMap == null) { - throw new IllegalStateException("Only callable during ClassScanner#checkClass"); - } - assert name.indexOf('.') == -1 : "Use VM signatures, e.g. java/lang/Integer"; - return mSuperClassMap.get(name); - } - - /** - * Returns true if the given class is a subclass of the given super class. - * - * @param classNode the class to check whether it is a subclass of the given - * super class name - * @param superClassName the fully qualified super class name (in VM format, - * e.g. java/lang/Integer, not java.lang.Integer. - * @return true if the given class is a subclass of the given super class - */ - public boolean isSubclassOf(@NonNull ClassNode classNode, @NonNull String superClassName) { - if (superClassName.equals(classNode.superName)) { - return true; - } - - String className = classNode.name; - while (className != null) { - if (className.equals(superClassName)) { - return true; - } - className = getSuperClass(className); - } - - return false; - } - @Nullable - private static List<Detector> union( - @Nullable List<Detector> list1, - @Nullable List<Detector> list2) { - if (list1 == null) { - return list2; - } else if (list2 == null) { - return list1; - } else { - // Use set to pick out unique detectors, since it's possible for there to be overlap, - // e.g. the DuplicateIdDetector registers both a cross-resource issue and a - // single-file issue, so it shows up on both scope lists: - Set<Detector> set = new HashSet<Detector>(list1.size() + list2.size()); - if (list1 != null) { - set.addAll(list1); - } - if (list2 != null) { - set.addAll(list2); - } - - return new ArrayList<Detector>(set); - } - } - - /** Check the classes in this project (and if applicable, in any library projects */ - private void checkClasses(Project project, Project main) { - List<File> files = project.getSubset(); - if (files != null) { - checkIndividualClassFiles(project, main, files); - return; - } - - // We need to read in all the classes up front such that we can initialize - // the parent chains (such that for example for a virtual dispatch, we can - // also check the super classes). - - List<File> libraries = project.getJavaLibraries(); - List<ClassEntry> libraryEntries; - if (libraries.size() > 0) { - libraryEntries = new ArrayList<ClassEntry>(64); - findClasses(libraryEntries, libraries); - Collections.sort(libraryEntries); - } else { - libraryEntries = Collections.emptyList(); - } - - List<File> classFolders = project.getJavaClassFolders(); - List<ClassEntry> classEntries; - if (classFolders.size() == 0) { - String message = String.format("No .class files were found in project \"%1$s\", " - + "so none of the classfile based checks could be run. " - + "Does the project need to be built first?", project.getName()); - Location location = Location.create(project.getDir()); - mClient.report(new Context(this, project, main, project.getDir()), - IssueRegistry.LINT_ERROR, - project.getConfiguration().getSeverity(IssueRegistry.LINT_ERROR), - location, message, null); - classEntries = Collections.emptyList(); - } else { - classEntries = new ArrayList<ClassEntry>(64); - findClasses(classEntries, classFolders); - Collections.sort(classEntries); - } - - if (getPhase() == 1) { - mSuperClassMap = getSuperMap(libraryEntries, classEntries); - } - - // Actually run the detectors. Libraries should be called before the - // main classes. - runClassDetectors(Scope.JAVA_LIBRARIES, libraryEntries, project, main); - - if (mCanceled) { - return; - } - - runClassDetectors(Scope.CLASS_FILE, classEntries, project, main); - } - - private void checkIndividualClassFiles( - @NonNull Project project, - @Nullable Project main, - @NonNull List<File> files) { - List<ClassEntry> entries = new ArrayList<ClassEntry>(files.size()); - - List<File> classFolders = project.getJavaClassFolders(); - if (!classFolders.isEmpty()) { - for (File file : files) { - String path = file.getPath(); - if (file.isFile() && path.endsWith(DOT_CLASS)) { - try { - byte[] bytes = mClient.readBytes(file); - if (bytes != null) { - for (File dir : classFolders) { - if (path.startsWith(dir.getPath())) { - entries.add(new ClassEntry(file, null /* jarFile*/, dir, - bytes)); - break; - } - } - } - } catch (IOException e) { - mClient.log(e, null); - continue; - } - - if (mCanceled) { - return; - } - } - } - - if (entries.size() > 0) { - Collections.sort(entries); - // No superclass info available on individual lint runs - mSuperClassMap = Collections.emptyMap(); - runClassDetectors(Scope.CLASS_FILE, entries, project, main); - } - } - } - - /** - * Stack of {@link ClassNode} nodes for outer classes of the currently - * processed class, including that class itself. Populated by - * {@link #runClassDetectors(Scope, List, Project, Project)} and used by - * {@link #getOuterClassNode(ClassNode)} - */ - private Deque<ClassNode> mOuterClasses; - - private void runClassDetectors(Scope scope, List<ClassEntry> entries, - Project project, Project main) { - if (mScope.contains(scope)) { - List<Detector> classDetectors = mScopeDetectors.get(scope); - if (classDetectors != null && classDetectors.size() > 0 && entries.size() > 0) { - AsmVisitor visitor = new AsmVisitor(mClient, classDetectors); - - String sourceContents = null; - String sourceName = ""; - mOuterClasses = new ArrayDeque<ClassNode>(); - for (ClassEntry entry : entries) { - ClassReader reader; - ClassNode classNode; - try { - reader = new ClassReader(entry.bytes); - classNode = new ClassNode(); - reader.accept(classNode, 0 /* flags */); - } catch (Throwable t) { - mClient.log(null, "Error processing %1$s: broken class file?", - entry.path()); - continue; - } - - ClassNode peek; - while ((peek = mOuterClasses.peek()) != null) { - if (classNode.name.startsWith(peek.name)) { - break; - } else { - mOuterClasses.pop(); - } - } - mOuterClasses.push(classNode); - - if (isSuppressed(null, classNode)) { - // Class was annotated with suppress all -- no need to look any further - continue; - } - - if (sourceContents != null) { - // Attempt to reuse the source buffer if initialized - // This means making sure that the source files - // foo/bar/MyClass and foo/bar/MyClass$Bar - // and foo/bar/MyClass$3 and foo/bar/MyClass$3$1 have the same prefix. - String newName = classNode.name; - int newRootLength = newName.indexOf('$'); - if (newRootLength == -1) { - newRootLength = newName.length(); - } - int oldRootLength = sourceName.indexOf('$'); - if (oldRootLength == -1) { - oldRootLength = sourceName.length(); - } - if (newRootLength != oldRootLength || - !sourceName.regionMatches(0, newName, 0, newRootLength)) { - sourceContents = null; - } - } - - ClassContext context = new ClassContext(this, project, main, - entry.file, entry.jarFile, entry.binDir, entry.bytes, - classNode, scope == Scope.JAVA_LIBRARIES /*fromLibrary*/, - sourceContents); - - try { - visitor.runClassDetectors(context); - } catch (Exception e) { - mClient.log(e, null); - } - - if (mCanceled) { - return; - } - - sourceContents = context.getSourceContents(false/*read*/); - sourceName = classNode.name; - } - - mOuterClasses = null; - } - } - } - - /** Returns the outer class node of the given class node - * @param classNode the inner class node - * @return the outer class node */ - public ClassNode getOuterClassNode(@NonNull ClassNode classNode) { - String outerName = classNode.outerClass; - - Iterator<ClassNode> iterator = mOuterClasses.iterator(); - while (iterator.hasNext()) { - ClassNode node = iterator.next(); - if (outerName != null) { - if (node.name.equals(outerName)) { - return node; - } - } else if (node == classNode) { - return iterator.hasNext() ? iterator.next() : null; - } - } - - return null; - } - - private Map<String, String> getSuperMap(List<ClassEntry> libraryEntries, - List<ClassEntry> classEntries) { - int size = libraryEntries.size() + classEntries.size(); - Map<String, String> map = new HashMap<String, String>(size); - - SuperclassVisitor visitor = new SuperclassVisitor(map); - addSuperClasses(visitor, libraryEntries); - addSuperClasses(visitor, classEntries); - - return map; - } - - private void addSuperClasses(SuperclassVisitor visitor, List<ClassEntry> entries) { - for (ClassEntry entry : entries) { - try { - ClassReader reader = new ClassReader(entry.bytes); - int flags = ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG - | ClassReader.SKIP_FRAMES; - reader.accept(visitor, flags); - } catch (Throwable t) { - mClient.log(null, "Error processing %1$s: broken class file?", entry.path()); - } - } - } - - /** Visitor skimming classes and initializing a map of super classes */ - private static class SuperclassVisitor extends ClassVisitor { - private final Map<String, String> mMap; - - public SuperclassVisitor(Map<String, String> map) { - super(ASM4); - mMap = map; - } - - @Override - public void visit(int version, int access, String name, String signature, String superName, - String[] interfaces) { - if (superName != null) { - mMap.put(name, superName); - } - } - } - - private void findClasses( - @NonNull List<ClassEntry> entries, - @NonNull List<File> classPath) { - for (File classPathEntry : classPath) { - if (classPathEntry.getName().endsWith(DOT_JAR)) { - File jarFile = classPathEntry; - if (!jarFile.exists()) { - continue; - } - ZipInputStream zis = null; - try { - FileInputStream fis = new FileInputStream(jarFile); - zis = new ZipInputStream(fis); - ZipEntry entry = zis.getNextEntry(); - while (entry != null) { - String name = entry.getName(); - if (name.endsWith(DOT_CLASS)) { - try { - byte[] bytes = ByteStreams.toByteArray(zis); - if (bytes != null) { - File file = new File(entry.getName()); - entries.add(new ClassEntry(file, jarFile, jarFile, bytes)); - } - } catch (Exception e) { - mClient.log(e, null); - continue; - } - } - - if (mCanceled) { - return; - } - - entry = zis.getNextEntry(); - } - } catch (IOException e) { - mClient.log(e, "Could not read jar file contents from %1$s", jarFile); - } finally { - Closeables.closeQuietly(zis); - } - - continue; - } else if (classPathEntry.isDirectory()) { - File binDir = classPathEntry; - List<File> classFiles = new ArrayList<File>(); - addClassFiles(binDir, classFiles); - - for (File file : classFiles) { - try { - byte[] bytes = mClient.readBytes(file); - if (bytes != null) { - entries.add(new ClassEntry(file, null /* jarFile*/, binDir, bytes)); - } - } catch (IOException e) { - mClient.log(e, null); - continue; - } - - if (mCanceled) { - return; - } - } - } else { - mClient.log(null, "Ignoring class path entry %1$s", classPathEntry); - } - } - } - - private void addClassFiles(@NonNull File dir, @NonNull List<File> classFiles) { - // Process the resource folder - File[] files = dir.listFiles(); - if (files != null && files.length > 0) { - for (File file : files) { - if (file.isFile() && file.getName().endsWith(DOT_CLASS)) { - classFiles.add(file); - } else if (file.isDirectory()) { - // Recurse - addClassFiles(file, classFiles); - } - } - } - } - - private void checkJava( - @NonNull Project project, - @Nullable Project main, - @NonNull List<File> sourceFolders, - @NonNull List<Detector> checks) { - IJavaParser javaParser = mClient.getJavaParser(); - if (javaParser == null) { - mClient.log(null, "No java parser provided to lint: not running Java checks"); - return; - } - - assert checks.size() > 0; - - // Gather all Java source files in a single pass; more efficient. - List<File> sources = new ArrayList<File>(100); - for (File folder : sourceFolders) { - gatherJavaFiles(folder, sources); - } - if (sources.size() > 0) { - JavaVisitor visitor = new JavaVisitor(javaParser, checks); - for (File file : sources) { - JavaContext context = new JavaContext(this, project, main, file); - fireEvent(EventType.SCANNING_FILE, context); - visitor.visitFile(context, file); - if (mCanceled) { - return; - } - } - } - } - - private void checkIndividualJavaFiles( - @NonNull Project project, - @Nullable Project main, - @NonNull List<Detector> checks, - @NonNull List<File> files) { - - IJavaParser javaParser = mClient.getJavaParser(); - if (javaParser == null) { - mClient.log(null, "No java parser provided to lint: not running Java checks"); - return; - } - - JavaVisitor visitor = new JavaVisitor(javaParser, checks); - - for (File file : files) { - if (file.isFile() && file.getPath().endsWith(DOT_JAVA)) { - JavaContext context = new JavaContext(this, project, main, file); - fireEvent(EventType.SCANNING_FILE, context); - visitor.visitFile(context, file); - if (mCanceled) { - return; - } - } - } - } - - private void gatherJavaFiles(@NonNull File dir, @NonNull List<File> result) { - File[] files = dir.listFiles(); - if (files != null) { - for (File file : files) { - if (file.isFile() && file.getName().endsWith(".java")) { //$NON-NLS-1$ - result.add(file); - } else if (file.isDirectory()) { - gatherJavaFiles(file, result); - } - } - } - } - - private ResourceFolderType mCurrentFolderType; - private List<ResourceXmlDetector> mCurrentXmlDetectors; - private XmlVisitor mCurrentVisitor; - - @Nullable - private XmlVisitor getVisitor( - @NonNull ResourceFolderType type, - @NonNull List<ResourceXmlDetector> checks) { - if (type != mCurrentFolderType) { - mCurrentFolderType = type; - - // Determine which XML resource detectors apply to the given folder type - List<ResourceXmlDetector> applicableChecks = - new ArrayList<ResourceXmlDetector>(checks.size()); - for (ResourceXmlDetector check : checks) { - if (check.appliesTo(type)) { - applicableChecks.add(check); - } - } - - // If the list of detectors hasn't changed, then just use the current visitor! - if (mCurrentXmlDetectors != null && mCurrentXmlDetectors.equals(applicableChecks)) { - return mCurrentVisitor; - } - - if (applicableChecks.size() == 0) { - mCurrentVisitor = null; - return null; - } - - IDomParser parser = mClient.getDomParser(); - if (parser != null) { - mCurrentVisitor = new XmlVisitor(parser, applicableChecks); - } - } - - return mCurrentVisitor; - } - - private void checkResFolder( - @NonNull Project project, - @Nullable Project main, - @NonNull File res, - @NonNull List<ResourceXmlDetector> checks) { - assert res.isDirectory(); - File[] resourceDirs = res.listFiles(); - if (resourceDirs == null) { - return; - } - - // Sort alphabetically such that we can process related folder types at the - // same time - - Arrays.sort(resourceDirs); - ResourceFolderType type = null; - for (File dir : resourceDirs) { - type = ResourceFolderType.getFolderType(dir.getName()); - if (type != null) { - checkResourceFolder(project, main, dir, type, checks); - } - - if (mCanceled) { - return; - } - } - } - - private void checkResourceFolder( - @NonNull Project project, - @Nullable Project main, - @NonNull File dir, - @NonNull ResourceFolderType type, - @NonNull List<ResourceXmlDetector> checks) { - // Process the resource folder - File[] xmlFiles = dir.listFiles(); - if (xmlFiles != null && xmlFiles.length > 0) { - XmlVisitor visitor = getVisitor(type, checks); - if (visitor != null) { // if not, there are no applicable rules in this folder - for (File file : xmlFiles) { - if (LintUtils.isXmlFile(file)) { - XmlContext context = new XmlContext(this, project, main, file, type); - fireEvent(EventType.SCANNING_FILE, context); - visitor.visitFile(context, file); - if (mCanceled) { - return; - } - } - } - } - } - } - - /** Checks individual resources */ - private void checkIndividualResources( - @NonNull Project project, - @Nullable Project main, - @NonNull List<ResourceXmlDetector> xmlDetectors, - @NonNull List<File> files) { - for (File file : files) { - if (file.isDirectory()) { - // Is it a resource folder? - ResourceFolderType type = ResourceFolderType.getFolderType(file.getName()); - if (type != null && new File(file.getParentFile(), RES_FOLDER).exists()) { - // Yes. - checkResourceFolder(project, main, file, type, xmlDetectors); - } else if (file.getName().equals(RES_FOLDER)) { // Is it the res folder? - // Yes - checkResFolder(project, main, file, xmlDetectors); - } else { - mClient.log(null, "Unexpected folder %1$s; should be project, " + - "\"res\" folder or resource folder", file.getPath()); - continue; - } - } else if (file.isFile() && LintUtils.isXmlFile(file)) { - // Yes, find out its resource type - String folderName = file.getParentFile().getName(); - ResourceFolderType type = ResourceFolderType.getFolderType(folderName); - if (type != null) { - XmlVisitor visitor = getVisitor(type, xmlDetectors); - if (visitor != null) { - XmlContext context = new XmlContext(this, project, main, file, type); - fireEvent(EventType.SCANNING_FILE, context); - visitor.visitFile(context, file); - } - } - } - } - } - - /** - * Adds a listener to be notified of lint progress - * - * @param listener the listener to be added - */ - public void addLintListener(@NonNull LintListener listener) { - if (mListeners == null) { - mListeners = new ArrayList<LintListener>(1); - } - mListeners.add(listener); - } - - /** - * Removes a listener such that it is no longer notified of progress - * - * @param listener the listener to be removed - */ - public void removeLintListener(@NonNull LintListener listener) { - mListeners.remove(listener); - if (mListeners.size() == 0) { - mListeners = null; - } - } - - /** Notifies listeners, if any, that the given event has occurred */ - private void fireEvent(@NonNull LintListener.EventType type, @Nullable Context context) { - if (mListeners != null) { - for (int i = 0, n = mListeners.size(); i < n; i++) { - LintListener listener = mListeners.get(i); - listener.update(this, type, context); - } - } - } - - /** - * Wrapper around the lint client. This sits in the middle between a - * detector calling for example - * {@link LintClient#report(Context, Issue, Location, String, Object)} and - * the actual embedding tool, and performs filtering etc such that detectors - * and lint clients don't have to make sure they check for ignored issues or - * filtered out warnings. - */ - private class LintClientWrapper extends LintClient { - @NonNull - private final LintClient mDelegate; - - public LintClientWrapper(@NonNull LintClient delegate) { - mDelegate = delegate; - } - - @Override - public void report( - @NonNull Context context, - @NonNull Issue issue, - @NonNull Severity severity, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - assert mCurrentProject != null; - if (!mCurrentProject.getReportIssues()) { - return; - } - - Configuration configuration = context.getConfiguration(); - if (!configuration.isEnabled(issue)) { - if (issue != IssueRegistry.PARSER_ERROR && issue != IssueRegistry.LINT_ERROR) { - mDelegate.log(null, "Incorrect detector reported disabled issue %1$s", - issue.toString()); - } - return; - } - - if (configuration.isIgnored(context, issue, location, message, data)) { - return; - } - - if (severity == Severity.IGNORE) { - return; - } - - mDelegate.report(context, issue, severity, location, message, data); - } - - // Everything else just delegates to the embedding lint client - - @Override - @NonNull - public Configuration getConfiguration(@NonNull Project project) { - return mDelegate.getConfiguration(project); - } - - - @Override - public void log(@NonNull Severity severity, @Nullable Throwable exception, - @Nullable String format, @Nullable Object... args) { - mDelegate.log(exception, format, args); - } - - @Override - @NonNull - public String readFile(@NonNull File file) { - return mDelegate.readFile(file); - } - - @Override - @NonNull - public byte[] readBytes(@NonNull File file) throws IOException { - return mDelegate.readBytes(file); - } - - @Override - @NonNull - public List<File> getJavaSourceFolders(@NonNull Project project) { - return mDelegate.getJavaSourceFolders(project); - } - - @Override - @NonNull - public List<File> getJavaClassFolders(@NonNull Project project) { - return mDelegate.getJavaClassFolders(project); - } - - @Override - public @NonNull List<File> getJavaLibraries(@NonNull Project project) { - return mDelegate.getJavaLibraries(project); - } - - @Override - @Nullable - public IDomParser getDomParser() { - return mDelegate.getDomParser(); - } - - @Override - @NonNull - public Class<? extends Detector> replaceDetector( - @NonNull Class<? extends Detector> detectorClass) { - return mDelegate.replaceDetector(detectorClass); - } - - @Override - @NonNull - public SdkInfo getSdkInfo(@NonNull Project project) { - return mDelegate.getSdkInfo(project); - } - - @Override - @NonNull - public Project getProject(@NonNull File dir, @NonNull File referenceDir) { - return mDelegate.getProject(dir, referenceDir); - } - - @Override - @Nullable - public IJavaParser getJavaParser() { - return mDelegate.getJavaParser(); - } - - @Override - public File findResource(@NonNull String relativePath) { - return mDelegate.findResource(relativePath); - } - - @Override - @Nullable - public File getCacheDir(boolean create) { - return mDelegate.getCacheDir(create); - } - - @Override - @NonNull - protected ClassPathInfo getClassPath(@NonNull Project project) { - return mDelegate.getClassPath(project); - } - - @Override - public void log(@Nullable Throwable exception, @Nullable String format, - @Nullable Object... args) { - mDelegate.log(exception, format, args); - } - - @Override - @Nullable - public File getSdkHome() { - return mDelegate.getSdkHome(); - } - - @Override - @NonNull - public IAndroidTarget[] getTargets() { - return mDelegate.getTargets(); - } - - @Override - public int getHighestKnownApiLevel() { - return mDelegate.getHighestKnownApiLevel(); - } - } - - /** - * Requests another pass through the data for the given detector. This is - * typically done when a detector needs to do more expensive computation, - * but it only wants to do this once it <b>knows</b> that an error is - * present, or once it knows more specifically what to check for. - * - * @param detector the detector that should be included in the next pass. - * Note that the lint runner may refuse to run more than a couple - * of runs. - * @param scope the scope to be revisited. This must be a subset of the - * current scope ({@link #getScope()}, and it is just a performance hint; - * in particular, the detector should be prepared to be called on other - * scopes as well (since they may have been requested by other detectors). - * You can pall null to indicate "all". - */ - public void requestRepeat(@NonNull Detector detector, @Nullable EnumSet<Scope> scope) { - if (mRepeatingDetectors == null) { - mRepeatingDetectors = new ArrayList<Detector>(); - } - mRepeatingDetectors.add(detector); - - if (scope != null) { - if (mRepeatScope == null) { - mRepeatScope = scope; - } else { - mRepeatScope = EnumSet.copyOf(mRepeatScope); - mRepeatScope.addAll(scope); - } - } else { - mRepeatScope = Scope.ALL; - } - } - - // Unfortunately, ASMs nodes do not extend a common DOM node type with parent - // pointers, so we have to have multiple methods which pass in each type - // of node (class, method, field) to be checked. - - // TODO: The Quickfix should look for lint warnings placed *inside* warnings - // and warn that they won't apply to checks that are bytecode oriented! - - /** - * Returns whether the given issue is suppressed in the given method. - * - * @param issue the issue to be checked, or null to just check for "all" - * @param method the method containing the issue - * @return true if there is a suppress annotation covering the specific - * issue on this method - */ - public boolean isSuppressed(@Nullable Issue issue, @NonNull MethodNode method) { - if (method.invisibleAnnotations != null) { - @SuppressWarnings("unchecked") - List<AnnotationNode> annotations = method.invisibleAnnotations; - return isSuppressed(issue, annotations); - } - - return false; - } - - /** - * Returns whether the given issue is suppressed for the given field. - * - * @param issue the issue to be checked, or null to just check for "all" - * @param field the field potentially annotated with a suppress annotation - * @return true if there is a suppress annotation covering the specific - * issue on this field - */ - public boolean isSuppressed(@Nullable Issue issue, @NonNull FieldNode field) { - if (field.invisibleAnnotations != null) { - @SuppressWarnings("unchecked") - List<AnnotationNode> annotations = field.invisibleAnnotations; - return isSuppressed(issue, annotations); - } - - return false; - } - - /** - * Returns whether the given issue is suppressed in the given class. - * - * @param issue the issue to be checked, or null to just check for "all" - * @param classNode the class containing the issue - * @return true if there is a suppress annotation covering the specific - * issue in this class - */ - public boolean isSuppressed(@Nullable Issue issue, @NonNull ClassNode classNode) { - if (classNode.invisibleAnnotations != null) { - @SuppressWarnings("unchecked") - List<AnnotationNode> annotations = classNode.invisibleAnnotations; - return isSuppressed(issue, annotations); - } - - return false; - } - - private boolean isSuppressed(@Nullable Issue issue, List<AnnotationNode> annotations) { - for (AnnotationNode annotation : annotations) { - String desc = annotation.desc; - - // We could obey @SuppressWarnings("all") too, but no need to look for it - // because that annotation only has source retention. - - if (desc.endsWith(SUPPRESS_LINT_VMSIG)) { - if (annotation.values != null) { - for (int i = 0, n = annotation.values.size(); i < n; i += 2) { - String key = (String) annotation.values.get(i); - if (key.equals("value")) { //$NON-NLS-1$ - Object value = annotation.values.get(i + 1); - if (value instanceof String) { - String id = (String) value; - if (id.equalsIgnoreCase(SUPPRESS_ALL) || - issue != null && id.equalsIgnoreCase(issue.getId())) { - return true; - } - } else if (value instanceof List) { - @SuppressWarnings("rawtypes") - List list = (List) value; - for (Object v : list) { - if (v instanceof String) { - String id = (String) v; - if (id.equalsIgnoreCase(SUPPRESS_ALL) || (issue != null - && id.equalsIgnoreCase(issue.getId()))) { - return true; - } - } - } - } - } - } - } - } - } - - return false; - } - - /** - * Returns whether the given issue is suppressed in the given parse tree node. - * - * @param issue the issue to be checked, or null to just check for "all" - * @param scope the AST node containing the issue - * @return true if there is a suppress annotation covering the specific - * issue in this class - */ - public boolean isSuppressed(@NonNull Issue issue, @Nullable Node scope) { - while (scope != null) { - Class<? extends Node> type = scope.getClass(); - // The Lombok AST uses a flat hierarchy of node type implementation classes - // so no need to do instanceof stuff here. - if (type == VariableDefinition.class) { - // Variable - VariableDefinition declaration = (VariableDefinition) scope; - if (isSuppressed(issue, declaration.astModifiers())) { - return true; - } - } else if (type == MethodDeclaration.class) { - // Method - // Look for annotations on the method - MethodDeclaration declaration = (MethodDeclaration) scope; - if (isSuppressed(issue, declaration.astModifiers())) { - return true; - } - } else if (type == ConstructorDeclaration.class) { - // Constructor - // Look for annotations on the method - ConstructorDeclaration declaration = (ConstructorDeclaration) scope; - if (isSuppressed(issue, declaration.astModifiers())) { - return true; - } - } else if (type == ClassDeclaration.class) { - // Class - ClassDeclaration declaration = (ClassDeclaration) scope; - if (isSuppressed(issue, declaration.astModifiers())) { - return true; - } - } - - scope = scope.getParent(); - } - - return false; - } - - /** - * Returns true if the given AST modifier has a suppress annotation for the - * given issue (which can be null to check for the "all" annotation) - * - * @param issue the issue to be checked - * @param modifiers the modifier to check - * @return true if the issue or all issues should be suppressed for this - * modifier - */ - private static boolean isSuppressed(@Nullable Issue issue, @Nullable Modifiers modifiers) { - if (modifiers == null) { - return false; - } - StrictListAccessor<Annotation, Modifiers> annotations = modifiers.astAnnotations(); - if (annotations == null) { - return false; - } - - Iterator<Annotation> iterator = annotations.iterator(); - while (iterator.hasNext()) { - Annotation annotation = iterator.next(); - TypeReference t = annotation.astAnnotationTypeReference(); - String typeName = t.getTypeName(); - if (typeName.endsWith(SUPPRESS_LINT) - || typeName.endsWith("SuppressWarnings")) { //$NON-NLS-1$ - StrictListAccessor<AnnotationElement, Annotation> values = - annotation.astElements(); - if (values != null) { - Iterator<AnnotationElement> valueIterator = values.iterator(); - while (valueIterator.hasNext()) { - AnnotationElement element = valueIterator.next(); - AnnotationValue valueNode = element.astValue(); - if (valueNode == null) { - continue; - } - if (valueNode instanceof StringLiteral) { - StringLiteral literal = (StringLiteral) valueNode; - String value = literal.astValue(); - if (value.equalsIgnoreCase(SUPPRESS_ALL) || - issue != null && issue.getId().equalsIgnoreCase(value)) { - return true; - } - } else if (valueNode instanceof ArrayInitializer) { - ArrayInitializer array = (ArrayInitializer) valueNode; - StrictListAccessor<Expression, ArrayInitializer> expressions = - array.astExpressions(); - if (expressions == null) { - continue; - } - Iterator<Expression> arrayIterator = expressions.iterator(); - while (arrayIterator.hasNext()) { - Expression arrayElement = arrayIterator.next(); - if (arrayElement instanceof StringLiteral) { - String value = ((StringLiteral) arrayElement).astValue(); - if (value.equalsIgnoreCase(SUPPRESS_ALL) || (issue != null - && issue.getId().equalsIgnoreCase(value))) { - return true; - } - } - } - } - } - } - } - } - - return false; - } - - /** - * Returns whether the given issue is suppressed in the given XML DOM node. - * - * @param issue the issue to be checked, or null to just check for "all" - * @param node the DOM node containing the issue - * @return true if there is a suppress annotation covering the specific - * issue in this class - */ - public boolean isSuppressed(@NonNull Issue issue, @Nullable org.w3c.dom.Node node) { - if (node instanceof Attr) { - node = ((Attr) node).getOwnerElement(); - } - while (node != null) { - if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) { - Element element = (Element) node; - if (element.hasAttributeNS(TOOLS_URI, ATTR_IGNORE)) { - String ignore = element.getAttributeNS(TOOLS_URI, ATTR_IGNORE); - if (ignore.indexOf(',') == -1) { - if (ignore.equalsIgnoreCase(SUPPRESS_ALL) || (issue != null - && issue.getId().equalsIgnoreCase(ignore))) { - return true; - } - } else { - for (String id : ignore.split(",")) { //$NON-NLS-1$ - if (id.equalsIgnoreCase(SUPPRESS_ALL) || (issue != null - && issue.getId().equalsIgnoreCase(id))) { - return true; - } - } - } - } - } - - node = node.getParentNode(); - } - - return false; - } - - /** A pending class to be analyzed by {@link #checkClasses} */ - @VisibleForTesting - static class ClassEntry implements Comparable<ClassEntry> { - public final File file; - public final File jarFile; - public final File binDir; - public final byte[] bytes; - - public ClassEntry(File file, File jarFile, File binDir, byte[] bytes) { - super(); - this.file = file; - this.jarFile = jarFile; - this.binDir = binDir; - this.bytes = bytes; - } - - public String path() { - if (jarFile != null) { - return jarFile.getPath() + ':' + file.getPath(); - } else { - return file.getPath(); - } - } - - @Override - public int compareTo(ClassEntry other) { - String p1 = file.getPath(); - String p2 = other.file.getPath(); - int m1 = p1.length(); - int m2 = p2.length(); - int m = Math.min(m1, m2); - - for (int i = 0; i < m; i++) { - char c1 = p1.charAt(i); - char c2 = p2.charAt(i); - if (c1 != c2) { - // Sort Foo$Bar.class *after* Foo.class, even though $ < . - if (c1 == '.' && c2 == '$') { - return -1; - } - if (c1 == '$' && c2 == '.') { - return 1; - } - return c1 - c2; - } - } - - return (m == m1) ? -1 : 1; - } - - @Override - public String toString() { - return file.getPath(); - } - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintListener.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintListener.java deleted file mode 100644 index 2247a6d..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintListener.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.client.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Context; -import com.google.common.annotations.Beta; - -/** - * Interface implemented by listeners to be notified of lint events - * <p> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public interface LintListener { - /** The various types of events provided to lint listeners */ - public enum EventType { - /** A lint check is about to begin */ - STARTING, - - /** Lint is about to check the given project, see {@link Context#getProject()} */ - SCANNING_PROJECT, - - /** Lint is about to check the given library project, see {@link Context#getProject()} */ - SCANNING_LIBRARY_PROJECT, - - /** Lint is about to check the given file, see {@link Context#file} */ - SCANNING_FILE, - - /** A new pass was initiated */ - NEW_PHASE, - - /** The lint check was canceled */ - CANCELED, - - /** The lint check is done */ - COMPLETED, - }; - - /** - * Notifies listeners that the event of the given type has occurred. - * Additional information, such as the file being scanned, or the project - * being scanned, is available in the {@link Context} object (except for the - * {@link EventType#STARTING}, {@link EventType#CANCELED} or - * {@link EventType#COMPLETED} events which are fired outside of project - * contexts.) - * - * @param driver the driver running through the checks - * @param type the type of event that occurred - * @param context the context providing additional information - */ - public void update(@NonNull LintDriver driver, @NonNull EventType type, - @Nullable Context context); -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/SdkInfo.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/SdkInfo.java deleted file mode 100644 index 5ff7f90..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/SdkInfo.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.client.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.google.common.annotations.Beta; - -/** - * Information about SDKs - * <p> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public abstract class SdkInfo { - /** - * Returns true if the given child view is the same class or a sub class of - * the given parent view class - * - * @param parentViewFqcn the fully qualified class name of the parent view - * @param childViewFqcn the fully qualified class name of the child view - * @return true if the child view is a sub view of (or the same class as) - * the parent view - */ - public boolean isSubViewOf(@NonNull String parentViewFqcn, @NonNull String childViewFqcn) { - while (!childViewFqcn.equals("android.view.View")) { //$NON-NLS-1$ - if (parentViewFqcn.equals(childViewFqcn)) { - return true; - } - String parent = getParentViewClass(childViewFqcn); - if (parent == null) { - // Unknown view - err on the side of caution - return true; - } - childViewFqcn = parent; - } - - return false; - } - - - /** - * Returns the fully qualified name of the parent view, or null if the view - * is the root android.view.View class. - * - * @param fqcn the fully qualified class name of the view - * @return the fully qualified class name of the parent view, or null - */ - @Nullable - public abstract String getParentViewClass(@NonNull String fqcn); - - /** - * Returns the class name of the parent view, or null if the view is the - * root android.view.View class. This is the same as the - * {@link #getParentViewClass(String)} but without the package. - * - * @param name the view class name to look up the parent for (not including - * package) - * @return the view name of the parent - */ - @Nullable - public abstract String getParentViewName(@NonNull String name); - - // TODO: Add access to resource resolution here. -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/XmlVisitor.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/XmlVisitor.java deleted file mode 100644 index 816c028..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/XmlVisitor.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.client.api; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Detector.XmlScanner; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.XmlContext; -import com.google.common.annotations.Beta; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.RandomAccess; - -/** - * Specialized visitor for running detectors on an XML document. - * It operates in two phases: - * <ol> - * <li> First, it computes a set of maps where it generates a map from each - * significant element name, and each significant attribute name, to a list - * of detectors to consult for that element or attribute name. - * The set of element names or attribute names (or both) that a detector - * is interested in is provided by the detectors themselves. - * <li> Second, it iterates over the document a single time. For each element and - * attribute it looks up the list of interested detectors, and runs them. - * </ol> - * It also notifies all the detectors before and after the document is processed - * such that they can do pre- and post-processing. - * <p> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -class XmlVisitor { - private final Map<String, List<Detector.XmlScanner>> mElementToCheck = - new HashMap<String, List<Detector.XmlScanner>>(); - private final Map<String, List<Detector.XmlScanner>> mAttributeToCheck = - new HashMap<String, List<Detector.XmlScanner>>(); - private final List<Detector.XmlScanner> mDocumentDetectors = - new ArrayList<Detector.XmlScanner>(); - private final List<Detector.XmlScanner> mAllElementDetectors = - new ArrayList<Detector.XmlScanner>(); - private final List<Detector.XmlScanner> mAllAttributeDetectors = - new ArrayList<Detector.XmlScanner>(); - private final List<? extends Detector> mAllDetectors; - private final IDomParser mParser; - - // Really want this: - //<T extends List<Detector> & Detector.XmlScanner> XmlVisitor(IDomParser parser, - // T xmlDetectors) { - // but it makes client code tricky and ugly. - XmlVisitor(@NonNull IDomParser parser, @NonNull List<? extends Detector> xmlDetectors) { - mParser = parser; - mAllDetectors = xmlDetectors; - - // TODO: Check appliesTo() for files, and find a quick way to enable/disable - // rules when running through a full project! - for (Detector detector : xmlDetectors) { - Detector.XmlScanner xmlDetector = (XmlScanner) detector; - Collection<String> attributes = xmlDetector.getApplicableAttributes(); - if (attributes == XmlScanner.ALL) { - mAllAttributeDetectors.add(xmlDetector); - } else if (attributes != null) { - for (String attribute : attributes) { - List<Detector.XmlScanner> list = mAttributeToCheck.get(attribute); - if (list == null) { - list = new ArrayList<Detector.XmlScanner>(); - mAttributeToCheck.put(attribute, list); - } - list.add(xmlDetector); - } - } - Collection<String> elements = xmlDetector.getApplicableElements(); - if (elements == XmlScanner.ALL) { - mAllElementDetectors.add(xmlDetector); - } else if (elements != null) { - for (String element : elements) { - List<Detector.XmlScanner> list = mElementToCheck.get(element); - if (list == null) { - list = new ArrayList<Detector.XmlScanner>(); - mElementToCheck.put(element, list); - } - list.add(xmlDetector); - } - } - - if ((attributes == null || (attributes.size() == 0 - && attributes != XmlScanner.ALL)) - && (elements == null || (elements.size() == 0 - && elements != XmlScanner.ALL))) { - mDocumentDetectors.add(xmlDetector); - } - } - } - - void visitFile(@NonNull XmlContext context, @NonNull File file) { - assert LintUtils.isXmlFile(file); - context.parser = mParser; - - try { - if (context.document == null) { - context.document = mParser.parseXml(context); - if (context.document == null) { - // No need to log this; the parser should be reporting - // a full warning (such as IssueRegistry#PARSER_ERROR) - // with details, location, etc. - return; - } - if (context.document.getDocumentElement() == null) { - // Ignore empty documents - return; - } - } - - for (Detector check : mAllDetectors) { - check.beforeCheckFile(context); - } - - for (Detector.XmlScanner check : mDocumentDetectors) { - check.visitDocument(context, context.document); - } - - if (mElementToCheck.size() > 0 || mAttributeToCheck.size() > 0 - || mAllAttributeDetectors.size() > 0 || mAllElementDetectors.size() > 0) { - visitElement(context, context.document.getDocumentElement()); - } - - for (Detector check : mAllDetectors) { - check.afterCheckFile(context); - } - } finally { - if (context.document != null) { - mParser.dispose(context, context.document); - context.document = null; - } - } - } - - private void visitElement(@NonNull XmlContext context, @NonNull Element element) { - List<Detector.XmlScanner> elementChecks = mElementToCheck.get(element.getTagName()); - if (elementChecks != null) { - assert elementChecks instanceof RandomAccess; - for (int i = 0, n = elementChecks.size(); i < n; i++) { - Detector.XmlScanner check = elementChecks.get(i); - check.visitElement(context, element); - } - } - if (mAllElementDetectors.size() > 0) { - for (int i = 0, n = mAllElementDetectors.size(); i < n; i++) { - Detector.XmlScanner check = mAllElementDetectors.get(i); - check.visitElement(context, element); - } - } - - if (mAttributeToCheck.size() > 0 || mAllAttributeDetectors.size() > 0) { - NamedNodeMap attributes = element.getAttributes(); - for (int i = 0, n = attributes.getLength(); i < n; i++) { - Attr attribute = (Attr) attributes.item(i); - String name = attribute.getLocalName(); - if (name == null) { - name = attribute.getName(); - } - List<Detector.XmlScanner> list = mAttributeToCheck.get(name); - if (list != null) { - for (int j = 0, max = list.size(); j < max; j++) { - Detector.XmlScanner check = list.get(j); - check.visitAttribute(context, attribute); - } - } - if (mAllAttributeDetectors.size() > 0) { - for (int j = 0, max = mAllAttributeDetectors.size(); j < max; j++) { - Detector.XmlScanner check = mAllAttributeDetectors.get(j); - check.visitAttribute(context, attribute); - } - } - } - } - - // Visit children - NodeList childNodes = element.getChildNodes(); - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - Node child = childNodes.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE) { - visitElement(context, (Element) child); - } - } - - // Post hooks - if (elementChecks != null) { - for (int i = 0, n = elementChecks.size(); i < n; i++) { - Detector.XmlScanner check = elementChecks.get(i); - check.visitElementAfter(context, element); - } - } - if (mAllElementDetectors.size() > 0) { - for (int i = 0, n = mAllElementDetectors.size(); i < n; i++) { - Detector.XmlScanner check = mAllElementDetectors.get(i); - check.visitElementAfter(context, element); - } - } - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Category.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Category.java deleted file mode 100644 index ba8e5b5..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Category.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.google.common.annotations.Beta; - -/** - * A category is a container for related issues. - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public final class Category implements Comparable<Category> { - private final String mName; - private final String mExplanation; - private final int mPriority; - private final Category mParent; - - /** - * Creates a new {@link Category}. - * - * @param parent the name of a parent category, or null - * @param name the name of the category - * @param explanation an optional explanation of the category - * @param priority a sorting priority, with higher being more important - */ - private Category( - @Nullable Category parent, - @NonNull String name, - @Nullable String explanation, - int priority) { - mParent = parent; - mName = name; - mExplanation = explanation; - mPriority = priority; - } - - /** - * Creates a new top level {@link Category} with the given sorting priority. - * - * @param name the name of the category - * @param priority a sorting priority, with higher being more important - * @return a new category - */ - @NonNull - public static Category create(@NonNull String name, int priority) { - return new Category(null, name, null, priority); - } - - /** - * Creates a new top level {@link Category} with the given sorting priority. - * - * @param parent the name of a parent category, or null - * @param name the name of the category - * @param explanation an optional explanation of the category - * @param priority a sorting priority, with higher being more important - * @return a new category - */ - @NonNull - public static Category create( - @Nullable Category parent, - @NonNull String name, - @Nullable String explanation, - int priority) { - return new Category(parent, name, null, priority); - } - - /** - * Returns the parent category, or null if this is a top level category - * - * @return the parent category, or null if this is a top level category - */ - public Category getParent() { - return mParent; - } - - /** - * Returns the name of this category - * - * @return the name of this category - */ - public String getName() { - return mName; - } - - /** - * Returns an explanation for this category, or null - * - * @return an explanation for this category, or null - */ - public String getExplanation() { - return mExplanation; - } - - /** - * Returns a full name for this category. For a top level category, this is just - * the {@link #getName()} value, but for nested categories it will include the parent - * names as well. - * - * @return a full name for this category - */ - public String getFullName() { - if (mParent != null) { - return mParent.getFullName() + ':' + mName; - } else { - return mName; - } - } - - @Override - public int compareTo(Category other) { - if (other.mPriority == mPriority) { - if (mParent == other) { - return 1; - } else if (other.mParent == this) { - return -1; - } - } - return other.mPriority - mPriority; - } - - /** Issues related to running lint itself */ - public static final Category LINT = Category.create("Lint", 110); - - /** Issues related to correctness */ - public static final Category CORRECTNESS = Category.create("Correctness", 100); - - /** Issues related to security */ - public static final Category SECURITY = Category.create("Security", 90); - - /** Issues related to performance */ - public static final Category PERFORMANCE = Category.create("Performance", 80); - - /** Issues related to usability */ - public static final Category USABILITY = Category.create("Usability", 70); - - /** Issues related to accessibility */ - public static final Category A11Y = Category.create("Accessibility", 60); - - /** Issues related to internationalization */ - public static final Category I18N = Category.create("Internationalization", 50); - - // Sub categories - - /** Issues related to icons */ - public static final Category ICONS = Category.create(USABILITY, "Icons", null, 73); - - /** Issues related to typography */ - public static final Category TYPOGRAPHY = Category.create(USABILITY, "Typography", null, 76); - - /** Issues related to messages/strings */ - public static final Category MESSAGES = Category.create(CORRECTNESS, "Messages", null, 95); -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java deleted file mode 100644 index 68c25d9..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java +++ /dev/null @@ -1,663 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import static com.android.SdkConstants.CONSTRUCTOR_NAME; -import static com.android.SdkConstants.DOT_CLASS; -import static com.android.SdkConstants.DOT_JAVA; -import static com.android.tools.lint.detector.api.Location.SearchDirection.BACKWARD; -import static com.android.tools.lint.detector.api.Location.SearchDirection.EOL_BACKWARD; -import static com.android.tools.lint.detector.api.Location.SearchDirection.FORWARD; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.client.api.LintDriver; -import com.android.tools.lint.detector.api.Location.SearchHints; -import com.google.common.annotations.Beta; - -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldNode; -import org.objectweb.asm.tree.LineNumberNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; - -import java.io.File; -import java.util.List; - -/** - * A {@link Context} used when checking .class files. - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public class ClassContext extends Context { - private final File mBinDir; - /** The class file DOM root node */ - private ClassNode mClassNode; - /** The class file byte data */ - private byte[] mBytes; - /** The source file, if known/found */ - private File mSourceFile; - /** The contents of the source file, if source file is known/found */ - private String mSourceContents; - /** Whether we've searched for the source file (used to avoid repeated failed searches) */ - private boolean mSearchedForSource; - /** If the file is a relative path within a jar file, this is the jar file, otherwise null */ - private final File mJarFile; - /** Whether this class is part of a library (rather than corresponding to one of the - * source files in this project */ - private final boolean mFromLibrary; - - /** - * Construct a new {@link ClassContext} - * - * @param driver the driver running through the checks - * @param project the project containing the file being checked - * @param main the main project if this project is a library project, or - * null if this is not a library project. The main project is the - * root project of all library projects, not necessarily the - * directly including project. - * @param file the file being checked - * @param jarFile If the file is a relative path within a jar file, this is - * the jar file, otherwise null - * @param binDir the root binary directory containing this .class file. - * @param bytes the bytecode raw data - * @param classNode the bytecode object model - * @param fromLibrary whether this class is from a library rather than part - * of this project - * @param sourceContents initial contents of the Java source, if known, or - * null - */ - public ClassContext( - @NonNull LintDriver driver, - @NonNull Project project, - @Nullable Project main, - @NonNull File file, - @Nullable File jarFile, - @NonNull File binDir, - @NonNull byte[] bytes, - @NonNull ClassNode classNode, - boolean fromLibrary, - @Nullable String sourceContents) { - super(driver, project, main, file); - mJarFile = jarFile; - mBinDir = binDir; - mBytes = bytes; - mClassNode = classNode; - mFromLibrary = fromLibrary; - mSourceContents = sourceContents; - } - - /** - * Returns the raw bytecode data for this class file - * - * @return the byte array containing the bytecode data - */ - @NonNull - public byte[] getBytecode() { - return mBytes; - } - - /** - * Returns the bytecode object model - * - * @return the bytecode object model, never null - */ - @NonNull - public ClassNode getClassNode() { - return mClassNode; - } - - /** - * Returns the jar file, if any. If this is null, the .class file is a real file - * on disk, otherwise it represents a relative path within the jar file. - * - * @return the jar file, or null - */ - @Nullable - public File getJarFile() { - return mJarFile; - } - - /** - * Returns whether this class is part of a library (not this project). - * - * @return true if this class is part of a library - */ - public boolean isFromClassLibrary() { - return mFromLibrary; - } - - /** - * Returns the source file for this class file, if possible. - * - * @return the source file, or null - */ - @Nullable - public File getSourceFile() { - if (mSourceFile == null && !mSearchedForSource) { - mSearchedForSource = true; - - String source = mClassNode.sourceFile; - if (source == null) { - source = file.getName(); - if (source.endsWith(DOT_CLASS)) { - source = source.substring(0, source.length() - DOT_CLASS.length()) + DOT_JAVA; - } - int index = source.indexOf('$'); - if (index != -1) { - source = source.substring(0, index) + DOT_JAVA; - } - } - if (source != null) { - if (mJarFile != null) { - String relative = file.getParent() + File.separator + source; - List<File> sources = getProject().getJavaSourceFolders(); - for (File dir : sources) { - File sourceFile = new File(dir, relative); - if (sourceFile.exists()) { - mSourceFile = sourceFile; - break; - } - } - } else { - // Determine package - String topPath = mBinDir.getPath(); - String parentPath = file.getParentFile().getPath(); - if (parentPath.startsWith(topPath)) { - int start = topPath.length() + 1; - String relative = start > parentPath.length() ? // default package? - "" : parentPath.substring(start); - List<File> sources = getProject().getJavaSourceFolders(); - for (File dir : sources) { - File sourceFile = new File(dir, relative + File.separator + source); - if (sourceFile.exists()) { - mSourceFile = sourceFile; - break; - } - } - } - } - } - } - - return mSourceFile; - } - - /** - * Returns the contents of the source file for this class file, if found. - * - * @return the source contents, or "" - */ - @NonNull - public String getSourceContents() { - if (mSourceContents == null) { - File sourceFile = getSourceFile(); - if (sourceFile != null) { - mSourceContents = getClient().readFile(mSourceFile); - } - - if (mSourceContents == null) { - mSourceContents = ""; - } - } - - return mSourceContents; - } - - /** - * Returns the contents of the source file for this class file, if found. If - * {@code read} is false, do not read the source contents if it has not - * already been read. (This is primarily intended for the lint - * infrastructure; most client code would call {@link #getSourceContents()} - * .) - * - * @param read whether to read the source contents if it has not already - * been initialized - * @return the source contents, which will never be null if {@code read} is - * true, or null if {@code read} is false and the source contents - * hasn't already been read. - */ - @Nullable - public String getSourceContents(boolean read) { - if (read) { - return getSourceContents(); - } else { - return mSourceContents; - } - } - - /** - * Returns a location for the given source line number in this class file's - * source file, if available. - * - * @param line the line number (1-based, which is what ASM uses) - * @param patternStart optional pattern to search for in the source for - * range start - * @param patternEnd optional pattern to search for in the source for range - * end - * @param hints additional hints about the pattern search (provided - * {@code patternStart} is non null) - * @return a location, never null - */ - @NonNull - public Location getLocationForLine(int line, @Nullable String patternStart, - @Nullable String patternEnd, @Nullable SearchHints hints) { - File sourceFile = getSourceFile(); - if (sourceFile != null) { - // ASM line numbers are 1-based, and lint line numbers are 0-based - if (line != -1) { - return Location.create(sourceFile, getSourceContents(), line - 1, - patternStart, patternEnd, hints); - } else { - return Location.create(sourceFile); - } - } - - return Location.create(file); - } - - /** - * Reports an issue. - * <p> - * Detectors should only call this method if an error applies to the whole class - * scope and there is no specific method or field that applies to the error. - * If so, use - * {@link #report(Issue, MethodNode, Location, String, Object)} or - * {@link #report(Issue, FieldNode, Location, String, Object)}, such that - * suppress annotations are checked. - * - * @param issue the issue to report - * @param location the location of the issue, or null if not known - * @param message the message for this warning - * @param data any associated data, or null - */ - @Override - public void report( - @NonNull Issue issue, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - if (mDriver.isSuppressed(issue, mClassNode)) { - return; - } - ClassNode curr = mClassNode; - while (curr != null) { - ClassNode prev = curr; - curr = mDriver.getOuterClassNode(curr); - if (curr != null) { - if (prev.outerMethod != null) { - @SuppressWarnings("rawtypes") // ASM API - List methods = curr.methods; - for (Object m : methods) { - MethodNode method = (MethodNode) m; - if (method.name.equals(prev.outerMethod) - && method.desc.equals(prev.outerMethodDesc)) { - // Found the outer method for this anonymous class; continue - // reporting on it (which will also work its way up the parent - // class hierarchy) - if (method != null && mDriver.isSuppressed(issue, method)) { - return; - } - break; - } - } - } - if (mDriver.isSuppressed(issue, curr)) { - return; - } - } - } - - super.report(issue, location, message, data); - } - - // Unfortunately, ASMs nodes do not extend a common DOM node type with parent - // pointers, so we have to have multiple methods which pass in each type - // of node (class, method, field) to be checked. - - /** - * Reports an issue applicable to a given method node. - * - * @param issue the issue to report - * @param method the method scope the error applies to. The lint infrastructure - * will check whether there are suppress annotations on this method (or its enclosing - * class) and if so suppress the warning without involving the client. - * @param location the location of the issue, or null if not known - * @param message the message for this warning - * @param data any associated data, or null - */ - public void report( - @NonNull Issue issue, - @Nullable MethodNode method, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - if (method != null && mDriver.isSuppressed(issue, method)) { - return; - } - report(issue, location, message, data); // also checks the class node - } - - /** - * Reports an issue applicable to a given method node. - * - * @param issue the issue to report - * @param field the scope the error applies to. The lint infrastructure - * will check whether there are suppress annotations on this field (or its enclosing - * class) and if so suppress the warning without involving the client. - * @param location the location of the issue, or null if not known - * @param message the message for this warning - * @param data any associated data, or null - */ - public void report( - @NonNull Issue issue, - @Nullable FieldNode field, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - if (field != null && mDriver.isSuppressed(issue, field)) { - return; - } - report(issue, location, message, data); // also checks the class node - } - - /** - * Finds the line number closest to the given node - * - * @param node the instruction node to get a line number for - * @return the closest line number, or -1 if not known - */ - public static int findLineNumber(@NonNull AbstractInsnNode node) { - AbstractInsnNode curr = node; - - // First search backwards - while (curr != null) { - if (curr.getType() == AbstractInsnNode.LINE) { - return ((LineNumberNode) curr).line; - } - curr = curr.getPrevious(); - } - - // Then search forwards - curr = node; - while (curr != null) { - if (curr.getType() == AbstractInsnNode.LINE) { - return ((LineNumberNode) curr).line; - } - curr = curr.getNext(); - } - - return -1; - } - - /** - * Finds the line number closest to the given method declaration - * - * @param node the method node to get a line number for - * @return the closest line number, or -1 if not known - */ - public static int findLineNumber(@NonNull MethodNode node) { - if (node.instructions != null && node.instructions.size() > 0) { - return findLineNumber(node.instructions.get(0)); - } - - return -1; - } - - /** - * Finds the line number closest to the given class declaration - * - * @param node the method node to get a line number for - * @return the closest line number, or -1 if not known - */ - public static int findLineNumber(@NonNull ClassNode node) { - if (node.methods != null && !node.methods.isEmpty()) { - MethodNode firstMethod = getFirstRealMethod(node); - if (firstMethod != null) { - return ClassContext.findLineNumber(firstMethod); - } - } - - return -1; - } - - /** - * Returns a location for the given {@link ClassNode}, where class node is - * either the top level class, or an inner class, in the current context. - * - * @param classNode the class in the current context - * @return a location pointing to the class declaration, or as close to it - * as possible - */ - @NonNull - public Location getLocation(@NonNull ClassNode classNode) { - // Attempt to find a proper location for this class. This is tricky - // since classes do not have line number entries in the class file; we need - // to find a method, look up the corresponding line number then search - // around it for a suitable tag, such as the class name. - String pattern; - if (isAnonymousClass(classNode.name)) { - pattern = classNode.superName; - } else { - pattern = classNode.name; - } - int index = pattern.lastIndexOf('$'); - if (index != -1) { - pattern = pattern.substring(index + 1); - } - index = pattern.lastIndexOf('/'); - if (index != -1) { - pattern = pattern.substring(index + 1); - } - - return getLocationForLine(findLineNumber(classNode), pattern, null, - SearchHints.create(BACKWARD).matchJavaSymbol()); - } - - @Nullable - private static MethodNode getFirstRealMethod(@NonNull ClassNode classNode) { - // Return the first method in the class for line number purposes. Skip <init>, - // since it's typically not located near the real source of the method. - if (classNode.methods != null) { - @SuppressWarnings("rawtypes") // ASM API - List methods = classNode.methods; - for (Object m : methods) { - MethodNode method = (MethodNode) m; - if (method.name.charAt(0) != '<') { - return method; - } - } - - if (classNode.methods.size() > 0) { - return (MethodNode) classNode.methods.get(0); - } - } - - return null; - } - - /** - * Returns a location for the given {@link MethodNode}. - * - * @param methodNode the class in the current context - * @param classNode the class containing the method - * @return a location pointing to the class declaration, or as close to it - * as possible - */ - @NonNull - public Location getLocation(@NonNull MethodNode methodNode, - @NonNull ClassNode classNode) { - // Attempt to find a proper location for this class. This is tricky - // since classes do not have line number entries in the class file; we need - // to find a method, look up the corresponding line number then search - // around it for a suitable tag, such as the class name. - String pattern; - if (methodNode.name.equals(CONSTRUCTOR_NAME)) { - if (isAnonymousClass(classNode.name)) { - pattern = classNode.superName.substring(classNode.superName.lastIndexOf('/') + 1); - } else { - pattern = classNode.name.substring(classNode.name.lastIndexOf('$') + 1); - } - } else { - pattern = methodNode.name; - } - - return getLocationForLine(findLineNumber(methodNode), pattern, null, - SearchHints.create(EOL_BACKWARD).matchJavaSymbol()); - } - - /** - * Returns a location for the given {@link AbstractInsnNode}. - * - * @param instruction the instruction to look up the location for - * @return a location pointing to the instruction, or as close to it - * as possible - */ - @NonNull - public Location getLocation(@NonNull AbstractInsnNode instruction) { - SearchHints hints = SearchHints.create(FORWARD).matchJavaSymbol(); - String pattern = null; - if (instruction instanceof MethodInsnNode) { - MethodInsnNode call = (MethodInsnNode) instruction; - if (call.name.equals(CONSTRUCTOR_NAME)) { - pattern = call.owner; - hints = hints.matchConstructor(); - } else { - pattern = call.name; - } - int index = pattern.lastIndexOf('$'); - if (index != -1) { - pattern = pattern.substring(index + 1); - } - index = pattern.lastIndexOf('/'); - if (index != -1) { - pattern = pattern.substring(index + 1); - } - } - - int line = findLineNumber(instruction); - return getLocationForLine(line, pattern, null, hints); - } - - private static boolean isAnonymousClass(@NonNull String fqcn) { - int lastIndex = fqcn.lastIndexOf('$'); - if (lastIndex != -1 && lastIndex < fqcn.length() - 1) { - if (Character.isDigit(fqcn.charAt(lastIndex + 1))) { - return true; - } - } - return false; - } - - /** - * Converts from a VM owner name (such as foo/bar/Foo$Baz) to a - * fully qualified class name (such as foo.bar.Foo.Baz). - * - * @param owner the owner name to convert - * @return the corresponding fully qualified class name - */ - @NonNull - public static String getFqcn(@NonNull String owner) { - return owner.replace('/', '.').replace('$','.'); - } - - /** - * Computes a user-readable type signature from the given class owner, name - * and description. For example, for owner="foo/bar/Foo$Baz", name="foo", - * description="(I)V", it returns "void foo.bar.Foo.Bar#foo(int)". - * - * @param owner the class name - * @param name the method name - * @param desc the method description - * @return a user-readable string - */ - public static String createSignature(String owner, String name, String desc) { - StringBuilder sb = new StringBuilder(); - - if (desc != null) { - Type returnType = Type.getReturnType(desc); - sb.append(getTypeString(returnType)); - sb.append(' '); - } - - if (owner != null) { - sb.append(getFqcn(owner)); - } - if (name != null) { - sb.append('#'); - sb.append(name); - if (desc != null) { - Type[] argumentTypes = Type.getArgumentTypes(desc); - if (argumentTypes != null && argumentTypes.length > 0) { - sb.append('('); - boolean first = true; - for (Type type : argumentTypes) { - if (first) { - first = false; - } else { - sb.append(", "); - } - sb.append(getTypeString(type)); - } - sb.append(')'); - } - } - } - - return sb.toString(); - } - - private static String getTypeString(Type type) { - String s = type.getClassName(); - if (s.startsWith("java.lang.")) { //$NON-NLS-1$ - s = s.substring("java.lang.".length()); //$NON-NLS-1$ - } - - return s; - } - - /** - * Computes the internal class name of the given fully qualified class name. - * For example, it converts foo.bar.Foo.Bar into foo/bar/Foo$Bar - * - * @param fqcn the fully qualified class name - * @return the internal class name - */ - @NonNull - public static String getInternalName(@NonNull String fqcn) { - String[] parts = fqcn.split("\\."); //$NON-NLS-1$ - StringBuilder sb = new StringBuilder(); - String prev = null; - for (String part : parts) { - if (prev != null) { - if (Character.isUpperCase(prev.charAt(0))) { - sb.append('$'); - } else { - sb.append('/'); - } - } - sb.append(part); - prev = part; - } - - return sb.toString(); - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Context.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Context.java deleted file mode 100644 index 9ddd10a..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Context.java +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.client.api.Configuration; -import com.android.tools.lint.client.api.LintClient; -import com.android.tools.lint.client.api.LintDriver; -import com.android.tools.lint.client.api.SdkInfo; -import com.google.common.annotations.Beta; - -import java.io.File; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Context passed to the detectors during an analysis run. It provides - * information about the file being analyzed, it allows shared properties (so - * the detectors can share results), etc. - * <p> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public class Context { - /** - * The file being checked. Note that this may not always be to a concrete - * file. For example, in the {@link Detector#beforeCheckProject(Context)} - * method, the context file is the directory of the project. - */ - public final File file; - - /** The driver running through the checks */ - protected final LintDriver mDriver; - - /** The project containing the file being checked */ - @NonNull - private final Project mProject; - - /** - * The "main" project. For normal projects, this is the same as {@link #mProject}, - * but for library projects, it's the root project that includes (possibly indirectly) - * the various library projects and their library projects. - * <p> - * Note that this is a property on the {@link Context}, not the - * {@link Project}, since a library project can be included from multiple - * different top level projects, so there isn't <b>one</b> main project, - * just one per main project being analyzed with its library projects. - */ - private final Project mMainProject; - - /** The current configuration controlling which checks are enabled etc */ - private final Configuration mConfiguration; - - /** The contents of the file */ - private String mContents; - - /** - * Whether the lint job has been canceled. - * <p> - * Slow-running detectors should check this flag via - * {@link AtomicBoolean#get()} and abort if canceled - */ - @NonNull - public final AtomicBoolean canceled = new AtomicBoolean(); - - /** Map of properties to share results between detectors */ - private Map<String, Object> mProperties; - - /** - * Construct a new {@link Context} - * - * @param driver the driver running through the checks - * @param project the project containing the file being checked - * @param main the main project if this project is a library project, or - * null if this is not a library project. The main project is - * the root project of all library projects, not necessarily the - * directly including project. - * @param file the file being checked - */ - public Context( - @NonNull LintDriver driver, - @NonNull Project project, - @Nullable Project main, - @NonNull File file) { - this.file = file; - - mDriver = driver; - mProject = project; - mMainProject = main; - mConfiguration = project.getConfiguration(); - } - - /** - * Returns the scope for the lint job - * - * @return the scope, never null - */ - @NonNull - public EnumSet<Scope> getScope() { - return mDriver.getScope(); - } - - /** - * Returns the configuration for this project. - * - * @return the configuration, never null - */ - @NonNull - public Configuration getConfiguration() { - return mConfiguration; - } - - /** - * Returns the project containing the file being checked - * - * @return the project, never null - */ - @NonNull - public Project getProject() { - return mProject; - } - - /** - * Returns the main project if this project is a library project, or self - * if this is not a library project. The main project is the root project - * of all library projects, not necessarily the directly including project. - * - * @return the main project, never null - */ - @NonNull - public Project getMainProject() { - return mMainProject != null ? mMainProject : mProject; - } - - /** - * Returns the lint client requesting the lint check - * - * @return the client, never null - */ - @NonNull - public LintClient getClient() { - return mDriver.getClient(); - } - - /** - * Returns the driver running through the lint checks - * - * @return the driver - */ - @NonNull - public LintDriver getDriver() { - return mDriver; - } - - /** - * Returns the contents of the file. This may not be the contents of the - * file on disk, since it delegates to the {@link LintClient}, which in turn - * may decide to return the current edited contents of the file open in an - * editor. - * - * @return the contents of the given file, or null if an error occurs. - */ - @Nullable - public String getContents() { - if (mContents == null) { - mContents = mDriver.getClient().readFile(file); - } - - return mContents; - } - - /** - * Returns the value of the given named property, or null. - * - * @param name the name of the property - * @return the corresponding value, or null - */ - @Nullable - public Object getProperty(String name) { - if (mProperties == null) { - return null; - } - - return mProperties.get(name); - } - - /** - * Sets the value of the given named property. - * - * @param name the name of the property - * @param value the corresponding value - */ - public void setProperty(@NonNull String name, @Nullable Object value) { - if (value == null) { - if (mProperties != null) { - mProperties.remove(name); - } - } else { - if (mProperties == null) { - mProperties = new HashMap<String, Object>(); - } - mProperties.put(name, value); - } - } - - /** - * Gets the SDK info for the current project. - * - * @return the SDK info for the current project, never null - */ - @NonNull - public SdkInfo getSdkInfo() { - return mProject.getSdkInfo(); - } - - // ---- Convenience wrappers ---- (makes the detector code a bit leaner) - - /** - * Returns false if the given issue has been disabled. Convenience wrapper - * around {@link Configuration#getSeverity(Issue)}. - * - * @param issue the issue to check - * @return false if the issue has been disabled - */ - public boolean isEnabled(@NonNull Issue issue) { - return mConfiguration.isEnabled(issue); - } - - /** - * Reports an issue. Convenience wrapper around {@link LintClient#report} - * - * @param issue the issue to report - * @param location the location of the issue, or null if not known - * @param message the message for this warning - * @param data any associated data, or null - */ - public void report( - @NonNull Issue issue, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - Configuration configuration = mConfiguration; - - // If this error was computed for a context where the context corresponds to - // a project instead of a file, the actual error may be in a different project (e.g. - // a library project), so adjust the configuration as necessary. - if (location != null && location.getFile() != null) { - Project project = mDriver.findProjectFor(location.getFile()); - if (project != null) { - configuration = project.getConfiguration(); - } - } - - // If an error occurs in a library project, but you've disabled that check in the - // main project, disable it in the library project too. (In some cases you don't - // control the lint.xml of a library project, and besides, if you're not interested in - // a check for your main project you probably don't care about it in the library either.) - if (configuration != mConfiguration - && mConfiguration.getSeverity(issue) == Severity.IGNORE) { - return; - } - - Severity severity = configuration.getSeverity(issue); - if (severity == Severity.IGNORE) { - return; - } - - mDriver.getClient().report(this, issue, severity, location, message, data); - } - - /** - * Send an exception to the log. Convenience wrapper around {@link LintClient#log}. - * - * @param exception the exception, possibly null - * @param format the error message using {@link String#format} syntax, possibly null - * @param args any arguments for the format string - */ - public void log( - @Nullable Throwable exception, - @Nullable String format, - @Nullable Object... args) { - mDriver.getClient().log(exception, format, args); - } - - /** - * Returns the current phase number. The first pass is numbered 1. Only one pass - * will be performed, unless a {@link Detector} calls {@link #requestRepeat}. - * - * @return the current phase, usually 1 - */ - public int getPhase() { - return mDriver.getPhase(); - } - - /** - * Requests another pass through the data for the given detector. This is - * typically done when a detector needs to do more expensive computation, - * but it only wants to do this once it <b>knows</b> that an error is - * present, or once it knows more specifically what to check for. - * - * @param detector the detector that should be included in the next pass. - * Note that the lint runner may refuse to run more than a couple - * of runs. - * @param scope the scope to be revisited. This must be a subset of the - * current scope ({@link #getScope()}, and it is just a performance hint; - * in particular, the detector should be prepared to be called on other - * scopes as well (since they may have been requested by other detectors). - * You can pall null to indicate "all". - */ - public void requestRepeat(@NonNull Detector detector, @Nullable EnumSet<Scope> scope) { - mDriver.requestRepeat(detector, scope); - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/DefaultPosition.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/DefaultPosition.java deleted file mode 100644 index 72c8ee7..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/DefaultPosition.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import com.google.common.annotations.Beta; - -/** - * A simple offset-based position * - * <p> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public class DefaultPosition extends Position { - /** The line number (0-based where the first line is line 0) */ - private final int mLine; - - /** - * The column number (where the first character on the line is 0), or -1 if - * unknown - */ - private final int mColumn; - - /** The character offset */ - private final int mOffset; - - /** - * Creates a new {@link DefaultPosition} - * - * @param line the 0-based line number, or -1 if unknown - * @param column the 0-based column number, or -1 if unknown - * @param offset the offset, or -1 if unknown - */ - public DefaultPosition(int line, int column, int offset) { - mLine = line; - mColumn = column; - mOffset = offset; - } - - @Override - public int getLine() { - return mLine; - } - - @Override - public int getOffset() { - return mOffset; - } - - @Override - public int getColumn() { - return mColumn; - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Detector.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Detector.java deleted file mode 100644 index e2c5907..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Detector.java +++ /dev/null @@ -1,581 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.client.api.LintDriver; -import com.google.common.annotations.Beta; - -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import lombok.ast.AstVisitor; -import lombok.ast.MethodInvocation; - -/** - * A detector is able to find a particular problem. It might also be thought of as enforcing - * a rule, but "rule" is a bit overloaded in ADT terminology since ViewRules are used in - * the Rules API to allow views to specify designtime behavior in the graphical layout editor. - * <p> - * Each detector provides information about the issues it can find, such as an explanation - * of how to fix the issue, the priority, the category, etc. It also has an id which is - * used to persistently identify a particular type of error. - * <p> - * Detectors will be called in a predefined order: - * <ol> - * <li> Manifest file - * <li> Resource files, in alphabetical order by resource type - * (therefore, "layout" is checked before "values", "values-de" is checked before - * "values-en" but after "values", and so on. - * <li> Java sources - * <li> Java classes - * <li> Proguard files - * </ol> - * If a detector needs information when processing a file type that comes from a type of - * file later in the order above, they can request a second phase; see - * {@link LintDriver#requestRepeat}. - * <p> - * NOTE: Detectors might be constructed just once and shared between lint runs, so - * any per-detector state should be initialized and reset via the before/after - * methods. - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public abstract class Detector { - /** Specialized interface for detectors that scan Java source file parse trees */ - public interface JavaScanner { - /** - * Create a parse tree visitor to process the parse tree. All - * {@link JavaScanner} detectors must provide a visitor, unless they - * either return true from {@link #appliesToResourceRefs()} or return - * non null from {@link #getApplicableMethodNames()}. - * <p> - * If you return specific AST node types from - * {@link #getApplicableNodeTypes()}, then the visitor will <b>only</b> - * be called for the specific requested node types. This is more - * efficient, since it allows many detectors that apply to only a small - * part of the AST (such as method call nodes) to share iteration of the - * majority of the parse tree. - * <p> - * If you return null from {@link #getApplicableNodeTypes()}, then your - * visitor will be called from the top and all node types visited. - * <p> - * Note that a new visitor is created for each separate compilation - * unit, so you can store per file state in the visitor. - * - * @param context the {@link Context} for the file being analyzed - * @return a visitor, or null. - */ - @Nullable - AstVisitor createJavaVisitor(@NonNull JavaContext context); - - /** - * Return the types of AST nodes that the visitor returned from - * {@link #createJavaVisitor(JavaContext)} should visit. See the - * documentation for {@link #createJavaVisitor(JavaContext)} for details - * on how the shared visitor is used. - * <p> - * If you return null from this method, then the visitor will process - * the full tree instead. - * <p> - * Note that for the shared visitor, the return codes from the visit - * methods are ignored: returning true will <b>not</b> prune iteration - * of the subtree, since there may be other node types interested in the - * children. If you need to ensure that your visitor only processes a - * part of the tree, use a full visitor instead. See the - * OverdrawDetector implementation for an example of this. - * - * @return the list of applicable node types (AST node classes), or null - */ - @Nullable - List<Class<? extends lombok.ast.Node>> getApplicableNodeTypes(); - - /** - * Return the list of method names this detector is interested in, or - * null. If this method returns non-null, then any AST nodes that match - * a method call in the list will be passed to the - * {@link #visitMethod(JavaContext, AstVisitor, MethodInvocation)} - * method for processing. The visitor created by - * {@link #createJavaVisitor(JavaContext)} is also passed to that - * method, although it can be null. - * <p> - * This makes it easy to write detectors that focus on some fixed calls. - * For example, the StringFormatDetector uses this mechanism to look for - * "format" calls, and when found it looks around (using the AST's - * {@link lombok.ast.Node#getParent()} method) to see if it's called on - * a String class instance, and if so do its normal processing. Note - * that since it doesn't need to do any other AST processing, that - * detector does not actually supply a visitor. - * - * @return a set of applicable method names, or null. - */ - @Nullable - List<String> getApplicableMethodNames(); - - /** - * Method invoked for any method calls found that matches any names - * returned by {@link #getApplicableMethodNames()}. This also passes - * back the visitor that was created by - * {@link #createJavaVisitor(JavaContext)}, but a visitor is not - * required. It is intended for detectors that need to do additional AST - * processing, but also want the convenience of not having to look for - * method names on their own. - * - * @param context the context of the lint request - * @param visitor the visitor created from - * {@link #createJavaVisitor(JavaContext)}, or null - * @param node the {@link MethodInvocation} node for the invoked method - */ - void visitMethod( - @NonNull JavaContext context, - @Nullable AstVisitor visitor, - @NonNull MethodInvocation node); - - /** - * Returns whether this detector cares about Android resource references - * (such as {@code R.layout.main} or {@code R.string.app_name}). If it - * does, then the visitor will look for these patterns, and if found, it - * will invoke {@link #visitResourceReference} passing the resource type - * and resource name. It also passes the visitor, if any, that was - * created by {@link #createJavaVisitor(JavaContext)}, such that a - * detector can do more than just look for resources. - * - * @return true if this detector wants to be notified of R resource - * identifiers found in the code. - */ - boolean appliesToResourceRefs(); - - /** - * Called for any resource references (such as {@code R.layout.main} - * found in Java code, provided this detector returned {@code true} from - * {@link #appliesToResourceRefs()}. - * - * @param context the lint scanning context - * @param visitor the visitor created from - * {@link #createJavaVisitor(JavaContext)}, or null - * @param node the variable reference for the resource - * @param type the resource type, such as "layout" or "string" - * @param name the resource name, such as "main" from - * {@code R.layout.main} - * @param isFramework whether the resource is a framework resource - * (android.R) or a local project resource (R) - */ - void visitResourceReference( - @NonNull JavaContext context, - @Nullable AstVisitor visitor, - @NonNull lombok.ast.Node node, - @NonNull String type, - @NonNull String name, - boolean isFramework); - } - - /** Specialized interface for detectors that scan Java class files */ - public interface ClassScanner { - /** - * Checks the given class' bytecode for issues. - * - * @param context the context of the lint check, pointing to for example - * the file - * @param classNode the root class node - */ - void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode); - - /** - * Returns the list of node types (corresponding to the constants in the - * {@link AbstractInsnNode} class) that this scanner applies to. The - * {@link #checkInstruction(ClassContext, ClassNode, MethodNode, AbstractInsnNode)} - * method will be called for each match. - * - * @return an array containing all the node types this detector should be - * called for, or null if none. - */ - @Nullable - int[] getApplicableAsmNodeTypes(); - - /** - * Process a given instruction node, and register lint issues if - * applicable. - * - * @param context the context of the lint check, pointing to for example - * the file - * @param classNode the root class node - * @param method the method node containing the call - * @param instruction the actual instruction - */ - void checkInstruction(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull AbstractInsnNode instruction); - - /** - * Return the list of method call names (in VM format, e.g. "<init>" for - * constructors, etc) for method calls this detector is interested in, - * or null. T his will be used to dispatch calls to - * {@link #checkCall(ClassContext, ClassNode, MethodNode, MethodInsnNode)} - * for only the method calls in owners that the detector is interested - * in. - * <p> - * <b>NOTE</b>: If you return non null from this method, then <b>only</b> - * {@link #checkCall(ClassContext, ClassNode, MethodNode, MethodInsnNode)} - * will be called if a suitable method is found; - * {@link #checkClass(ClassContext, ClassNode)} will not be called under - * any circumstances. - * <p> - * This makes it easy to write detectors that focus on some fixed calls, - * and allows lint to make a single pass over the bytecode over a class, - * and efficiently dispatch method calls to any detectors that are - * interested in it. Without this, each new lint check interested in a - * single method, would be doing a complete pass through all the - * bytecode instructions of the class via the - * {@link #checkClass(ClassContext, ClassNode)} method, which would make - * each newly added lint check make lint slower. Now a single dispatch - * map is used instead, and for each encountered call in the single - * dispatch, it looks up in the map which if any detectors are - * interested in the given call name, and dispatches to each one in - * turn. - * - * @return a list of applicable method names, or null. - */ - @Nullable - List<String> getApplicableCallNames(); - - /** - * Just like {@link Detector#getApplicableCallNames()}, but for the owner - * field instead. The - * {@link #checkCall(ClassContext, ClassNode, MethodNode, MethodInsnNode)} - * method will be called for all {@link MethodInsnNode} instances where the - * owner field matches any of the members returned in this node. - * <p> - * Note that if your detector provides both a name and an owner, the - * method will be called for any nodes matching either the name <b>or</b> - * the owner, not only where they match <b>both</b>. Note also that it will - * be called twice - once for the name match, and (at least once) for the owner - * match. - * - * @return a list of applicable owner names, or null. - */ - @Nullable - List<String> getApplicableCallOwners(); - - /** - * Process a given method call node, and register lint issues if - * applicable. This is similar to the - * {@link #checkInstruction(ClassContext, ClassNode, MethodNode, AbstractInsnNode)} - * method, but has the additional advantage that it is only called for known - * method names or method owners, according to - * {@link #getApplicableCallNames()} and {@link #getApplicableCallOwners()}. - * - * @param context the context of the lint check, pointing to for example - * the file - * @param classNode the root class node - * @param method the method node containing the call - * @param call the actual method call node - */ - void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull MethodInsnNode call); - } - - /** Specialized interface for detectors that scan XML files */ - public interface XmlScanner { - /** - * Visit the given document. The detector is responsible for its own iteration - * through the document. - * @param context information about the document being analyzed - * @param document the document to examine - */ - void visitDocument(@NonNull XmlContext context, @NonNull Document document); - - /** - * Visit the given element. - * @param context information about the document being analyzed - * @param element the element to examine - */ - void visitElement(@NonNull XmlContext context, @NonNull Element element); - - /** - * Visit the given element after its children have been analyzed. - * @param context information about the document being analyzed - * @param element the element to examine - */ - void visitElementAfter(@NonNull XmlContext context, @NonNull Element element); - - /** - * Visit the given attribute. - * @param context information about the document being analyzed - * @param attribute the attribute node to examine - */ - void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute); - - /** - * Returns the list of elements that this detector wants to analyze. If non - * null, this detector will be called (specifically, the - * {@link #visitElement} method) for each matching element in the document. - * <p> - * If this method returns null, and {@link #getApplicableAttributes()} also returns - * null, then the {@link #visitDocument} method will be called instead. - * - * @return a collection of elements, or null, or the special - * {@link XmlScanner#ALL} marker to indicate that every single - * element should be analyzed. - */ - @Nullable - Collection<String> getApplicableElements(); - - /** - * Returns the list of attributes that this detector wants to analyze. If non - * null, this detector will be called (specifically, the - * {@link #visitAttribute} method) for each matching attribute in the document. - * <p> - * If this method returns null, and {@link #getApplicableElements()} also returns - * null, then the {@link #visitDocument} method will be called instead. - * - * @return a collection of attributes, or null, or the special - * {@link XmlScanner#ALL} marker to indicate that every single - * attribute should be analyzed. - */ - @Nullable - Collection<String> getApplicableAttributes(); - - /** - * Special marker collection returned by {@link #getApplicableElements()} or - * {@link #getApplicableAttributes()} to indicate that the check should be - * invoked on all elements or all attributes - */ - @NonNull - public static final List<String> ALL = new ArrayList<String>(0); // NOT Collections.EMPTY! - // We want to distinguish this from just an *empty* list returned by the caller! - } - - /** - * Runs the detector. This method will not be called for certain specialized - * detectors, such as {@link XmlScanner} and {@link JavaScanner}, where - * there are specialized analysis methods instead such as - * {@link XmlScanner#visitElement(XmlContext, Element)}. - * - * @param context the context describing the work to be done - */ - public void run(@NonNull Context context) { - } - - /** - * Returns true if this detector applies to the given file - * - * @param context the context to check - * @param file the file in the context to check - * @return true if this detector applies to the given context and file - */ - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return false; - } - - /** - * Analysis is about to begin, perform any setup steps. - * - * @param context the context for the check referencing the project, lint - * client, etc - */ - public void beforeCheckProject(@NonNull Context context) { - } - - /** - * Analysis has just been finished for the whole project, perform any - * cleanup or report issues that require project-wide analysis. - * - * @param context the context for the check referencing the project, lint - * client, etc - */ - public void afterCheckProject(@NonNull Context context) { - } - - /** - * Analysis is about to begin for the given library project, perform any setup steps. - * - * @param context the context for the check referencing the project, lint - * client, etc - */ - public void beforeCheckLibraryProject(@NonNull Context context) { - } - - /** - * Analysis has just been finished for the given library project, perform any - * cleanup or report issues that require library-project-wide analysis. - * - * @param context the context for the check referencing the project, lint - * client, etc - */ - public void afterCheckLibraryProject(@NonNull Context context) { - } - - /** - * Analysis is about to be performed on a specific file, perform any setup - * steps. - * <p> - * Note: When this method is called at the beginning of checking an XML - * file, the context is guaranteed to be an instance of {@link XmlContext}, - * and similarly for a Java source file, the context will be a - * {@link JavaContext} and so on. - * - * @param context the context for the check referencing the file to be - * checked, the project, etc. - */ - public void beforeCheckFile(@NonNull Context context) { - } - - /** - * Analysis has just been finished for a specific file, perform any cleanup - * or report issues found - * <p> - * Note: When this method is called at the end of checking an XML - * file, the context is guaranteed to be an instance of {@link XmlContext}, - * and similarly for a Java source file, the context will be a - * {@link JavaContext} and so on. - * - * @param context the context for the check referencing the file to be - * checked, the project, etc. - */ - public void afterCheckFile(@NonNull Context context) { - } - - /** - * Returns the expected speed of this detector - * - * @return the expected speed of this detector - */ - @NonNull - public Speed getSpeed() { - return Speed.NORMAL; - } - - // ---- Dummy implementations to make implementing XmlScanner easier: ---- - - @SuppressWarnings("javadoc") - public void visitDocument(@NonNull XmlContext context, @NonNull Document document) { - // This method must be overridden if your detector does - // not return something from getApplicableElements or - // getApplicableATtributes - assert false; - } - - @SuppressWarnings("javadoc") - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - // This method must be overridden if your detector returns - // tag names from getApplicableElements - assert false; - } - - @SuppressWarnings("javadoc") - public void visitElementAfter(@NonNull XmlContext context, @NonNull Element element) { - } - - @SuppressWarnings("javadoc") - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - // This method must be overridden if your detector returns - // attribute names from getApplicableAttributes - assert false; - } - - @SuppressWarnings("javadoc") - @Nullable - public Collection<String> getApplicableElements() { - return null; - } - - @Nullable - @SuppressWarnings("javadoc") - public Collection<String> getApplicableAttributes() { - return null; - } - - // ---- Dummy implementations to make implementing JavaScanner easier: ---- - - @Nullable @SuppressWarnings("javadoc") - public List<String> getApplicableMethodNames() { - return null; - } - - @Nullable @SuppressWarnings("javadoc") - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { - return null; - } - - @Nullable @SuppressWarnings("javadoc") - public List<Class<? extends lombok.ast.Node>> getApplicableNodeTypes() { - return null; - } - - @SuppressWarnings("javadoc") - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - } - - @SuppressWarnings("javadoc") - public boolean appliesToResourceRefs() { - return false; - } - - @SuppressWarnings("javadoc") - public void visitResourceReference(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull lombok.ast.Node node, @NonNull String type, @NonNull String name, - boolean isFramework) { - } - - // ---- Dummy implementations to make implementing a ClassScanner easier: ---- - - @SuppressWarnings("javadoc") - public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) { - } - - @SuppressWarnings("javadoc") - @Nullable - public List<String> getApplicableCallNames() { - return null; - } - - @SuppressWarnings("javadoc") - @Nullable - public List<String> getApplicableCallOwners() { - return null; - } - - @SuppressWarnings("javadoc") - public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull MethodInsnNode call) { - } - - @SuppressWarnings("javadoc") - @Nullable - public int[] getApplicableAsmNodeTypes() { - return null; - } - - @SuppressWarnings("javadoc") - public void checkInstruction(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull AbstractInsnNode instruction) { - } - -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Issue.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Issue.java deleted file mode 100644 index 70d3cf7..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Issue.java +++ /dev/null @@ -1,552 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.client.api.Configuration; -import com.android.tools.lint.client.api.IssueRegistry; -import com.google.common.annotations.Beta; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.EnumSet; -import java.util.List; - - -/** - * An issue is a potential bug in an Android application. An issue is discovered - * by a {@link Detector}, and has an associated {@link Severity}. - * <p> - * Issues and detectors are separate classes because a detector can discover - * multiple different issues as it's analyzing code, and we want to be able to - * different severities for different issues, the ability to suppress one but - * not other issues from the same detector, and so on. - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public final class Issue implements Comparable<Issue> { - private static final String HTTP_PREFIX = "http://"; //$NON-NLS-1$ - - private final String mId; - private final String mDescription; - private final String mExplanation; - private final Category mCategory; - private final int mPriority; - private final Severity mSeverity; - private String mMoreInfoUrl; - private boolean mEnabledByDefault = true; - private final EnumSet<Scope> mScope; - private List<EnumSet<Scope>> mAnalysisScopes; - private final Class<? extends Detector> mClass; - - // Use factory methods - private Issue( - @NonNull String id, - @NonNull String description, - @NonNull String explanation, - @NonNull Category category, - int priority, - @NonNull Severity severity, - @NonNull Class<? extends Detector> detectorClass, - @NonNull EnumSet<Scope> scope) { - super(); - mId = id; - mDescription = description; - mExplanation = explanation; - mCategory = category; - mPriority = priority; - mSeverity = severity; - mClass = detectorClass; - mScope = scope; - } - - /** - * Creates a new issue - * - * @param id the fixed id of the issue - * @param description the quick summary of the issue (one line) - * @param explanation a full explanation of the issue, with suggestions for - * how to fix it - * @param category the associated category, if any - * @param priority the priority, a number from 1 to 10 with 10 being most - * important/severe - * @param severity the default severity of the issue - * @param detectorClass the class of the detector to find this issue - * @param scope the scope of files required to analyze this issue - * @return a new {@link Issue} - */ - @NonNull - public static Issue create( - @NonNull String id, - @NonNull String description, - @NonNull String explanation, - @NonNull Category category, - int priority, - @NonNull Severity severity, - @NonNull Class<? extends Detector> detectorClass, - @NonNull EnumSet<Scope> scope) { - return new Issue(id, description, explanation, category, priority, severity, - detectorClass, scope); - } - - /** - * Returns the unique id of this issue. These should not change over time - * since they are used to persist the names of issues suppressed by the user - * etc. It is typically a single camel-cased word. - * - * @return the associated fixed id, never null and always unique - */ - @NonNull - public String getId() { - return mId; - } - - /** - * Briefly (one line) describes the kinds of checks performed by this rule - * - * @return a quick summary of the issue, never null - */ - @NonNull - public String getDescription() { - return mDescription; - } - - /** - * Describes the error found by this rule, e.g. - * "Buttons must define contentDescriptions". Preferably the explanation - * should also contain a description of how the problem should be solved. - * Additional info can be provided via {@link #getMoreInfo()}. - * <p> - * Note that the text may contain some simple markup, such as *'s around sentences - * for bold text, and back quotes (`) for code fragments. You can obtain - * the text without this markup by calling {@link #getExplanationAsSimpleText()}, - * and you can obtain the text as annotated HTML by calling - * {@link #getExplanationAsHtml()}. - * - * @return an explanation of the issue, never null. - */ - @NonNull - public String getExplanation() { - return mExplanation; - } - - /** - * Like {@link #getExplanation()}, but returns the text as properly escaped - * and marked up HTML, where http URLs are linked, where words with asterisks - * such as *this* are shown in bold, etc. - * - * @return the explanation of the issue, never null - */ - @NonNull - public String getExplanationAsHtml() { - return convertMarkup(mExplanation, true /* html */); - } - - /** - * Like {@link #getExplanation()}, but returns the text as properly escaped - * and marked up HTML, where http URLs are linked, where words with asterisks - * such as *this* are shown in bold, etc. - * - * @return the explanation of the issue, never null - */ - @NonNull - public String getExplanationAsSimpleText() { - return convertMarkup(mExplanation, false /* not html = text */); - } - - /** - * The primary category of the issue - * - * @return the primary category of the issue, never null - */ - @NonNull - public Category getCategory() { - return mCategory; - } - - /** - * Returns a priority, in the range 1-10, with 10 being the most severe and - * 1 the least - * - * @return a priority from 1 to 10 - */ - public int getPriority() { - return mPriority; - } - - /** - * Returns the default severity of the issues found by this detector (some - * tools may allow the user to specify custom severities for detectors). - * <p> - * Note that even though the normal way for an issue to be disabled is for - * the {@link Configuration} to return {@link Severity#IGNORE}, there is a - * {@link #isEnabledByDefault()} method which can be used to turn off issues - * by default. This is done rather than just having the severity as the only - * attribute on the issue such that an issue can be configured with an - * appropriate severity (such as {@link Severity#ERROR}) even when issues - * are disabled by default for example because they are experimental or not - * yet stable. - * - * @return the severity of the issues found by this detector - */ - @NonNull - public Severity getDefaultSeverity() { - return mSeverity; - } - - /** - * Returns a link (a URL string) to more information, or null - * - * @return a link to more information, or null - */ - @Nullable - public String getMoreInfo() { - return mMoreInfoUrl; - } - - /** - * Returns whether this issue should be enabled by default, unless the user - * has explicitly disabled it. - * - * @return true if this issue should be enabled by default - */ - public boolean isEnabledByDefault() { - return mEnabledByDefault; - } - - /** - * Returns the scope required to analyze the code to detect this issue. - * This is determined by the detectors which reports the issue. - * - * @return the required scope - */ - @NonNull - public EnumSet<Scope> getScope() { - return mScope; - } - - /** - * Sorts the detectors alphabetically by id. This is intended to make it - * convenient to store settings for detectors in a fixed order. It is not - * intended as the order to be shown to the user; for that, a tool embedding - * lint might consider the priorities, categories, severities etc of the - * various detectors. - * - * @param other the {@link Issue} to compare this issue to - */ - @Override - public int compareTo(Issue other) { - return getId().compareTo(other.getId()); - } - - /** - * Sets a more info URL string - * - * @param moreInfoUrl url string - * @return this, for constructor chaining - */ - @NonNull - public Issue setMoreInfo(@NonNull String moreInfoUrl) { - mMoreInfoUrl = moreInfoUrl; - return this; - } - - /** - * Sets whether this issue is enabled by default. - * - * @param enabledByDefault whether the issue should be enabled by default - * @return this, for constructor chaining - */ - @NonNull - public Issue setEnabledByDefault(boolean enabledByDefault) { - mEnabledByDefault = enabledByDefault; - return this; - } - - /** - * Returns the sets of scopes required to analyze this issue, or null if all - * scopes named by {@link Issue#getScope()} are necessary. Note that only - * <b>one</b> match out of this collection is required, not all, and that - * the scope set returned by {@link #getScope()} does not have to be returned - * by this method, but is always implied to be included. - * <p> - * The scopes returned by {@link Issue#getScope()} list all the various - * scopes that are <b>affected</b> by this issue, meaning the detector - * should consider it. Frequently, the detector must analyze all these - * scopes in order to properly decide whether an issue is found. For - * example, the unused resource detector needs to consider both the XML - * resource files and the Java source files in order to decide if a resource - * is unused. If it analyzes just the Java files for example, it might - * incorrectly conclude that a resource is unused because it did not - * discover a resource reference in an XML file. - * <p> - * However, there are other issues where the issue can occur in a variety of - * files, but the detector can consider each in isolation. For example, the - * API checker is affected by both XML files and Java class files (detecting - * both layout constructor references in XML layout files as well as code - * references in .class files). It doesn't have to analyze both; it is - * capable of incrementally analyzing just an XML file, or just a class - * file, without considering the other. - * <p> - * The required scope list provides a list of scope sets that can be used to - * analyze this issue. For each scope set, all the scopes must be matched by - * the incremental analysis, but any one of the scope sets can be analyzed - * in isolation. - * <p> - * The required scope list is not required to include the full scope set - * returned by {@link #getScope()}; that set is always assumed to be - * included. - * <p> - * NOTE: You would normally call {@link #isAdequate(EnumSet)} rather - * than calling this method directly. - * - * @return a list of required scopes, or null. - */ - @Nullable - public Collection<EnumSet<Scope>> getAnalysisScopes() { - return mAnalysisScopes; - } - - /** - * Sets the collection of scopes that are allowed to be analyzed independently. - * See the {@link #getAnalysisScopes()} method for a full explanation. - * Note that you usually want to just call {@link #addAnalysisScope(EnumSet)} - * instead of constructing a list up front and passing it in here. This - * method exists primarily such that commonly used share sets of analysis - * scopes can be reused and set directly. - * - * @param required the collection of scopes - * @return this, for constructor chaining - */ - public Issue setAnalysisScopes(@Nullable List<EnumSet<Scope>> required) { - mAnalysisScopes = required; - - return this; - } - - /** - * Returns true if the given scope is adequate for analyzing this issue. - * This looks through the analysis scopes (see - * {@link #addAnalysisScope(EnumSet)}) and if the scope passed in fully - * covers at least one of them, or if it covers the scope of the issue - * itself (see {@link #getScope()}, which should be a superset of all the - * analysis scopes) returns true. - * <p> - * The scope set returned by {@link Issue#getScope()} lists all the various - * scopes that are <b>affected</b> by this issue, meaning the detector - * should consider it. Frequently, the detector must analyze all these - * scopes in order to properly decide whether an issue is found. For - * example, the unused resource detector needs to consider both the XML - * resource files and the Java source files in order to decide if a resource - * is unused. If it analyzes just the Java files for example, it might - * incorrectly conclude that a resource is unused because it did not - * discover a resource reference in an XML file. - * <p> - * However, there are other issues where the issue can occur in a variety of - * files, but the detector can consider each in isolation. For example, the - * API checker is affected by both XML files and Java class files (detecting - * both layout constructor references in XML layout files as well as code - * references in .class files). It doesn't have to analyze both; it is - * capable of incrementally analyzing just an XML file, or just a class - * file, without considering the other. - * <p> - * An issue can register additional scope sets that can are adequate - * for analyzing the issue, by calling {@link #addAnalysisScope(EnumSet)}. - * This method returns true if the given scope matches one or more analysis - * scope, or the overall scope. - * - * @param scope the scope available for analysis - * @return true if this issue can be analyzed with the given available scope - */ - public boolean isAdequate(@NonNull EnumSet<Scope> scope) { - if (scope.containsAll(mScope)) { - return true; - } - - if (mAnalysisScopes != null) { - for (EnumSet<Scope> analysisScope : mAnalysisScopes) { - if (mScope.containsAll(analysisScope)) { - return true; - } - } - } - - if (this == IssueRegistry.LINT_ERROR || this == IssueRegistry.PARSER_ERROR) { - return true; - } - - return false; - } - - /** - * Adds a scope set that can be analyzed independently to uncover this issue. - * See the {@link #getAnalysisScopes()} method for a full explanation. - * Note that the {@link #getScope()} does not have to be added here; it is - * always considered an analysis scope. - * - * @param scope the additional scope which can analyze this issue independently - * @return this, for constructor chaining - */ - public Issue addAnalysisScope(@Nullable EnumSet<Scope> scope) { - if (mAnalysisScopes == null) { - mAnalysisScopes = new ArrayList<EnumSet<Scope>>(2); - } - mAnalysisScopes.add(scope); - - return this; - } - - /** - * Returns the class of the detector to use to find this issue - * - * @return the class of the detector to use to find this issue - */ - @NonNull - public Class<? extends Detector> getDetectorClass() { - return mClass; - } - - @Override - public String toString() { - return mId; - } - - /** - * Converts the given markup text to HTML or text, depending on the. - * <p> - * This will recognize the following formatting conventions: - * <ul> - * <li>HTTP urls (http://...) - * <li>Sentences immediately surrounded by * will be shown as bold. - * <li>Sentences immediately surrounded by ` will be shown using monospace - * fonts - * </ul> - * Furthermore, newlines are converted to br's when converting newlines. - * Note: It does not insert {@code <html>} tags around the fragment for HTML output. - * <p> - * TODO: Consider switching to the restructured text format - - * http://docutils.sourceforge.net/docs/user/rst/quickstart.html - * - * @param text the text to be formatted - * @param html whether to convert into HTML or text - * @return the corresponding HTML or text properly formatted - */ - @NonNull - public static String convertMarkup(@NonNull String text, boolean html) { - StringBuilder sb = new StringBuilder(3 * text.length() / 2); - - char prev = 0; - int flushIndex = 0; - int n = text.length(); - for (int i = 0; i < n; i++) { - char c = text.charAt(i); - if ((c == '*' || c == '`' && i < n - 1)) { - // Scout ahead for range end - if (!Character.isLetterOrDigit(prev) - && !Character.isWhitespace(text.charAt(i + 1))) { - // Found * or ~ immediately before a letter, and not in the middle of a word - // Find end - int end = text.indexOf(c, i + 1); - if (end != -1 && (end == n - 1 || !Character.isLetter(text.charAt(end + 1)))) { - if (i > flushIndex) { - appendEscapedText(sb, text, html, flushIndex, i); - } - if (html) { - String tag = c == '*' ? "b" : "code"; //$NON-NLS-1$ //$NON-NLS-2$ - sb.append('<').append(tag).append('>'); - appendEscapedText(sb, text, html, i + 1, end); - sb.append('<').append('/').append(tag).append('>'); - } else { - appendEscapedText(sb, text, html, i + 1, end); - } - flushIndex = end + 1; - i = flushIndex - 1; // -1: account for the i++ in the loop - } - } - } else if (html && c == 'h' && i < n - 1 && text.charAt(i + 1) == 't' - && text.startsWith(HTTP_PREFIX, i) && !Character.isLetterOrDigit(prev)) { - // Find url end - int end = i + HTTP_PREFIX.length(); - while (end < n) { - char d = text.charAt(end); - if (Character.isWhitespace(d)) { - break; - } - end++; - } - char last = text.charAt(end - 1); - if (last == '.' || last == ')' || last == '!') { - end--; - } - if (end > i + HTTP_PREFIX.length()) { - if (i > flushIndex) { - appendEscapedText(sb, text, html, flushIndex, i); - } - - String url = text.substring(i, end); - sb.append("<a href=\""); //$NON-NLS-1$ - sb.append(url); - sb.append('"').append('>'); - sb.append(url); - sb.append("</a>"); //$NON-NLS-1$ - - flushIndex = end; - i = flushIndex - 1; // -1: account for the i++ in the loop - } - } - prev = c; - } - - if (flushIndex < n) { - appendEscapedText(sb, text, html, flushIndex, n); - } - - return sb.toString(); - } - - static void appendEscapedText(StringBuilder sb, String text, boolean html, - int start, int end) { - if (html) { - for (int i = start; i < end; i++) { - char c = text.charAt(i); - if (c == '<') { - sb.append("<"); //$NON-NLS-1$ - } else if (c == '&') { - sb.append("&"); //$NON-NLS-1$ - } else if (c == '\n') { - sb.append("<br/>\n"); - } else { - if (c > 255) { - sb.append("&#"); //$NON-NLS-1$ - sb.append(Integer.toString(c)); - sb.append(';'); - } else { - sb.append(c); - } - } - } - } else { - for (int i = start; i < end; i++) { - char c = text.charAt(i); - sb.append(c); - } - } - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/JavaContext.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/JavaContext.java deleted file mode 100644 index ae86568..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/JavaContext.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.client.api.IJavaParser; -import com.android.tools.lint.client.api.LintDriver; - -import java.io.File; - -import lombok.ast.Node; - -/** - * A {@link Context} used when checking Java files. - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -public class JavaContext extends Context { - /** The parse tree */ - public Node compilationUnit; - /** The parser which produced the parse tree */ - public IJavaParser parser; - - /** - * Constructs a {@link JavaContext} for running lint on the given file, with - * the given scope, in the given project reporting errors to the given - * client. - * - * @param driver the driver running through the checks - * @param project the project to run lint on which contains the given file - * @param main the main project if this project is a library project, or - * null if this is not a library project. The main project is - * the root project of all library projects, not necessarily the - * directly including project. - * @param file the file to be analyzed - */ - public JavaContext( - @NonNull LintDriver driver, - @NonNull Project project, - @Nullable Project main, - @NonNull File file) { - super(driver, project, main, file); - } - - /** - * Returns a location for the given node - * - * @param node the AST node to get a location for - * @return a location for the given node - */ - @NonNull - public Location getLocation(@NonNull Node node) { - if (parser != null) { - return parser.getLocation(this, node); - } - - return new Location(file, null, null); - } - - @Override - public void report(@NonNull Issue issue, @Nullable Location location, - @NonNull String message, @Nullable Object data) { - if (mDriver.isSuppressed(issue, compilationUnit)) { - return; - } - super.report(issue, location, message, data); - } - - /** - * Reports an issue applicable to a given AST node. The AST node is used as the - * scope to check for suppress lint annotations. - * - * @param issue the issue to report - * @param scope the AST node scope the error applies to. The lint infrastructure - * will check whether there are suppress annotations on this node (or its enclosing - * nodes) and if so suppress the warning without involving the client. - * @param location the location of the issue, or null if not known - * @param message the message for this warning - * @param data any associated data, or null - */ - public void report( - @NonNull Issue issue, - @Nullable Node scope, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - if (scope != null && mDriver.isSuppressed(issue, scope)) { - return; - } - super.report(issue, location, message, data); - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LayoutDetector.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LayoutDetector.java deleted file mode 100644 index b24c1a9..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LayoutDetector.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; -import static com.android.SdkConstants.ATTR_PADDING; -import static com.android.SdkConstants.ATTR_PADDING_BOTTOM; -import static com.android.SdkConstants.ATTR_PADDING_LEFT; -import static com.android.SdkConstants.ATTR_PADDING_RIGHT; -import static com.android.SdkConstants.ATTR_PADDING_TOP; -import static com.android.SdkConstants.VALUE_FILL_PARENT; -import static com.android.SdkConstants.VALUE_MATCH_PARENT; - -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -import com.google.common.annotations.Beta; - -import org.w3c.dom.Element; - -/** - * Abstract class specifically intended for layout detectors which provides some - * common utility methods shared by layout detectors. - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public abstract class LayoutDetector extends ResourceXmlDetector { - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.LAYOUT; - } - - private static boolean isFillParent(@NonNull Element element, @NonNull String dimension) { - String width = element.getAttributeNS(ANDROID_URI, dimension); - return width.equals(VALUE_MATCH_PARENT) || width.equals(VALUE_FILL_PARENT); - } - - protected static boolean isWidthFillParent(@NonNull Element element) { - return isFillParent(element, ATTR_LAYOUT_WIDTH); - } - - protected static boolean isHeightFillParent(@NonNull Element element) { - return isFillParent(element, ATTR_LAYOUT_HEIGHT); - } - - protected boolean hasPadding(@NonNull Element root) { - return root.hasAttributeNS(ANDROID_URI, ATTR_PADDING) - || root.hasAttributeNS(ANDROID_URI, ATTR_PADDING_LEFT) - || root.hasAttributeNS(ANDROID_URI, ATTR_PADDING_RIGHT) - || root.hasAttributeNS(ANDROID_URI, ATTR_PADDING_TOP) - || root.hasAttributeNS(ANDROID_URI, ATTR_PADDING_BOTTOM); - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java deleted file mode 100644 index 60c9e97..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java +++ /dev/null @@ -1,755 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import static com.android.SdkConstants.ANDROID_MANIFEST_XML; -import static com.android.SdkConstants.BIN_FOLDER; -import static com.android.SdkConstants.DOT_XML; -import static com.android.SdkConstants.ID_PREFIX; -import static com.android.SdkConstants.NEW_ID_PREFIX; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.FolderTypeRelationship; -import com.android.resources.ResourceFolderType; -import com.android.resources.ResourceType; -import com.android.tools.lint.client.api.LintClient; -import com.android.utils.PositionXmlParser; -import com.google.common.annotations.Beta; -import com.google.common.base.Splitter; -import com.google.common.collect.Iterables; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldNode; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.io.File; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.List; - -import lombok.ast.ImportDeclaration; - - -/** - * Useful utility methods related to lint. - * <p> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public class LintUtils { - /** - * Format a list of strings, and cut of the list at {@code maxItems} if the - * number of items are greater. - * - * @param strings the list of strings to print out as a comma separated list - * @param maxItems the maximum number of items to print - * @return a comma separated list - */ - @NonNull - public static String formatList(@NonNull List<String> strings, int maxItems) { - StringBuilder sb = new StringBuilder(20 * strings.size()); - - for (int i = 0, n = strings.size(); i < n; i++) { - if (sb.length() > 0) { - sb.append(", "); //$NON-NLS-1$ - } - sb.append(strings.get(i)); - - if (maxItems > 0 && i == maxItems - 1 && n > maxItems) { - sb.append(String.format("... (%1$d more)", n - i - 1)); - break; - } - } - - return sb.toString(); - } - - /** - * Determine if the given type corresponds to a resource that has a unique - * file - * - * @param type the resource type to check - * @return true if the given type corresponds to a file-type resource - */ - public static boolean isFileBasedResourceType(@NonNull ResourceType type) { - List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type); - for (ResourceFolderType folderType : folderTypes) { - if (folderType != ResourceFolderType.VALUES) { - if (type == ResourceType.ID) { - return false; - } - return true; - } - } - return false; - } - - /** - * Returns true if the given file represents an XML file - * - * @param file the file to be checked - * @return true if the given file is an xml file - */ - public static boolean isXmlFile(@NonNull File file) { - String string = file.getName(); - return string.regionMatches(true, string.length() - DOT_XML.length(), - DOT_XML, 0, DOT_XML.length()); - } - - /** - * Case insensitive ends with - * - * @param string the string to be tested whether it ends with the given - * suffix - * @param suffix the suffix to check - * @return true if {@code string} ends with {@code suffix}, - * case-insensitively. - */ - public static boolean endsWith(@NonNull String string, @NonNull String suffix) { - return string.regionMatches(true /* ignoreCase */, string.length() - suffix.length(), - suffix, 0, suffix.length()); - } - - /** - * Case insensitive starts with - * - * @param string the string to be tested whether it starts with the given prefix - * @param prefix the prefix to check - * @param offset the offset to start checking with - * @return true if {@code string} starts with {@code prefix}, - * case-insensitively. - */ - public static boolean startsWith(@NonNull String string, @NonNull String prefix, int offset) { - return string.regionMatches(true /* ignoreCase */, offset, prefix, 0, prefix.length()); - } - - /** - * Returns the basename of the given filename, unless it's a dot-file such as ".svn". - * - * @param fileName the file name to extract the basename from - * @return the basename (the filename without the file extension) - */ - public static String getBaseName(@NonNull String fileName) { - int extension = fileName.indexOf('.'); - if (extension > 0) { - return fileName.substring(0, extension); - } else { - return fileName; - } - } - - /** - * Returns the children elements of the given node - * - * @param node the parent node - * @return a list of element children, never null - */ - @NonNull - public static List<Element> getChildren(@NonNull Node node) { - NodeList childNodes = node.getChildNodes(); - List<Element> children = new ArrayList<Element>(childNodes.getLength()); - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - Node child = childNodes.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE) { - children.add((Element) child); - } - } - - return children; - } - - /** - * Returns the <b>number</b> of children of the given node - * - * @param node the parent node - * @return the count of element children - */ - public static int getChildCount(@NonNull Node node) { - NodeList childNodes = node.getChildNodes(); - int childCount = 0; - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - Node child = childNodes.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE) { - childCount++; - } - } - - return childCount; - } - - /** - * Returns true if the given element is the root element of its document - * - * @param element the element to test - * @return true if the element is the root element - */ - public static boolean isRootElement(Element element) { - return element == element.getOwnerDocument().getDocumentElement(); - } - - /** - * Returns the given id without an {@code @id/} or {@code @+id} prefix - * - * @param id the id to strip - * @return the stripped id, never null - */ - @NonNull - public static String stripIdPrefix(@Nullable String id) { - if (id == null) { - return ""; - } else if (id.startsWith(NEW_ID_PREFIX)) { - return id.substring(NEW_ID_PREFIX.length()); - } else if (id.startsWith(ID_PREFIX)) { - return id.substring(ID_PREFIX.length()); - } - - return id; - } - - /** - * Returns true if the given two id references match. This is similar to - * String equality, but it also considers "{@code @+id/foo == @id/foo}. - * - * @param id1 the first id to compare - * @param id2 the second id to compare - * @return true if the two id references refer to the same id - */ - public static boolean idReferencesMatch(String id1, String id2) { - if (id1.startsWith(NEW_ID_PREFIX)) { - if (id2.startsWith(NEW_ID_PREFIX)) { - return id1.equals(id2); - } else { - assert id2.startsWith(ID_PREFIX); - return ((id1.length() - id2.length()) - == (NEW_ID_PREFIX.length() - ID_PREFIX.length())) - && id1.regionMatches(NEW_ID_PREFIX.length(), id2, - ID_PREFIX.length(), - id2.length() - ID_PREFIX.length()); - } - } else { - assert id1.startsWith(ID_PREFIX); - if (id2.startsWith(ID_PREFIX)) { - return id1.equals(id2); - } else { - assert id2.startsWith(NEW_ID_PREFIX); - return (id2.length() - id1.length() - == (NEW_ID_PREFIX.length() - ID_PREFIX.length())) - && id2.regionMatches(NEW_ID_PREFIX.length(), id1, - ID_PREFIX.length(), - id1.length() - ID_PREFIX.length()); - } - } - } - - /** - * Computes the edit distance (number of insertions, deletions or substitutions - * to edit one string into the other) between two strings. In particular, - * this will compute the Levenshtein distance. - * <p> - * See http://en.wikipedia.org/wiki/Levenshtein_distance for details. - * - * @param s the first string to compare - * @param t the second string to compare - * @return the edit distance between the two strings - */ - public static int editDistance(@NonNull String s, @NonNull String t) { - int m = s.length(); - int n = t.length(); - int[][] d = new int[m + 1][n + 1]; - for (int i = 0; i <= m; i++) { - d[i][0] = i; - } - for (int j = 0; j <= n; j++) { - d[0][j] = j; - } - for (int j = 1; j <= n; j++) { - for (int i = 1; i <= m; i++) { - if (s.charAt(i - 1) == t.charAt(j - 1)) { - d[i][j] = d[i - 1][j - 1]; - } else { - int deletion = d[i - 1][j] + 1; - int insertion = d[i][j - 1] + 1; - int substitution = d[i - 1][j - 1] + 1; - d[i][j] = Math.min(deletion, Math.min(insertion, substitution)); - } - } - } - - return d[m][n]; - } - - /** - * Returns true if assertions are enabled - * - * @return true if assertions are enabled - */ - @SuppressWarnings("all") - public static boolean assertionsEnabled() { - boolean assertionsEnabled = false; - assert assertionsEnabled = true; // Intentional side-effect - return assertionsEnabled; - } - - /** - * Returns the layout resource name for the given layout file - * - * @param layoutFile the file pointing to the layout - * @return the layout resource name, not including the {@code @layout} - * prefix - */ - public static String getLayoutName(File layoutFile) { - String name = layoutFile.getName(); - int dotIndex = name.indexOf('.'); - if (dotIndex != -1) { - name = name.substring(0, dotIndex); - } - return name; - } - - /** - * Splits the given path into its individual parts, attempting to be - * tolerant about path separators (: or ;). It can handle possibly ambiguous - * paths, such as {@code c:\foo\bar:\other}, though of course these are to - * be avoided if possible. - * - * @param path the path variable to split, which can use both : and ; as - * path separators. - * @return the individual path components as an iterable of strings - */ - public static Iterable<String> splitPath(String path) { - if (path.indexOf(';') != -1) { - return Splitter.on(';').omitEmptyStrings().trimResults().split(path); - } - - List<String> combined = new ArrayList<String>(); - Iterables.addAll(combined, Splitter.on(':').omitEmptyStrings().trimResults().split(path)); - for (int i = 0, n = combined.size(); i < n; i++) { - String p = combined.get(i); - if (p.length() == 1 && i < n - 1 && Character.isLetter(p.charAt(0)) - // Technically, Windows paths do not have to have a \ after the :, - // which means it would be using the current directory on that drive, - // but that's unlikely to be the case in a path since it would have - // unpredictable results - && !combined.get(i+1).isEmpty() && combined.get(i+1).charAt(0) == '\\') { - combined.set(i, p + ':' + combined.get(i+1)); - combined.remove(i+1); - n--; - continue; - } - } - - return combined; - } - - /** - * Computes the shared parent among a set of files (which may be null). - * - * @param files the set of files to be checked - * @return the closest common ancestor file, or null if none was found - */ - @Nullable - public static File getCommonParent(@NonNull List<File> files) { - int fileCount = files.size(); - if (fileCount == 0) { - return null; - } else if (fileCount == 1) { - return files.get(0); - } else if (fileCount == 2) { - return getCommonParent(files.get(0), files.get(1)); - } else { - File common = files.get(0); - for (int i = 1; i < fileCount; i++) { - common = getCommonParent(common, files.get(i)); - if (common == null) { - return null; - } - } - - return common; - } - } - - /** - * Computes the closest common parent path between two files. - * - * @param file1 the first file to be compared - * @param file2 the second file to be compared - * @return the closest common ancestor file, or null if the two files have - * no common parent - */ - @Nullable - public static File getCommonParent(@NonNull File file1, @NonNull File file2) { - if (file1.equals(file2)) { - return file1; - } else if (file1.getPath().startsWith(file2.getPath())) { - return file2; - } else if (file2.getPath().startsWith(file1.getPath())) { - return file1; - } else { - // Dumb and simple implementation - File first = file1.getParentFile(); - while (first != null) { - File second = file2.getParentFile(); - while (second != null) { - if (first.equals(second)) { - return first; - } - second = second.getParentFile(); - } - - first = first.getParentFile(); - } - } - return null; - } - - private static final String UTF_8 = "UTF-8"; //$NON-NLS-1$ - private static final String UTF_16 = "UTF_16"; //$NON-NLS-1$ - private static final String UTF_16LE = "UTF_16LE"; //$NON-NLS-1$ - - /** - * Returns the encoded String for the given file. This is usually the - * same as {@code Files.toString(file, Charsets.UTF8}, but if there's a UTF byte order mark - * (for UTF8, UTF_16 or UTF_16LE), use that instead. - * - * @param client the client to use for I/O operations - * @param file the file to read from - * @return the string - * @throws IOException if the file cannot be read properly - */ - @NonNull - public static String getEncodedString( - @NonNull LintClient client, - @NonNull File file) throws IOException { - byte[] bytes = client.readBytes(file); - if (endsWith(file.getName(), DOT_XML)) { - return PositionXmlParser.getXmlString(bytes); - } - - return LintUtils.getEncodedString(bytes); - } - - /** - * Returns the String corresponding to the given data. This is usually the - * same as {@code new String(data)}, but if there's a UTF byte order mark - * (for UTF8, UTF_16 or UTF_16LE), use that instead. - * <p> - * NOTE: For XML files, there is the additional complication that there - * could be a {@code encoding=} attribute in the prologue. For those files, - * use {@link PositionXmlParser#getXmlString(byte[])} instead. - * - * @param data the byte array to construct the string from - * @return the string - */ - @NonNull - public static String getEncodedString(@Nullable byte[] data) { - if (data == null) { - return ""; - } - - int offset = 0; - String defaultCharset = UTF_8; - String charset = null; - // Look for the byte order mark, to see if we need to remove bytes from - // the input stream (and to determine whether files are big endian or little endian) etc - // for files which do not specify the encoding. - // See http://unicode.org/faq/utf_bom.html#BOM for more. - if (data.length > 4) { - if (data[0] == (byte)0xef && data[1] == (byte)0xbb && data[2] == (byte)0xbf) { - // UTF-8 - defaultCharset = charset = UTF_8; - offset += 3; - } else if (data[0] == (byte)0xfe && data[1] == (byte)0xff) { - // UTF-16, big-endian - defaultCharset = charset = UTF_16; - offset += 2; - } else if (data[0] == (byte)0x0 && data[1] == (byte)0x0 - && data[2] == (byte)0xfe && data[3] == (byte)0xff) { - // UTF-32, big-endian - defaultCharset = charset = "UTF_32"; //$NON-NLS-1$ - offset += 4; - } else if (data[0] == (byte)0xff && data[1] == (byte)0xfe - && data[2] == (byte)0x0 && data[3] == (byte)0x0) { - // UTF-32, little-endian. We must check for this *before* looking for - // UTF_16LE since UTF_32LE has the same prefix! - defaultCharset = charset = "UTF_32LE"; //$NON-NLS-1$ - offset += 4; - } else if (data[0] == (byte)0xff && data[1] == (byte)0xfe) { - // UTF-16, little-endian - defaultCharset = charset = UTF_16LE; - offset += 2; - } - } - int length = data.length - offset; - - // Guess encoding by searching for an encoding= entry in the first line. - boolean seenOddZero = false; - boolean seenEvenZero = false; - for (int lineEnd = offset; lineEnd < data.length; lineEnd++) { - if (data[lineEnd] == 0) { - if ((lineEnd - offset) % 1 == 0) { - seenEvenZero = true; - } else { - seenOddZero = true; - } - } else if (data[lineEnd] == '\n' || data[lineEnd] == '\r') { - break; - } - } - - if (charset == null) { - charset = seenOddZero ? UTF_16 : seenEvenZero ? UTF_16LE : UTF_8; - } - - String text = null; - try { - text = new String(data, offset, length, charset); - } catch (UnsupportedEncodingException e) { - try { - if (charset != defaultCharset) { - text = new String(data, offset, length, defaultCharset); - } - } catch (UnsupportedEncodingException u) { - // Just use the default encoding below - } - } - if (text == null) { - text = new String(data, offset, length); - } - return text; - } - - /** - * Returns true if the given class node represents a static inner class. - * - * @param classNode the inner class to be checked - * @return true if the class node represents an inner class that is static - */ - public static boolean isStaticInnerClass(@NonNull ClassNode classNode) { - // Note: We can't just filter out static inner classes like this: - // (classNode.access & Opcodes.ACC_STATIC) != 0 - // because the static flag only appears on methods and fields in the class - // file. Instead, look for the synthetic this pointer. - - @SuppressWarnings("rawtypes") // ASM API - List fieldList = classNode.fields; - for (Object f : fieldList) { - FieldNode field = (FieldNode) f; - if (field.name.startsWith("this$") && (field.access & Opcodes.ACC_SYNTHETIC) != 0) { - return false; - } - } - - return true; - } - - /** - * Returns the previous opcode prior to the given node, ignoring label and - * line number nodes - * - * @param node the node to look up the previous opcode for - * @return the previous opcode, or {@link Opcodes#NOP} if no previous node - * was found - */ - public static int getPrevOpcode(@NonNull AbstractInsnNode node) { - AbstractInsnNode prev = getPrevInstruction(node); - if (prev != null) { - return prev.getOpcode(); - } else { - return Opcodes.NOP; - } - } - - /** - * Returns the previous instruction prior to the given node, ignoring label - * and line number nodes. - * - * @param node the node to look up the previous instruction for - * @return the previous instruction, or null if no previous node was found - */ - @Nullable - public static AbstractInsnNode getPrevInstruction(@NonNull AbstractInsnNode node) { - AbstractInsnNode prev = node; - while (true) { - prev = prev.getPrevious(); - if (prev == null) { - return null; - } else { - int type = prev.getType(); - if (type != AbstractInsnNode.LINE && type != AbstractInsnNode.LABEL - && type != AbstractInsnNode.FRAME) { - return prev; - } - } - } - } - - /** - * Returns the next opcode after to the given node, ignoring label and line - * number nodes - * - * @param node the node to look up the next opcode for - * @return the next opcode, or {@link Opcodes#NOP} if no next node was found - */ - public static int getNextOpcode(@NonNull AbstractInsnNode node) { - AbstractInsnNode next = getNextInstruction(node); - if (next != null) { - return next.getOpcode(); - } else { - return Opcodes.NOP; - } - } - - /** - * Returns the next instruction after to the given node, ignoring label and - * line number nodes. - * - * @param node the node to look up the next node for - * @return the next instruction, or null if no next node was found - */ - @Nullable - public static AbstractInsnNode getNextInstruction(@NonNull AbstractInsnNode node) { - AbstractInsnNode next = node; - while (true) { - next = next.getNext(); - if (next == null) { - return null; - } else { - int type = next.getType(); - if (type != AbstractInsnNode.LINE && type != AbstractInsnNode.LABEL - && type != AbstractInsnNode.FRAME) { - return next; - } - } - } - } - - /** - * Returns true if the given directory represents an Android project - * directory. Note: This doesn't necessarily mean it's an Eclipse directory, - * only that it looks like it contains a logical Android project -- one - * including a manifest file, a resource folder, etc. - * - * @param dir the directory to check - * @return true if the directory looks like an Android project - */ - public static boolean isProjectDir(@NonNull File dir) { - boolean hasManifest = new File(dir, ANDROID_MANIFEST_XML).exists(); - if (hasManifest) { - // Special case: the bin/ folder can also contain a copy of the - // manifest file, but this is *not* a project directory - if (dir.getName().equals(BIN_FOLDER)) { - // ...unless of course it just *happens* to be a project named bin, in - // which case we peek at its parent to see if this is the case - dir = dir.getParentFile(); - if (dir != null && isProjectDir(dir)) { - // Yes, it's a bin/ directory inside a real project: ignore this dir - return false; - } - } - } - - return hasManifest; - } - - /** - * Look up the locale and region from the given parent folder name and - * return it as a combined string, such as "en", "en-rUS", etc, or null if - * no language is specified. - * - * @param folderName the folder name - * @return the locale+region string or null - */ - @Nullable - public static String getLocaleAndRegion(@NonNull String folderName) { - if (folderName.equals("values")) { //$NON-NLS-1$ - return null; - } - - String locale = null; - - for (String qualifier : Splitter.on('-').split(folderName)) { - int qualifierLength = qualifier.length(); - if (qualifierLength == 2) { - char first = qualifier.charAt(0); - char second = qualifier.charAt(1); - if (first >= 'a' && first <= 'z' && second >= 'a' && second <= 'z') { - locale = qualifier; - } - } else if (qualifierLength == 3 && qualifier.charAt(0) == 'r' && locale != null) { - char first = qualifier.charAt(1); - char second = qualifier.charAt(2); - if (first >= 'A' && first <= 'Z' && second >= 'A' && second <= 'Z') { - return locale + '-' + qualifier; - } - break; - } - } - - return locale; - } - - /** - * Returns true if the given class (specified by a fully qualified class - * name) name is imported in the given compilation unit either through a fully qualified - * import or by a wildcard import. - * - * @param compilationUnit the compilation unit - * @param fullyQualifiedName the fully qualified class name - * @return true if the given imported name refers to the given fully - * qualified name - */ - public static boolean isImported( - @NonNull lombok.ast.Node compilationUnit, - @NonNull String fullyQualifiedName) { - int dotIndex = fullyQualifiedName.lastIndexOf('.'); - int dotLength = fullyQualifiedName.length() - dotIndex; - - boolean imported = false; - for (lombok.ast.Node rootNode : compilationUnit.getChildren()) { - if (rootNode instanceof ImportDeclaration) { - ImportDeclaration importDeclaration = (ImportDeclaration) rootNode; - String fqn = importDeclaration.asFullyQualifiedName(); - if (fqn.equals(fullyQualifiedName)) { - return true; - } else if (fullyQualifiedName.regionMatches(dotIndex, fqn, - fqn.length() - dotLength, dotLength)) { - // This import is importing the class name using some other prefix, so there - // fully qualified class name cannot be imported under that name - return false; - } else if (importDeclaration.astStarImport() - && fqn.regionMatches(0, fqn, 0, dotIndex + 1)) { - imported = true; - // but don't break -- keep searching in case there's a non-wildcard - // import of the specific class name, e.g. if we're looking for - // android.content.SharedPreferences.Editor, don't match on the following: - // import android.content.SharedPreferences.*; - // import foo.bar.Editor; - } - } - } - - return imported; - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Location.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Location.java deleted file mode 100644 index f574189..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Location.java +++ /dev/null @@ -1,712 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.google.common.annotations.Beta; - -import java.io.File; - -/** - * Location information for a warning - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public class Location { - private static final String SUPER_KEYWORD = "super"; //$NON-NLS-1$ - - private final File mFile; - private final Position mStart; - private final Position mEnd; - private String mMessage; - private Location mSecondary; - private Object mClientData; - - /** - * (Private constructor, use one of the factory methods - * {@link Location#create(File)}, - * {@link Location#create(File, Position, Position)}, or - * {@link Location#create(File, String, int, int)}. - * <p> - * Constructs a new location range for the given file, from start to end. If - * the length of the range is not known, end may be null. - * - * @param file the associated file (but see the documentation for - * {@link #getFile()} for more information on what the file - * represents) - * @param start the starting position, or null - * @param end the ending position, or null - */ - protected Location(@NonNull File file, @Nullable Position start, @Nullable Position end) { - super(); - mFile = file; - mStart = start; - mEnd = end; - } - - /** - * Returns the file containing the warning. Note that the file *itself* may - * not yet contain the error. When editing a file in the IDE for example, - * the tool could generate warnings in the background even before the - * document is saved. However, the file is used as a identifying token for - * the document being edited, and the IDE integration can map this back to - * error locations in the editor source code. - * - * @return the file handle for the location - */ - @NonNull - public File getFile() { - return mFile; - } - - /** - * The start position of the range - * - * @return the start position of the range, or null - */ - @Nullable - public Position getStart() { - return mStart; - } - - /** - * The end position of the range - * - * @return the start position of the range, may be null for an empty range - */ - @Nullable - public Position getEnd() { - return mEnd; - } - - /** - * Returns a secondary location associated with this location (if - * applicable), or null. - * - * @return a secondary location or null - */ - @Nullable - public Location getSecondary() { - return mSecondary; - } - - /** - * Sets a secondary location for this location. - * - * @param secondary a secondary location associated with this location - */ - public void setSecondary(@Nullable Location secondary) { - mSecondary = secondary; - } - - /** - * Sets a custom message for this location. This is typically used for - * secondary locations, to describe the significance of this alternate - * location. For example, for a duplicate id warning, the primary location - * might say "This is a duplicate id", pointing to the second occurrence of - * id declaration, and then the secondary location could point to the - * original declaration with the custom message "Originally defined here". - * - * @param message the message to apply to this location - */ - public void setMessage(@NonNull String message) { - mMessage = message; - } - - /** - * Returns the custom message for this location, if any. This is typically - * used for secondary locations, to describe the significance of this - * alternate location. For example, for a duplicate id warning, the primary - * location might say "This is a duplicate id", pointing to the second - * occurrence of id declaration, and then the secondary location could point - * to the original declaration with the custom message - * "Originally defined here". - * - * @return the custom message for this location, or null - */ - @Nullable - public String getMessage() { - return mMessage; - } - - /** - * Sets the client data associated with this location. This is an optional - * field which can be used by the creator of the {@link Location} to store - * temporary state associated with the location. - * - * @param clientData the data to store with this location - */ - public void setClientData(@Nullable Object clientData) { - mClientData = clientData; - } - - /** - * Returns the client data associated with this location - an optional field - * which can be used by the creator of the {@link Location} to store - * temporary state associated with the location. - * - * @return the data associated with this location - */ - @Nullable - public Object getClientData() { - return mClientData; - } - - @Override - public String toString() { - return "Location [file=" + mFile + ", start=" + mStart + ", end=" + mEnd + ", message=" - + mMessage + "]"; - } - - /** - * Creates a new location for the given file - * - * @param file the file to create a location for - * @return a new location - */ - @NonNull - public static Location create(@NonNull File file) { - return new Location(file, null /*start*/, null /*end*/); - } - - /** - * Creates a new location for the given file and starting and ending - * positions. - * - * @param file the file containing the positions - * @param start the starting position - * @param end the ending position - * @return a new location - */ - @NonNull - public static Location create( - @NonNull File file, - @NonNull Position start, - @Nullable Position end) { - return new Location(file, start, end); - } - - /** - * Creates a new location for the given file, with the given contents, for - * the given offset range. - * - * @param file the file containing the location - * @param contents the current contents of the file - * @param startOffset the starting offset - * @param endOffset the ending offset - * @return a new location - */ - @NonNull - public static Location create( - @NonNull File file, - @Nullable String contents, - int startOffset, - int endOffset) { - if (startOffset < 0 || endOffset < startOffset) { - throw new IllegalArgumentException("Invalid offsets"); - } - - if (contents == null) { - return new Location(file, - new DefaultPosition(-1, -1, startOffset), - new DefaultPosition(-1, -1, endOffset)); - } - - int size = contents.length(); - endOffset = Math.min(endOffset, size); - startOffset = Math.min(startOffset, endOffset); - Position start = null; - int line = 0; - int lineOffset = 0; - for (int offset = 0; offset <= size; offset++) { - if (offset == startOffset) { - start = new DefaultPosition(line, offset - lineOffset, offset); - } - if (offset == endOffset) { - Position end = new DefaultPosition(line, offset - lineOffset, offset); - return new Location(file, start, end); - } - char c = contents.charAt(offset); - if (c == '\n') { - lineOffset = offset + 1; - line++; - } - } - return Location.create(file); - } - - /** - * Creates a new location for the given file, with the given contents, for - * the given line number. - * - * @param file the file containing the location - * @param contents the current contents of the file - * @param line the line number (0-based) for the position - * @return a new location - */ - @NonNull - public static Location create(@NonNull File file, @NonNull String contents, int line) { - return create(file, contents, line, null, null, null); - } - - /** - * Creates a new location for the given file, with the given contents, for - * the given line number. - * - * @param file the file containing the location - * @param contents the current contents of the file - * @param line the line number (0-based) for the position - * @param patternStart an optional pattern to search for from the line - * match; if found, adjust the column and offsets to begin at the - * pattern start - * @param patternEnd an optional pattern to search for behind the start - * pattern; if found, adjust the end offset to match the end of - * the pattern - * @param hints optional additional information regarding the pattern search - * @return a new location - */ - @NonNull - public static Location create(@NonNull File file, @NonNull String contents, int line, - @Nullable String patternStart, @Nullable String patternEnd, - @Nullable SearchHints hints) { - int currentLine = 0; - int offset = 0; - while (currentLine < line) { - offset = contents.indexOf('\n', offset); - if (offset == -1) { - return Location.create(file); - } - currentLine++; - offset++; - } - - if (line == currentLine) { - if (patternStart != null) { - int index = offset; - - SearchDirection direction = SearchDirection.NEAREST; - if (hints != null) { - direction = hints.mDirection; - } - - if (direction == SearchDirection.BACKWARD) { - index = findPreviousMatch(contents, offset, patternStart, hints); - line = adjustLine(contents, line, offset, index); - } else if (direction == SearchDirection.EOL_BACKWARD) { - int lineEnd = contents.indexOf('\n', offset); - if (lineEnd == -1) { - lineEnd = contents.length(); - } - - index = findPreviousMatch(contents, lineEnd, patternStart, hints); - line = adjustLine(contents, line, offset, index); - } else if (direction == SearchDirection.FORWARD) { - index = findNextMatch(contents, offset, patternStart, hints); - line = adjustLine(contents, line, offset, index); - } else { - assert direction == SearchDirection.NEAREST; - - int before = findPreviousMatch(contents, offset, patternStart, hints); - int after = findNextMatch(contents, offset, patternStart, hints); - - if (before == -1) { - index = after; - line = adjustLine(contents, line, offset, index); - } else if (after == -1) { - index = before; - line = adjustLine(contents, line, offset, index); - } else if (offset - before < after - offset) { - index = before; - line = adjustLine(contents, line, offset, index); - } else { - index = after; - line = adjustLine(contents, line, offset, index); - } - } - - if (index != -1) { - int lineStart = contents.lastIndexOf('\n', index); - if (lineStart == -1) { - lineStart = 0; - } else { - lineStart++; // was pointing to the previous line's CR, not line start - } - int column = index - lineStart; - if (patternEnd != null) { - int end = contents.indexOf(patternEnd, offset + patternStart.length()); - if (end != -1) { - return new Location(file, new DefaultPosition(line, column, index), - new DefaultPosition(line, -1, end + patternEnd.length())); - } - } else if (hints != null && (hints.isJavaSymbol() || hints.isWholeWord())) { - if (hints.isConstructor() && contents.startsWith(SUPER_KEYWORD, index)) { - patternStart = SUPER_KEYWORD; - } - return new Location(file, new DefaultPosition(line, column, index), - new DefaultPosition(line, column + patternStart.length(), - index + patternStart.length())); - } - return new Location(file, new DefaultPosition(line, column, index), - new DefaultPosition(line, column, index + patternStart.length())); - } - } - - Position position = new DefaultPosition(line, -1, offset); - return new Location(file, position, position); - } - - return Location.create(file); - } - - private static int findPreviousMatch(@NonNull String contents, int offset, String pattern, - @Nullable SearchHints hints) { - while (true) { - int index = contents.lastIndexOf(pattern, offset); - if (index == -1) { - return -1; - } else { - if (isMatch(contents, index, pattern, hints)) { - return index; - } else { - offset = index - pattern.length(); - } - } - } - } - - private static int findNextMatch(@NonNull String contents, int offset, String pattern, - @Nullable SearchHints hints) { - int constructorIndex = -1; - if (hints != null && hints.isConstructor()) { - // Special condition: See if the call is referenced as "super" instead. - assert hints.isWholeWord(); - int index = contents.indexOf(SUPER_KEYWORD, offset); - if (index != -1 && isMatch(contents, index, SUPER_KEYWORD, hints)) { - constructorIndex = index; - } - } - - while (true) { - int index = contents.indexOf(pattern, offset); - if (index == -1) { - return constructorIndex; - } else { - if (isMatch(contents, index, pattern, hints)) { - if (constructorIndex != -1) { - return Math.min(constructorIndex, index); - } - return index; - } else { - offset = index + pattern.length(); - } - } - } - } - - private static boolean isMatch(@NonNull String contents, int offset, String pattern, - @Nullable SearchHints hints) { - if (!contents.startsWith(pattern, offset)) { - return false; - } - - if (hints != null) { - char prevChar = offset > 0 ? contents.charAt(offset - 1) : 0; - int lastIndex = offset + pattern.length() - 1; - char nextChar = lastIndex < contents.length() - 1 ? contents.charAt(lastIndex + 1) : 0; - - if (hints.isWholeWord() && (Character.isLetter(prevChar) - || Character.isLetter(nextChar))) { - return false; - - } - - if (hints.isJavaSymbol()) { - if (Character.isJavaIdentifierPart(prevChar) - || Character.isJavaIdentifierPart(nextChar)) { - return false; - } - - if (prevChar == '"') { - return false; - } - - // TODO: Additional validation to see if we're in a comment, string, etc. - // This will require lexing from the beginning of the buffer. - } - - if (hints.isConstructor() && SUPER_KEYWORD.equals(pattern)) { - // Only looking for super(), not super.x, so assert that the next - // non-space character is ( - int index = lastIndex + 1; - while (index < contents.length() - 1) { - char c = contents.charAt(index); - if (c == '(') { - break; - } else if (!Character.isWhitespace(c)) { - return false; - } - index++; - } - } - } - - return true; - } - - private static int adjustLine(String doc, int line, int offset, int newOffset) { - if (newOffset == -1) { - return line; - } - - if (newOffset < offset) { - return line - countLines(doc, newOffset, offset); - } else { - return line + countLines(doc, offset, newOffset); - } - } - - private static int countLines(String doc, int start, int end) { - int lines = 0; - for (int offset = start; offset < end; offset++) { - char c = doc.charAt(offset); - if (c == '\n') { - lines++; - } - } - - return lines; - } - - /** - * Reverses the secondary location list initiated by the given location - * - * @param location the first location in the list - * @return the first location in the reversed list - */ - public static Location reverse(Location location) { - Location next = location.getSecondary(); - location.setSecondary(null); - while (next != null) { - Location nextNext = next.getSecondary(); - next.setSecondary(location); - location = next; - next = nextNext; - } - - return location; - } - - /** - * A {@link Handle} is a reference to a location. The point of a location - * handle is to be able to create them cheaply, and then resolve them into - * actual locations later (if needed). This makes it possible to for example - * delay looking up line numbers, for locations that are offset based. - */ - public static interface Handle { - /** - * Compute a full location for the given handle - * - * @return create a location for this handle - */ - @NonNull - Location resolve(); - - /** - * Sets the client data associated with this location. This is an optional - * field which can be used by the creator of the {@link Location} to store - * temporary state associated with the location. - * - * @param clientData the data to store with this location - */ - public void setClientData(@Nullable Object clientData); - - /** - * Returns the client data associated with this location - an optional field - * which can be used by the creator of the {@link Location} to store - * temporary state associated with the location. - * - * @return the data associated with this location - */ - @Nullable - public Object getClientData(); - } - - /** A default {@link Handle} implementation for simple file offsets */ - public static class DefaultLocationHandle implements Handle { - private File mFile; - private String mContents; - private int mStartOffset; - private int mEndOffset; - private Object mClientData; - - /** - * Constructs a new {@link DefaultLocationHandle} - * - * @param context the context pointing to the file and its contents - * @param startOffset the start offset within the file - * @param endOffset the end offset within the file - */ - public DefaultLocationHandle(@NonNull Context context, int startOffset, int endOffset) { - mFile = context.file; - mContents = context.getContents(); - mStartOffset = startOffset; - mEndOffset = endOffset; - } - - @Override - @NonNull - public Location resolve() { - return Location.create(mFile, mContents, mStartOffset, mEndOffset); - } - - @Override - public void setClientData(@Nullable Object clientData) { - mClientData = clientData; - } - - @Override - @Nullable - public Object getClientData() { - return mClientData; - } - } - - /** - * Whether to look forwards, or backwards, or in both directions, when - * searching for a pattern in the source code to determine the right - * position range for a given symbol. - * <p> - * When dealing with bytecode for example, there are only line number entries - * within method bodies, so when searching for the method declaration, we should only - * search backwards from the first line entry in the method. - */ - public enum SearchDirection { - /** Only search forwards */ - FORWARD, - - /** Only search backwards */ - BACKWARD, - - /** Search backwards from the current end of line (normally it's the beginning of - * the current line) */ - EOL_BACKWARD, - - /** - * Search both forwards and backwards from the given line, and prefer - * the match that is closest - */ - NEAREST, - } - - /** - * Extra information pertaining to finding a symbol in a source buffer, - * used by {@link Location#create(File, String, int, String, String, SearchHints)} - */ - public static class SearchHints { - /** - * the direction to search for the nearest match in (provided - * {@code patternStart} is non null) - */ - @NonNull - private SearchDirection mDirection; - - /** Whether the matched pattern should be a whole word */ - private boolean mWholeWord; - - /** - * Whether the matched pattern should be a Java symbol (so for example, - * a match inside a comment or string literal should not be used) - */ - private boolean mJavaSymbol; - - /** - * Whether the matched pattern corresponds to a constructor; if so, look for - * some other possible source aliases too, such as "super". - */ - private boolean mConstructor; - - private SearchHints(SearchDirection direction) { - super(); - mDirection = direction; - } - - /** - * Constructs a new {@link SearchHints} object - * - * @param direction the direction to search in for the pattern - * @return a new @link SearchHints} object - */ - public static SearchHints create(SearchDirection direction) { - return new SearchHints(direction); - } - - /** - * Indicates that pattern matches should apply to whole words only - - * @return this, for constructor chaining - */ - public SearchHints matchWholeWord() { - mWholeWord = true; - - return this; - } - - /** @return true if the pattern match should be for whole words only */ - public boolean isWholeWord() { - return mWholeWord; - } - - /** - * Indicates that pattern matches should apply to Java symbols only - * - * @return this, for constructor chaining - */ - public SearchHints matchJavaSymbol() { - mJavaSymbol = true; - mWholeWord = true; - - return this; - } - - /** @return true if the pattern match should be for Java symbols only */ - public boolean isJavaSymbol() { - return mJavaSymbol; - } - - /** - * Indicates that pattern matches should apply to constructors. If so, look for - * some other possible source aliases too, such as "super". - * - * @return this, for constructor chaining - */ - public SearchHints matchConstructor() { - mConstructor = true; - mWholeWord = true; - mJavaSymbol = true; - - return this; - } - - /** @return true if the pattern match should be for a constructor */ - public boolean isConstructor() { - return mConstructor; - } - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Position.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Position.java deleted file mode 100644 index 6407cc9..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Position.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import com.google.common.annotations.Beta; - -/** - * Information about a position in a file/document. - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public abstract class Position { - /** - * Returns the line number (0-based where the first line is line 0) - * - * @return the 0-based line number - */ - public abstract int getLine(); - - /** - * The character offset - * - * @return the 0-based character offset - */ - public abstract int getOffset(); - - /** - * Returns the column number (where the first character on the line is 0), - * or -1 if unknown - * - * @return the 0-based column number - */ - public abstract int getColumn(); -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Project.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Project.java deleted file mode 100644 index b584020..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Project.java +++ /dev/null @@ -1,815 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import static com.android.SdkConstants.ANDROID_LIBRARY; -import static com.android.SdkConstants.ANDROID_LIBRARY_REFERENCE_FORMAT; -import static com.android.SdkConstants.ANDROID_MANIFEST_XML; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_MIN_SDK_VERSION; -import static com.android.SdkConstants.ATTR_PACKAGE; -import static com.android.SdkConstants.ATTR_TARGET_SDK_VERSION; -import static com.android.SdkConstants.PROGUARD_CONFIG; -import static com.android.SdkConstants.PROJECT_PROPERTIES; -import static com.android.SdkConstants.TAG_USES_SDK; -import static com.android.SdkConstants.VALUE_TRUE; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.client.api.Configuration; -import com.android.tools.lint.client.api.LintClient; -import com.android.tools.lint.client.api.SdkInfo; -import com.google.common.annotations.Beta; -import com.google.common.base.Charsets; -import com.google.common.io.Closeables; -import com.google.common.io.Files; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Properties; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A project contains information about an Android project being scanned for - * Lint errors. - * <p> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public class Project { - private final LintClient mClient; - private final File mDir; - private final File mReferenceDir; - private Configuration mConfiguration; - private String mPackage; - private int mMinSdk = 1; - private int mTargetSdk = -1; - private boolean mLibrary; - private String mName; - private String mProguardPath; - private boolean mMergeManifests; - - /** The SDK info, if any */ - private SdkInfo mSdkInfo; - - /** - * If non null, specifies a non-empty list of specific files under this - * project which should be checked. - */ - private List<File> mFiles; - private List<File> mJavaSourceFolders; - private List<File> mJavaClassFolders; - private List<File> mJavaLibraries; - private List<Project> mDirectLibraries; - private List<Project> mAllLibraries; - private boolean mReportIssues = true; - - /** - * Creates a new {@link Project} for the given directory. - * - * @param client the tool running the lint check - * @param dir the root directory of the project - * @param referenceDir See {@link #getReferenceDir()}. - * @return a new {@link Project} - */ - @NonNull - public static Project create( - @NonNull LintClient client, - @NonNull File dir, - @NonNull File referenceDir) { - return new Project(client, dir, referenceDir); - } - - /** Creates a new Project. Use one of the factory methods to create. */ - private Project( - @NonNull LintClient client, - @NonNull File dir, - @NonNull File referenceDir) { - mClient = client; - mDir = dir; - mReferenceDir = referenceDir; - - try { - // Read properties file and initialize library state - Properties properties = new Properties(); - File propFile = new File(dir, PROJECT_PROPERTIES); - if (propFile.exists()) { - @SuppressWarnings("resource") // Eclipse doesn't know about Closeables.closeQuietly - BufferedInputStream is = new BufferedInputStream(new FileInputStream(propFile)); - try { - properties.load(is); - String value = properties.getProperty(ANDROID_LIBRARY); - mLibrary = VALUE_TRUE.equals(value); - mProguardPath = properties.getProperty(PROGUARD_CONFIG); - mMergeManifests = VALUE_TRUE.equals(properties.getProperty( - "manifestmerger.enabled")); //$NON-NLS-1$ - - for (int i = 1; i < 1000; i++) { - String key = String.format(ANDROID_LIBRARY_REFERENCE_FORMAT, i); - String library = properties.getProperty(key); - if (library == null || library.length() == 0) { - // No holes in the numbering sequence is allowed - break; - } - - File libraryDir = new File(dir, library).getCanonicalFile(); - - if (mDirectLibraries == null) { - mDirectLibraries = new ArrayList<Project>(); - } - - // Adjust the reference dir to be a proper prefix path of the - // library dir - File libraryReferenceDir = referenceDir; - if (!libraryDir.getPath().startsWith(referenceDir.getPath())) { - // Symlinks etc might have been resolved, so do those to - // the reference dir as well - libraryReferenceDir = libraryReferenceDir.getCanonicalFile(); - if (!libraryDir.getPath().startsWith(referenceDir.getPath())) { - File f = libraryReferenceDir; - while (f != null && f.getPath().length() > 0) { - if (libraryDir.getPath().startsWith(f.getPath())) { - libraryReferenceDir = f; - break; - } - f = f.getParentFile(); - } - } - } - - Project libraryPrj = client.getProject(libraryDir, libraryReferenceDir); - mDirectLibraries.add(libraryPrj); - // By default, we don't report issues in inferred library projects. - // The driver will set report = true for those library explicitly - // requested. - libraryPrj.setReportIssues(false); - } - } finally { - Closeables.closeQuietly(is); - } - } - } catch (IOException ioe) { - client.log(ioe, "Initializing project state"); - } - - if (mDirectLibraries != null) { - mDirectLibraries = Collections.unmodifiableList(mDirectLibraries); - } else { - mDirectLibraries = Collections.emptyList(); - } - } - - @Override - public String toString() { - return "Project [dir=" + mDir + "]"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((mDir == null) ? 0 : mDir.hashCode()); - return result; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Project other = (Project) obj; - if (mDir == null) { - if (other.mDir != null) - return false; - } else if (!mDir.equals(other.mDir)) - return false; - return true; - } - - /** - * Adds the given file to the list of files which should be checked in this - * project. If no files are added, the whole project will be checked. - * - * @param file the file to be checked - */ - public void addFile(@NonNull File file) { - if (mFiles == null) { - mFiles = new ArrayList<File>(); - } - mFiles.add(file); - } - - /** - * The list of files to be checked in this project. If null, the whole - * project should be checked. - * - * @return the subset of files to be checked, or null for the whole project - */ - @Nullable - public List<File> getSubset() { - return mFiles; - } - - /** - * Returns the list of source folders for Java source files - * - * @return a list of source folders to search for .java files - */ - @NonNull - public List<File> getJavaSourceFolders() { - if (mJavaSourceFolders == null) { - if (isAospBuildEnvironment()) { - String top = getAospTop(); - if (mDir.getAbsolutePath().startsWith(top)) { - mJavaSourceFolders = getAospJavaSourcePath(); - return mJavaSourceFolders; - } - } - - mJavaSourceFolders = mClient.getJavaSourceFolders(this); - } - - return mJavaSourceFolders; - } - - /** - * Returns the list of output folders for class files - * @return a list of output folders to search for .class files - */ - @NonNull - public List<File> getJavaClassFolders() { - if (mJavaClassFolders == null) { - if (isAospBuildEnvironment()) { - String top = getAospTop(); - if (mDir.getAbsolutePath().startsWith(top)) { - mJavaClassFolders = getAospJavaClassPath(); - return mJavaClassFolders; - } - } - - mJavaClassFolders = mClient.getJavaClassFolders(this); - } - return mJavaClassFolders; - } - - /** - * Returns the list of Java libraries (typically .jar files) that this - * project depends on. Note that this refers to jar libraries, not Android - * library projects which are processed in a separate pass with their own - * source and class folders. - * - * @return a list of .jar files (or class folders) that this project depends - * on. - */ - @NonNull - public List<File> getJavaLibraries() { - if (mJavaLibraries == null) { - // AOSP builds already merge libraries and class folders into - // the single classes.jar file, so these have already been processed - // in getJavaClassFolders. - - mJavaLibraries = mClient.getJavaLibraries(this); - } - - return mJavaLibraries; - } - - /** - * Returns the relative path of a given file relative to the user specified - * directory (which is often the project directory but sometimes a higher up - * directory when a directory tree is being scanned - * - * @param file the file under this project to check - * @return the path relative to the reference directory (often the project directory) - */ - @NonNull - public String getDisplayPath(@NonNull File file) { - String path = file.getPath(); - String referencePath = mReferenceDir.getPath(); - if (path.startsWith(referencePath)) { - int length = referencePath.length(); - if (path.length() > length && path.charAt(length) == File.separatorChar) { - length++; - } - - return path.substring(length); - } - - return path; - } - - /** - * Returns the relative path of a given file within the current project. - * - * @param file the file under this project to check - * @return the path relative to the project - */ - @NonNull - public String getRelativePath(@NonNull File file) { - String path = file.getPath(); - String referencePath = mDir.getPath(); - if (path.startsWith(referencePath)) { - int length = referencePath.length(); - if (path.length() > length && path.charAt(length) == File.separatorChar) { - length++; - } - - return path.substring(length); - } - - return path; - } - - /** - * Returns the project root directory - * - * @return the dir - */ - @NonNull - public File getDir() { - return mDir; - } - - /** - * Returns the original user supplied directory where the lint search - * started. For example, if you run lint against {@code /tmp/foo}, and it - * finds a project to lint in {@code /tmp/foo/dev/src/project1}, then the - * {@code dir} is {@code /tmp/foo/dev/src/project1} and the - * {@code referenceDir} is {@code /tmp/foo/}. - * - * @return the reference directory, never null - */ - @NonNull - public File getReferenceDir() { - return mReferenceDir; - } - - /** - * Gets the configuration associated with this project - * - * @return the configuration associated with this project - */ - @NonNull - public Configuration getConfiguration() { - if (mConfiguration == null) { - mConfiguration = mClient.getConfiguration(this); - } - return mConfiguration; - } - - /** - * Returns the application package specified by the manifest - * - * @return the application package, or null if unknown - */ - @Nullable - public String getPackage() { - //assert !mLibrary; // Should call getPackage on the master project, not the library - // Assertion disabled because you might be running lint on a standalone library project. - - return mPackage; - } - - /** - * Returns the minimum API level requested by the manifest, or -1 if not - * specified - * - * @return the minimum API level or -1 if unknown - */ - public int getMinSdk() { - //assert !mLibrary; // Should call getMinSdk on the master project, not the library - // Assertion disabled because you might be running lint on a standalone library project. - - return mMinSdk; - } - - /** - * Returns the target API level specified by the manifest, or -1 if not - * specified - * - * @return the target API level or -1 if unknown - */ - public int getTargetSdk() { - //assert !mLibrary; // Should call getTargetSdk on the master project, not the library - // Assertion disabled because you might be running lint on a standalone library project. - - return mTargetSdk; - } - - /** - * Initialized the manifest state from the given manifest model - * - * @param document the DOM document for the manifest XML document - */ - public void readManifest(@NonNull Document document) { - Element root = document.getDocumentElement(); - if (root == null) { - return; - } - - mPackage = root.getAttribute(ATTR_PACKAGE); - - // Initialize minSdk and targetSdk - NodeList usesSdks = root.getElementsByTagName(TAG_USES_SDK); - if (usesSdks.getLength() > 0) { - Element element = (Element) usesSdks.item(0); - - String minSdk = null; - if (element.hasAttributeNS(ANDROID_URI, ATTR_MIN_SDK_VERSION)) { - minSdk = element.getAttributeNS(ANDROID_URI, ATTR_MIN_SDK_VERSION); - } - if (minSdk != null) { - try { - mMinSdk = Integer.valueOf(minSdk); - } catch (NumberFormatException e) { - mMinSdk = 1; - } - } - - String targetSdk = null; - if (element.hasAttributeNS(ANDROID_URI, ATTR_TARGET_SDK_VERSION)) { - targetSdk = element.getAttributeNS(ANDROID_URI, ATTR_TARGET_SDK_VERSION); - } else if (minSdk != null) { - targetSdk = minSdk; - } - if (targetSdk != null) { - try { - mTargetSdk = Integer.valueOf(targetSdk); - } catch (NumberFormatException e) { - // TODO: Handle codenames? - mTargetSdk = -1; - } - } - } else if (isAospBuildEnvironment()) { - extractAospMinSdkVersion(); - } - } - - /** - * Returns true if this project is an Android library project - * - * @return true if this project is an Android library project - */ - public boolean isLibrary() { - return mLibrary; - } - - /** - * Returns the list of library projects referenced by this project - * - * @return the list of library projects referenced by this project, never - * null - */ - @NonNull - public List<Project> getDirectLibraries() { - return mDirectLibraries; - } - - /** - * Returns the transitive closure of the library projects for this project - * - * @return the transitive closure of the library projects for this project - */ - @NonNull - public List<Project> getAllLibraries() { - if (mAllLibraries == null) { - if (mDirectLibraries.size() == 0) { - return mDirectLibraries; - } - - List<Project> all = new ArrayList<Project>(); - addLibraryProjects(all); - mAllLibraries = all; - } - - return mAllLibraries; - } - - /** - * Adds this project's library project and their library projects - * recursively into the given collection of projects - * - * @param collection the collection to add the projects into - */ - private void addLibraryProjects(@NonNull Collection<Project> collection) { - for (Project library : mDirectLibraries) { - collection.add(library); - // Recurse - library.addLibraryProjects(collection); - } - } - - /** - * Gets the SDK info for the current project. - * - * @return the SDK info for the current project, never null - */ - @NonNull - public SdkInfo getSdkInfo() { - if (mSdkInfo == null) { - mSdkInfo = mClient.getSdkInfo(this); - } - - return mSdkInfo; - } - - /** - * Gets the path to the manifest file in this project, if it exists - * - * @return the path to the manifest file, or null if it does not exist - */ - public File getManifestFile() { - File manifestFile = new File(mDir, ANDROID_MANIFEST_XML); - if (manifestFile.exists()) { - return manifestFile; - } - - return null; - } - - /** - * Returns the proguard path configured for this project, or null if ProGuard is - * not configured. - * - * @return the proguard path, or null - */ - @Nullable - public String getProguardPath() { - return mProguardPath; - } - - /** - * Returns the name of the project - * - * @return the name of the project, never null - */ - @NonNull - public String getName() { - if (mName == null) { - // TODO: Consider reading the name from .project (if it's an Eclipse project) - mName = mDir.getName(); - } - - return mName; - } - - /** - * Sets whether lint should report issues in this project. See - * {@link #getReportIssues()} for a full description of what that means. - * - * @param reportIssues whether lint should report issues in this project - */ - public void setReportIssues(boolean reportIssues) { - mReportIssues = reportIssues; - } - - /** - * Returns whether lint should report issues in this project. - * <p> - * If a user specifies a project and its library projects for analysis, then - * those library projects are all "included", and all errors found in all - * the projects are reported. But if the user is only running lint on the - * main project, we shouldn't report errors in any of the library projects. - * We still need to <b>consider</b> them for certain types of checks, such - * as determining whether resources found in the main project are unused, so - * the detectors must still get a chance to look at these projects. The - * {@code #getReportIssues()} attribute is used for this purpose. - * - * @return whether lint should report issues in this project - */ - public boolean getReportIssues() { - return mReportIssues; - } - - /** - * Sets whether manifest merging is in effect. - * - * @param merging whether manifest merging is in effect - */ - public void setMergingManifests(boolean merging) { - mMergeManifests = merging; - } - - /** - * Returns whether manifest merging is in effect - * - * @return true if manifests in library projects should be merged into main projects - */ - public boolean isMergingManifests() { - return mMergeManifests; - } - - - // --------------------------------------------------------------------------- - // Support for running lint on the AOSP source tree itself - - private static Boolean sAospBuild; - - /** Is lint running in an AOSP build environment */ - private static boolean isAospBuildEnvironment() { - if (sAospBuild == null) { - sAospBuild = getAospTop() != null; - } - - return sAospBuild.booleanValue(); - } - - /** Get the root AOSP dir, if any */ - private static String getAospTop() { - return System.getenv("ANDROID_BUILD_TOP"); //$NON-NLS-1$ - } - - /** Get the host out directory in AOSP, if any */ - private static String getAospHostOut() { - return System.getenv("ANDROID_HOST_OUT"); //$NON-NLS-1$ - } - - /** Get the product out directory in AOSP, if any */ - private static String getAospProductOut() { - return System.getenv("ANDROID_PRODUCT_OUT"); //$NON-NLS-1$ - } - - private List<File> getAospJavaSourcePath() { - List<File> sources = new ArrayList<File>(2); - // Normal sources - File src = new File(mDir, "src"); //$NON-NLS-1$ - if (src.exists()) { - sources.add(src); - } - - // Generates sources - for (File dir : getIntermediateDirs()) { - File classes = new File(dir, "src"); //$NON-NLS-1$ - if (classes.exists()) { - sources.add(classes); - } - } - - if (sources.size() == 0) { - mClient.log(null, - "Warning: Could not find sources or generated sources for project %1$s", - getName()); - } - - return sources; - } - - private List<File> getAospJavaClassPath() { - List<File> classDirs = new ArrayList<File>(1); - - for (File dir : getIntermediateDirs()) { - File classes = new File(dir, "classes"); //$NON-NLS-1$ - if (classes.exists()) { - classDirs.add(classes); - } else { - classes = new File(dir, "classes.jar"); //$NON-NLS-1$ - if (classes.exists()) { - classDirs.add(classes); - } - } - } - - if (classDirs.size() == 0) { - mClient.log(null, - "No bytecode found: Has the project been built? (%1$s)", getName()); - } - - return classDirs; - } - - /** Find the _intermediates directories for a given module name */ - private List<File> getIntermediateDirs() { - // See build/core/definitions.mk and in particular the "intermediates-dir-for" definition - List<File> intermediates = new ArrayList<File>(); - - // TODO: Look up the module name, e.g. LOCAL_MODULE. However, - // some Android.mk files do some complicated things with it - and most - // projects use the same module name as the directory name. - String moduleName = mDir.getName(); - - String top = getAospTop(); - final String[] outFolders = new String[] { - top + "/out/host/common/obj", //$NON-NLS-1$ - top + "/out/target/common/obj", //$NON-NLS-1$ - getAospHostOut() + "/obj", //$NON-NLS-1$ - getAospProductOut() + "/obj" //$NON-NLS-1$ - }; - final String[] moduleClasses = new String[] { - "APPS", //$NON-NLS-1$ - "JAVA_LIBRARIES", //$NON-NLS-1$ - }; - - for (String out : outFolders) { - assert new File(out.replace('/', File.separatorChar)).exists() : out; - for (String moduleClass : moduleClasses) { - String path = out + '/' + moduleClass + '/' + moduleName - + "_intermediates"; //$NON-NLS-1$ - File file = new File(path.replace('/', File.separatorChar)); - if (file.exists()) { - intermediates.add(file); - } - } - } - - return intermediates; - } - - private void extractAospMinSdkVersion() { - // Is the SDK level specified by a Makefile? - boolean found = false; - File makefile = new File(mDir, "Android.mk"); //$NON-NLS-1$ - if (makefile.exists()) { - try { - List<String> lines = Files.readLines(makefile, Charsets.UTF_8); - Pattern p = Pattern.compile("LOCAL_SDK_VERSION\\s*:=\\s*(.*)"); //$NON-NLS-1$ - for (String line : lines) { - line = line.trim(); - Matcher matcher = p.matcher(line); - if (matcher.matches()) { - found = true; - String version = matcher.group(1); - if (version.equals("current")) { //$NON-NLS-1$ - mMinSdk = findCurrentAospVersion(); - } else { - try { - mMinSdk = Integer.valueOf(version); - } catch (NumberFormatException e) { - // Codename - just use current - mMinSdk = findCurrentAospVersion(); - } - } - break; - } - } - } catch (IOException ioe) { - mClient.log(ioe, null); - } - } - - if (!found) { - mMinSdk = findCurrentAospVersion(); - } - } - - /** Cache for {@link #findCurrentAospVersion()} */ - private static int sCurrentVersion; - - /** In an AOSP build environment, identify the currently built image version, if available */ - private int findCurrentAospVersion() { - if (sCurrentVersion < 1) { - File apiDir = new File(getAospTop(), "frameworks/base/api" //$NON-NLS-1$ - .replace('/', File.separatorChar)); - File[] apiFiles = apiDir.listFiles(); - int max = 1; - for (File apiFile : apiFiles) { - String name = apiFile.getName(); - int index = name.indexOf('.'); - if (index > 0) { - String base = name.substring(0, index); - if (Character.isDigit(base.charAt(0))) { - try { - int version = Integer.parseInt(base); - if (version > max) { - max = version; - } - } catch (NumberFormatException nufe) { - // pass - } - } - } - } - sCurrentVersion = max; - } - - return sCurrentVersion; - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ResourceXmlDetector.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ResourceXmlDetector.java deleted file mode 100644 index 68685c6..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ResourceXmlDetector.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -import com.google.common.annotations.Beta; - -import java.io.File; - -/** - * Specialized detector intended for XML resources. Detectors that apply to XML - * resources should extend this detector instead since it provides special - * iteration hooks that are more efficient. - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public abstract class ResourceXmlDetector extends Detector implements Detector.XmlScanner { - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return LintUtils.isXmlFile(file); - } - - /** - * Returns whether this detector applies to the given folder type. This - * allows the detectors to be pruned from iteration, so for example when we - * are analyzing a string value file we don't need to look up detectors - * related to layout. - * - * @param folderType the folder type to be visited - * @return true if this detector can apply to resources in folders of the - * given type - */ - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return true; - } - - @Override - public void run(@NonNull Context context) { - // The infrastructure should never call this method on an xml detector since - // it will run the various visitors instead - assert false; - } -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Scope.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Scope.java deleted file mode 100644 index a917c11..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Scope.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import com.android.annotations.NonNull; -import com.google.common.annotations.Beta; - -import java.util.EnumSet; - -/** - * The scope of a detector is the set of files a detector must consider when - * performing its analysis. This can be used to determine when issues are - * potentially obsolete, whether a detector should re-run on a file save, etc. - * <p> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public enum Scope { - /** - * The analysis only considers a single XML resource file at a time. - * <p> - * Issues which are only affected by a single resource file can be checked - * for incrementally when a file is edited. - */ - RESOURCE_FILE, - - /** - * The analysis considers <b>all</b> the resource file. This scope must not - * be used in conjunction with {@link #RESOURCE_FILE}; an issue scope is - * either considering just a single resource file or all the resources, not - * both. - */ - ALL_RESOURCE_FILES, - - /** - * The analysis only considers a single Java source file at a time. - * <p> - * Issues which are only affected by a single Java source file can be - * checked for incrementally when a Java source file is edited. - */ - JAVA_FILE, - - /** - * The analysis considers <b>all</b> the Java source files together. - * <p> - * This flag is mutually exclusive with {@link #JAVA_FILE}. - */ - ALL_JAVA_FILES, - - /** - * The analysis only considers a single Java class file at a time. - * <p> - * Issues which are only affected by a single Java class file can be checked - * for incrementally when a Java source file is edited and then recompiled. - */ - CLASS_FILE, - - /** The analysis considers the manifest file */ - MANIFEST, - - /** The analysis considers the Proguard configuration file */ - PROGUARD_FILE, - - /** - * The analysis considers classes in the libraries for this project. These - * will be analyzed before the classes themselves. - */ - JAVA_LIBRARIES; - - /** - * Returns true if the given scope set corresponds to scanning a single file - * rather than a whole project - * - * @param scopes the scope set to check - * @return true if the scope set references a single file - */ - public static boolean checkSingleFile(@NonNull EnumSet<Scope> scopes) { - int size = scopes.size(); - if (size == 2) { - // When single checking a Java source file, we check both its Java source - // and the associated class files - return scopes.contains(JAVA_FILE) && scopes.contains(CLASS_FILE); - } else { - return size == 1 && - (scopes.contains(JAVA_FILE) - || scopes.contains(CLASS_FILE) - || scopes.contains(RESOURCE_FILE) - || scopes.contains(PROGUARD_FILE) - || scopes.contains(MANIFEST)); - } - } - - /** - * Returns the intersection of two scope sets - * - * @param scope1 the first set to intersect - * @param scope2 the second set to intersect - * @return the intersection of the two sets - */ - @NonNull - public static EnumSet<Scope> intersect( - @NonNull EnumSet<Scope> scope1, - @NonNull EnumSet<Scope> scope2) { - EnumSet<Scope> scope = EnumSet.copyOf(scope1); - scope.retainAll(scope2); - - return scope; - } - - /** All scopes: running lint on a project will check these scopes */ - public static final EnumSet<Scope> ALL = EnumSet.allOf(Scope.class); - /** Scope-set used for detectors which are affected by a single resource file */ - public static final EnumSet<Scope> RESOURCE_FILE_SCOPE = EnumSet.of(RESOURCE_FILE); - /** Scope-set used for detectors which scan all resources */ - public static final EnumSet<Scope> ALL_RESOURCES_SCOPE = EnumSet.of(ALL_RESOURCE_FILES); - /** Scope-set used for detectors which are affected by a single Java source file */ - public static final EnumSet<Scope> JAVA_FILE_SCOPE = EnumSet.of(JAVA_FILE); - /** Scope-set used for detectors which are affected by a single Java class file */ - public static final EnumSet<Scope> CLASS_FILE_SCOPE = EnumSet.of(CLASS_FILE); - /** Scope-set used for detectors which are affected by the manifest only */ - public static final EnumSet<Scope> MANIFEST_SCOPE = EnumSet.of(MANIFEST); -} diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Severity.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Severity.java deleted file mode 100644 index cde61bd..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Severity.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import com.android.annotations.NonNull; -import com.google.common.annotations.Beta; - -/** - * Severity of an issue found by lint - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public enum Severity { - /** - * Fatal: Use sparingly because a warning marked as fatal will be - * considered critical and will abort Export APK etc in ADT - */ - @NonNull - FATAL("Fatal"), - - /** - * Errors: The issue is known to be a real error that must be addressed. - */ - @NonNull - ERROR("Error"), - - /** - * Warning: Probably a problem. - */ - @NonNull - WARNING("Warning"), - - /** - * Information only: Might not be a problem, but the check has found - * something interesting to say about the code. - */ - @NonNull - INFORMATIONAL("Information"), - - /** - * Ignore: The user doesn't want to see this issue - */ - @NonNull - IGNORE("Ignore"); - - @NonNull - private final String mDisplay; - - private Severity(@NonNull String display) { - mDisplay = display; - } - - /** - * Returns a description of this severity suitable for display to the user - * - * @return a description of the severity - */ - public @NonNull String getDescription() { - return mDisplay; - } -}
\ No newline at end of file diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Speed.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Speed.java deleted file mode 100644 index 8c20a19..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Speed.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import com.android.annotations.NonNull; -import com.google.common.annotations.Beta; - -/** - * Enum which describes the different computation speeds of various detectors - * <p> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public enum Speed { - /** The detector can run very quickly */ - FAST("Fast"), - - /** The detector runs reasonably fast */ - NORMAL("Normal"), - - /** The detector might take a long time to run */ - SLOW("Slow"); - - private String mDisplayName; - - Speed(@NonNull String displayName) { - mDisplayName = displayName; - } - - /** - * Returns the user-visible description of the speed of the given - * detector - * - * @return the description of the speed to display to the user - */ - @NonNull - public String getDisplayName() { - return mDisplayName; - } -}
\ No newline at end of file diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/XmlContext.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/XmlContext.java deleted file mode 100644 index 14e0a91..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/XmlContext.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.tools.lint.detector.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.client.api.IDomParser; -import com.android.tools.lint.client.api.LintDriver; -import com.google.common.annotations.Beta; - -import org.w3c.dom.Document; -import org.w3c.dom.Node; - -import java.io.File; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A {@link Context} used when checking XML files. - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public class XmlContext extends Context { - /** The XML parser */ - public IDomParser parser; - /** The XML document */ - public Document document; - private final ResourceFolderType mFolderType; - - /** - * Construct a new {@link XmlContext} - * - * @param driver the driver running through the checks - * @param project the project containing the file being checked - * @param main the main project if this project is a library project, or - * null if this is not a library project. The main project is - * the root project of all library projects, not necessarily the - * directly including project. - * @param file the file being checked - * @param folderType the {@link ResourceFolderType} of this file, if any - */ - public XmlContext( - @NonNull LintDriver driver, - @NonNull Project project, - @Nullable Project main, - @NonNull File file, - @Nullable ResourceFolderType folderType) { - super(driver, project, main, file); - mFolderType = folderType; - } - - /** - * Returns the location for the given node, which may be an element or an attribute. - * - * @param node the node to look up the location for - * @return the location for the node - */ - @NonNull - public Location getLocation(@NonNull Node node) { - if (parser != null) { - return parser.getLocation(this, node); - } - - return Location.create(file); - } - - /** - * Creates a new location within an XML text node - * - * @param textNode the text node - * @param begin the start offset within the text node (inclusive) - * @param end the end offset within the text node (exclusive) - * @return a new location - */ - @NonNull - public Location getLocation(@NonNull Node textNode, int begin, int end) { - assert textNode.getNodeType() == Node.TEXT_NODE; - if (parser != null) { - return parser.getLocation(this, textNode, begin, end); - } - - return Location.create(file); - } - - - /** - * Reports an issue applicable to a given DOM node. The DOM node is used as the - * scope to check for suppress lint annotations. - * - * @param issue the issue to report - * @param scope the DOM node scope the error applies to. The lint infrastructure - * will check whether there are suppress directives on this node (or its enclosing - * nodes) and if so suppress the warning without involving the client. - * @param location the location of the issue, or null if not known - * @param message the message for this warning - * @param data any associated data, or null - */ - public void report( - @NonNull Issue issue, - @Nullable Node scope, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - if (scope != null && mDriver.isSuppressed(issue, scope)) { - return; - } - super.report(issue, location, message, data); - } - - @Override - public void report( - @NonNull Issue issue, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - // Warn if clients use the non-scoped form? No, there are cases where an - // XML detector's error isn't applicable to one particular location (or it's - // not feasible to compute it cheaply) - //mDriver.getClient().log(null, "Warning: Issue " + issue - // + " was reported without a scope node: Can't be suppressed."); - - // For now just check the document root itself - if (document != null && mDriver.isSuppressed(issue, document)) { - return; - } - - super.report(issue, location, message, data); - } - - /** - * Returns the resource folder type of this XML file, if any. - * - * @return the resource folder type or null - */ - @Nullable - public ResourceFolderType getResourceFolderType() { - return mFolderType; - } - - - private final static Pattern sVersionPattern = Pattern.compile("^v(\\d+)$");//$NON-NLS-1$ - - private static File sCachedFolder = null; - private static int sCachedFolderVersion = -1; - - /** - * Returns the folder version. For example, for the file values-v14/foo.xml, - * it returns 14. - * - * @return the folder version, or -1 if no specific version was specified - */ - public int getFolderVersion() { - File parent = file.getParentFile(); - if (parent.equals(sCachedFolder)) { - return sCachedFolderVersion; - } - - sCachedFolder = parent; - sCachedFolderVersion = -1; - - String[] qualifiers = parent.getName().split("-"); //$NON-NLS-1$ - for (String qualifier : qualifiers) { - Matcher matcher = sVersionPattern.matcher(qualifier); - if (matcher.matches()) { - sCachedFolderVersion = Integer.parseInt(matcher.group(1)); - break; - } - } - - return sCachedFolderVersion; - } -} |