* 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. */ diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultConfiguration.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultConfiguration.java index 0a2278d0937..eb2d9f09ae6 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultConfiguration.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultConfiguration.java @@ -16,6 +16,9 @@ package com.android.tools.klint.client.api; +import static com.android.SdkConstants.CURRENT_PLATFORM; +import static com.android.SdkConstants.PLATFORM_WINDOWS; + import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.tools.klint.detector.api.Context; @@ -28,22 +31,35 @@ import com.android.utils.XmlUtils; import com.google.common.annotations.Beta; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Splitter; -import org.w3c.dom.*; + +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.SAXParseException; -import java.io.*; -import java.util.*; +import java.io.BufferedWriter; +import java.io.File; +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 java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; -import static com.android.SdkConstants.CURRENT_PLATFORM; -import static com.android.SdkConstants.PLATFORM_WINDOWS; - /** * Default implementation of a {@link Configuration} which reads and writes * configuration data into {@code lint.xml} in the project directory. - *
+ ** 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. */ @@ -251,7 +267,7 @@ public class DefaultConfiguration extends Configuration { }, mClient); mClient.report(new Context(driver, mProject, mProject, mConfigFile), IssueRegistry.LINT_ERROR, - mProject.getConfiguration().getSeverity(IssueRegistry.LINT_ERROR), + mProject.getConfiguration(driver).getSeverity(IssueRegistry.LINT_ERROR), Location.create(mConfigFile), message, TextFormat.RAW); } @@ -348,7 +364,6 @@ public class DefaultConfiguration extends Configuration { } } - @VisibleForTesting @NonNull public static String globToRegexp(@NonNull String glob) { StringBuilder sb = new StringBuilder(glob.length() * 2); diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultSdkInfo.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultSdkInfo.java index 5097822ad59..cc7b157a2c4 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultSdkInfo.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultSdkInfo.java @@ -16,6 +16,52 @@ package com.android.tools.klint.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.CHECKABLE; +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; @@ -26,8 +72,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -import static com.android.SdkConstants.*; - /** * Default simple implementation of an {@link SdkInfo} *
@@ -85,7 +129,7 @@ class DefaultSdkInfo extends SdkInfo {
if (parent.equals(child)) {
return true;
}
- if (implementsInterface(child, parentType)) {
+ if (implementsInterface(child, parent)) {
return true;
}
child = PARENTS.get(child);
@@ -102,7 +146,7 @@ class DefaultSdkInfo extends SdkInfo {
return interfaceName.equals(INTERFACES.get(className));
}
- // Strip off type parameters, e.g. AdapterView> => AdapterView
+ // Strip off type parameters, e.g. AdapterView> ⇒ AdapterView
private static String getRawType(String type) {
if (type != null) {
int index = type.indexOf('<');
diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/IssueRegistry.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/IssueRegistry.java
index 552ea3e2cea..bc822ec73c0 100644
--- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/IssueRegistry.java
+++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/IssueRegistry.java
@@ -18,7 +18,6 @@ package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.annotations.VisibleForTesting;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
@@ -27,8 +26,16 @@ import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.google.common.annotations.Beta;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
-import java.util.*;
+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
@@ -38,8 +45,8 @@ import java.util.*;
*/
@Beta
public abstract class IssueRegistry {
- private static List
- * NOTE: This is only intended for testing purposes.
*/
- @VisibleForTesting
protected static void reset() {
sIdToIssue = null;
sCategories = null;
diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JarFileIssueRegistry.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JarFileIssueRegistry.java
index bcafb754a1c..79b4dca57b5 100644
--- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JarFileIssueRegistry.java
+++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JarFileIssueRegistry.java
@@ -15,23 +15,32 @@
*/
package com.android.tools.klint.client.api;
+import static com.android.SdkConstants.DOT_CLASS;
+
import com.android.annotations.NonNull;
import com.android.tools.klint.detector.api.Issue;
+import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.utils.SdkUtils;
import com.google.common.collect.Lists;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.lang.ref.SoftReference;
+import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
+import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
/**
* An {@link IssueRegistry} for a custom lint rule jar file. The rule jar should provide a
@@ -46,15 +55,24 @@ class JarFileIssueRegistry extends IssueRegistry {
* Manifest constant for declaring an issue provider. Example: Lint-Registry:
* foo.bar.CustomIssueRegistry
*/
- private static final String MF_LINT_REGISTRY = "Lint-Registry"; //$NON-NLS-1$
+ private static final String MF_LINT_REGISTRY_OLD = "Lint-Registry"; //$NON-NLS-1$
+ private static final String MF_LINT_REGISTRY = "Lint-Registry-v2"; //$NON-NLS-1$
private static Map
+ * Therefore, if we can call close() on this URLClassLoader, we'll proactively load
+ * all class files we find in the .jar file, then close it.
+ *
+ * @param client the client to report errors to
+ * @param file the .jar file
+ * @param loader the URLClassLoader we should close
+ */
+ private static void loadAndCloseURLClassLoader(
+ @NonNull LintClient client,
+ @NonNull File file,
+ @NonNull URLClassLoader loader) {
+ try {
+ // Proactively close out the .jar file. This is only available on Java 7.
+ Method closeMethod = loader.getClass().getDeclaredMethod("close");
+
+ // But first, proactively load all classes:
+ try {
+ InputStream inputStream = new FileInputStream(file);
+ try {
+ JarInputStream jarInputStream = new JarInputStream(inputStream);
+ try {
+ ZipEntry entry = jarInputStream.getNextEntry();
+ while (entry != null) {
+ String name = entry.getName();
+ // Load non-inner-classes
+ if (name.endsWith(DOT_CLASS)) {
+ // Strip .class suffix and change .jar file path (/)
+ // to class name (.'s).
+ name = name.substring(0,
+ name.length() - DOT_CLASS.length());
+ name = name.replace('/', '.');
+ try {
+ Class.forName(name, true, loader);
+ } catch (Throwable e) {
+ client.log(Severity.ERROR, e,
+ "Failed to prefetch " + name + " from " + file);
+ }
+ }
+ entry = jarInputStream.getNextEntry();
+ }
+ } finally {
+ jarInputStream.close();
+ }
+ } finally {
+ inputStream.close();
+ }
+ } catch (Throwable ignore) {
+ } finally {
+ // Finally close the URL class loader
+ try {
+ closeMethod.invoke(loader);
+ } catch (Throwable ignore) {
+ // Couldn't close. This is unlikely.
+ }
+ }
+ } catch (NoSuchMethodException ignore) {
+ // No close method - we're on 1.6
+ }
+ }
+
@NonNull
@Override
public List
* NOTE: This is not public or final API; if you rely on this be prepared
* to adjust your code for the next tools release.
*/
+// Currently ships with deprecated API support
+@SuppressWarnings({"deprecation", "UnusedParameters"})
@Beta
public abstract class JavaParser {
- public static final String TYPE_OBJECT = "java.lang.Object"; //$NON-NLS-1$
- public static final String TYPE_STRING = "java.lang.String"; //$NON-NLS-1$
- public static final String TYPE_INT = "int"; //$NON-NLS-1$
- public static final String TYPE_LONG = "long"; //$NON-NLS-1$
- public static final String TYPE_CHAR = "char"; //$NON-NLS-1$
- public static final String TYPE_FLOAT = "float"; //$NON-NLS-1$
- public static final String TYPE_DOUBLE = "double"; //$NON-NLS-1$
- public static final String TYPE_BOOLEAN = "boolean"; //$NON-NLS-1$
- public static final String TYPE_SHORT = "short"; //$NON-NLS-1$
- public static final String TYPE_BYTE = "byte"; //$NON-NLS-1$
- public static final String TYPE_NULL = "null"; //$NON-NLS-1$
+ public static final String TYPE_OBJECT = "java.lang.Object";
+ public static final String TYPE_STRING = "java.lang.String";
+ public static final String TYPE_INT = "int";
+ public static final String TYPE_LONG = "long";
+ public static final String TYPE_CHAR = "char";
+ public static final String TYPE_FLOAT = "float";
+ public static final String TYPE_DOUBLE = "double";
+ public static final String TYPE_BOOLEAN = "boolean";
+ public static final String TYPE_SHORT = "short";
+ public static final String TYPE_BYTE = "byte";
+ public static final String TYPE_NULL = "null";
+ public static final String TYPE_INTEGER_WRAPPER = "java.lang.Integer";
+ public static final String TYPE_BOOLEAN_WRAPPER = "java.lang.Boolean";
+ public static final String TYPE_BYTE_WRAPPER = "java.lang.Byte";
+ public static final String TYPE_SHORT_WRAPPER = "java.lang.Short";
+ public static final String TYPE_LONG_WRAPPER = "java.lang.Long";
+ public static final String TYPE_DOUBLE_WRAPPER = "java.lang.Double";
+ public static final String TYPE_FLOAT_WRAPPER = "java.lang.Float";
+ public static final String TYPE_CHARACTER_WRAPPER = "java.lang.Character";
+
+ /**
+ * Prepare to parse the given contexts. This method will be called before
+ * a series of {@link #parseJava(JavaContext)} calls, which allows some
+ * parsers to do up front global computation in case they want to more
+ * efficiently process multiple files at the same time. This allows a single
+ * type-attribution pass for example, which is a lot more efficient than
+ * performing global type analysis over and over again for each individual
+ * file
+ *
+ * @param contexts a list of contexts to be parsed
+ */
+ public abstract void prepareJavaParse(@NonNull List
+ * This is a workaround for the fact that the Lombok AST API (and implementation)
+ * doesn't support multi-catch statements.
+ * @deprecated Use {@link JavaPsiScanner} APIs instead
+ */
+ @Deprecated
+ public List
+ * Note that looking up the AST node can result in different instances for each lookup.
+ *
+ * @return an AST node, if possible.
+ */
+ @Nullable
+ public Node findAstNode() {
+ return null;
+ }
+ }
+
+ /**
+ * A resolved class declaration (class, interface, enumeration or annotation)
+ * @deprecated Use {@link JavaPsiScanner} APIs instead
+ */
+ @SuppressWarnings("unused")
+ @Deprecated
+ public abstract static class ResolvedClass extends ResolvedNode {
+ /** Returns the fully qualified name of this class */
+ @Override
+ @NonNull
+ public abstract String getName();
+
+ /** Returns the simple name of this class */
+ @NonNull
+ public abstract String getSimpleName();
+
+ /** Returns the package name of this class */
+ @NonNull
+ public String getPackageName() {
+ String name = getName();
+ String simpleName = getSimpleName();
+ if (name.length() > simpleName.length() + 1) {
+ return name.substring(0, name.length() - simpleName.length() - 1);
+ }
+ return name;
+ }
+
+ /** Returns whether this class' fully qualified name matches the given name */
+ public abstract boolean matches(@NonNull String name);
+
+ @Nullable
+ public abstract ResolvedClass getSuperClass();
+
+ @NonNull
+ public abstract Iterable
+ * The target must be a class; to check whether this class extends an interface,
+ * use {@link #isImplementing(String,boolean)} instead. If you're not sure, use
+ * {@link #isInheritingFrom(String, boolean)}.
+ *
+ * @param name the fully qualified class name
+ * @param strict if true, do not consider a class to be extending itself
+ * @return true if this class extends the given class
+ */
+ public abstract boolean isSubclassOf(@NonNull String name, boolean strict);
+
+ /**
+ * Determines whether this is implementing the given interface.
+ *
+ * The target must be an interface; to check whether this class extends a class,
+ * use {@link #isSubclassOf(String, boolean)} instead. If you're not sure, use
+ * {@link #isInheritingFrom(String, boolean)}.
+ *
+ * @param name the fully qualified interface name
+ * @param strict if true, do not consider a class to be extending itself
+ * @return true if this class implements the given interface
+ */
+ public abstract boolean isImplementing(@NonNull String name, boolean strict);
+
+ /**
+ * Determines whether this class extends or implements the class of the given name.
+ * If strict is true, it will not consider C extends C true.
+ *
+ * For performance reasons, if you know that the target is a class, consider using
+ * {@link #isSubclassOf(String, boolean)} instead, and if the target is an interface,
+ * consider using {@link #isImplementing(String,boolean)}.
+ *
+ * @param name the fully qualified class name
+ * @param strict if true, do not consider a class to be inheriting from itself
+ * @return true if this class extends or implements the given class
+ */
+ public abstract boolean isInheritingFrom(@NonNull String name, boolean strict);
+
+ @NonNull
+ public abstract Iterable
* 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.
*/
@@ -67,6 +85,16 @@ import static com.android.tools.klint.detector.api.LintUtils.endsWith;
public abstract class LintClient {
private static final String PROP_BIN_DIR = "com.android.tools.lint.bindir"; //$NON-NLS-1$
+ protected LintClient(@NonNull String clientName) {
+ //noinspection AssignmentToStaticFieldFromInstanceMethod
+ sClientName = clientName;
+ }
+
+ protected LintClient() {
+ //noinspection AssignmentToStaticFieldFromInstanceMethod
+ sClientName = "unknown";
+ }
+
/**
* Returns a configuration for use by the given project. The configuration
* provides information about which issues are enabled, any customizations
@@ -75,15 +103,17 @@ public abstract class LintClient {
* By default this method returns a {@link DefaultConfiguration}.
*
* @param project the project to obtain a configuration for
+ * @param driver the current driver, if any
* @return a configuration, never null.
*/
- public Configuration getConfiguration(@NonNull Project project) {
+ @NonNull
+ public Configuration getConfiguration(@NonNull Project project, @Nullable LintDriver driver) {
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
+ * provided by {@link #getConfiguration(Project,LintDriver)} has reported the corresponding
* issue as enabled and has not filtered out the issue with its
* {@link Configuration#ignore(Context,Issue,Location,String)} method.
*
@@ -98,7 +128,7 @@ public abstract class LintClient {
@NonNull Context context,
@NonNull Issue issue,
@NonNull Severity severity,
- @Nullable Location location,
+ @NonNull Location location,
@NonNull String message,
@NonNull TextFormat format);
@@ -141,6 +171,19 @@ public abstract class LintClient {
@Nullable
public abstract XmlParser getXmlParser();
+ /**
+ * Returns a {@link JavaParser} to use to parse Java
+ *
+ * @param project the project to parse, if known (this can be used to look up
+ * the class path for type attribution etc, and it can also be used
+ * to more efficiently process a set of files, for example to
+ * perform type attribution for multiple units in a single pass)
+ * @return a new {@link JavaParser}, or null if this client does not
+ * support Java analysis
+ */
+ @Nullable
+ public abstract JavaParser getJavaParser(@Nullable Project project);
+
/**
* Returns an optimal detector, if applicable. By default, just returns the
* original detector, but tools can replace detectors using this hook with a version
@@ -207,12 +250,15 @@ public abstract class LintClient {
/**
* Returns the list of Java libraries
*
- * @param project the project to look up jar dependencies for
+ * @param project the project to look up jar dependencies for
+ * @param includeProvided If true, included provided libraries too (libraries that are not
+ * packaged with the app, but are provided for compilation purposes and
+ * are assumed to be present in the running environment)
* @return a list of jar dependencies containing .class files
*/
@NonNull
- public List
+ *
+ * It also notifies all the detectors before and after the document is processed
+ * such that they can do pre- and post-processing.
+ */
+public class JavaPsiVisitor {
+ /** 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