aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--anttasks/src/com/android/ant/AaptExecTask.java8
-rw-r--r--anttasks/src/com/android/ant/NewSetupTask.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/Hyperlinks.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java17
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfiguration.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/LintPreferencePage.java5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizardState.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger.tests/src/com/android/ide/eclipse/gltrace/format/GLMessageFormatterTest.java14
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/plugin.xml8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/CollectTraceAction.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTracePerspective.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java29
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/format/GLMessageFormatter.java17
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLObjectProperty.java66
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLState.java31
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLStateType.java12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/CurrentProgramPropertyAccessor.java60
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/SparseArrayElementAddTransform.java14
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/StateTransformFactory.java177
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/DetailsPage.java203
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/DetailsView.java51
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FitToCanvasAction.java40
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferView.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferViewPage.java26
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/IStateDetailsProvider.java45
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/ImageViewPart.java108
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/ShaderSourceDetailsProvider.java102
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/TextureImageDetailsProvider.java114
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/TextureView.java104
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfigurationTest.java2
-rw-r--r--lint/cli/src/com/android/tools/lint/HtmlReporter.java4
-rw-r--r--lint/cli/src/com/android/tools/lint/LintCliXmlParser.java13
-rw-r--r--lint/cli/src/com/android/tools/lint/LombokParser.java14
-rw-r--r--lint/cli/src/com/android/tools/lint/Main.java14
-rw-r--r--lint/cli/src/com/android/tools/lint/MultiProjectHtmlReporter.java2
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/client/api/LintClient.java32
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java268
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/client/api/XmlVisitor.java6
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/Category.java18
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java29
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java3
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/Location.java54
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/Project.java23
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/Scope.java3
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/Severity.java9
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java39
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiLookup.java28
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java11
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/DetectMissingPrefix.java3
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/DuplicateIdDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/FieldGetterDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java4
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/IconDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java454
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java56
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/MathDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/NamespaceDetector.java217
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/PrivateResourceDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ProguardDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/SdCardDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java3
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/StringFormatDetector.java6
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/StyleCycleDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/TranslationDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ViewConstructorDetector.java81
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongIdDetector.java28
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongImportDetector.java2
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java4
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java16
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/FieldGetterDetectorTest.java15
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java45
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ManifestOrderDetectorTest.java13
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/NamespaceDetectorTest.java102
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/SecurityDetectorTest.java9
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ViewConstructorDetectorTest.java18
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/WrongIdDetectorTest.java19
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.class.databin0 -> 409 bytes
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.java.txt12
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/Intermediate$IntermediateCustomV.class.databin0 -> 440 bytes
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/Intermediate.class.databin0 -> 383 bytes
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/Intermediate.java.txt15
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/CustomViewTest.class.databin0 -> 390 bytes
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/classpath-lib9
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportservice5.xml24
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/multiplesdk.xml25
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/customview.xml30
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/unused_namespace.xml14
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace.xml24
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace2.xml24
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace3.xml24
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace4.xml27
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/CustomViewTest.java.txt6
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt147
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml47
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java14
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PkgItem.java4
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java6
-rwxr-xr-xsdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogicTest.java2
101 files changed, 2963 insertions, 456 deletions
diff --git a/anttasks/src/com/android/ant/AaptExecTask.java b/anttasks/src/com/android/ant/AaptExecTask.java
index 610ce0f..e3bb0a2 100644
--- a/anttasks/src/com/android/ant/AaptExecTask.java
+++ b/anttasks/src/com/android/ant/AaptExecTask.java
@@ -487,7 +487,7 @@ public final class AaptExecTask extends SingleDependencyTask {
}
// filters if needed
- if (mResourceFilter != null) {
+ if (mResourceFilter != null && mResourceFilter.length() > 0) {
task.createArg().setValue("-c");
task.createArg().setValue(mResourceFilter);
}
@@ -511,7 +511,7 @@ public final class AaptExecTask extends SingleDependencyTask {
}
}
- if (extraPackages != null) {
+ if (extraPackages != null && extraPackages.length() > 0) {
task.createArg().setValue("--extra-packages");
task.createArg().setValue(extraPackages);
}
@@ -526,13 +526,13 @@ public final class AaptExecTask extends SingleDependencyTask {
task.createArg().setValue(Integer.toString(mVersionCode));
}
- if ((mVersionName != null) && (mVersionName.length() > 0)) {
+ if (mVersionName != null && mVersionName.length() > 0) {
task.createArg().setValue("--version-name");
task.createArg().setValue(mVersionName);
}
// manifest location
- if (mManifest != null) {
+ if (mManifest != null && mManifest.length() > 0) {
task.createArg().setValue("-M");
task.createArg().setValue(mManifest);
}
diff --git a/anttasks/src/com/android/ant/NewSetupTask.java b/anttasks/src/com/android/ant/NewSetupTask.java
index b8765da..f3fd9aa 100644
--- a/anttasks/src/com/android/ant/NewSetupTask.java
+++ b/anttasks/src/com/android/ant/NewSetupTask.java
@@ -528,6 +528,8 @@ public class NewSetupTask extends Task {
System.out.println("------------------\n");
+ boolean hasLibraries = jarsPath.list().length > 0;
+
if (androidTarget.getVersion().getApiLevel() <= 15) {
System.out.println("API<=15: Adding annotations.jar to the classpath.\n");
@@ -545,7 +547,7 @@ public class NewSetupTask extends Task {
antProject.addReference(mProjectLibrariesLibsOut, libsPath);
// the rest is done only if there's a library.
- if (jarsPath.list().length > 0) {
+ if (hasLibraries) {
antProject.addReference(mProjectLibrariesRootOut, rootPath);
antProject.addReference(mProjectLibrariesResOut, resPath);
antProject.setProperty(mProjectLibrariesPackageOut, packageStrBuilder.toString());
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/Hyperlinks.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/Hyperlinks.java
index 4b7fbe0..6da978d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/Hyperlinks.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/Hyperlinks.java
@@ -669,7 +669,7 @@ public class Hyperlinks {
private static FolderConfiguration getConfiguration() {
IEditorPart editor = getEditor();
if (editor != null) {
- LayoutEditorDelegate delegate = (LayoutEditorDelegate) editor;
+ LayoutEditorDelegate delegate = LayoutEditorDelegate.fromEditor(editor);
GraphicalEditorPart graphicalEditor =
delegate == null ? null : delegate.getGraphicalEditor();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java
index 2a55395..cf6e967 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java
@@ -79,7 +79,7 @@ class ExtractStyleWizard extends VisualRefactoringWizard {
private Button mRemoveExtracted;
private Button mSetStyle;
private Button mRemoveAll;
- private Button mExtend;;
+ private Button mExtend;
private CheckboxTableViewer mCheckedView;
private String mParentStyle;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java
index e3669f0..b3174d3 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java
@@ -35,7 +35,6 @@ import com.android.tools.lint.checks.ObsoleteLayoutParamsDetector;
import com.android.tools.lint.checks.OverdrawDetector;
import com.android.tools.lint.checks.StringFormatDetector;
import com.android.tools.lint.checks.TranslationDetector;
-import com.android.tools.lint.checks.WrongIdDetector;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
@@ -183,9 +182,7 @@ class AddSuppressAttribute implements ICompletionProposal {
|| id.equals(StringFormatDetector.ARG_TYPES.getId())
|| id.equals(StringFormatDetector.ARG_COUNT.getId())
|| id.equals(TranslationDetector.MISSING.getId())
- || id.equals(TranslationDetector.EXTRA.getId())
- || id.equals(WrongIdDetector.UNKNOWN_ID.getId())
- || id.equals(WrongIdDetector.UNKNOWN_ID_LAYOUT.getId())) {
+ || id.equals(TranslationDetector.EXTRA.getId())) {
node = document.getDocumentElement();
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java
index 8c8bea7..3289bbe 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java
@@ -124,7 +124,7 @@ public class EclipseLintClient extends LintClient implements IDomParser {
// ----- Extends LintClient -----
@Override
- public void log(Throwable exception, String format, Object... args) {
+ public void log(Severity severity, Throwable exception, String format, Object... args) {
if (exception == null) {
AdtPlugin.log(IStatus.WARNING, format, args);
} else {
@@ -261,7 +261,7 @@ public class EclipseLintClient extends LintClient implements IDomParser {
}
}
- if (s == Severity.ERROR) {
+ if (s == Severity.FATAL) {
mWasFatal = true;
}
}
@@ -369,6 +369,7 @@ public class EclipseLintClient extends LintClient implements IDomParser {
return IMarker.SEVERITY_INFO;
case WARNING:
return IMarker.SEVERITY_WARNING;
+ case FATAL:
case ERROR:
default:
return IMarker.SEVERITY_ERROR;
@@ -821,6 +822,7 @@ public class EclipseLintClient extends LintClient implements IDomParser {
private class LocationHandle implements Handle {
private File mFile;
private lombok.ast.Node mNode;
+ private Object mClientData;
public LocationHandle(File file, lombok.ast.Node node) {
mFile = file;
@@ -832,6 +834,17 @@ public class EclipseLintClient extends LintClient implements IDomParser {
lombok.ast.Position pos = mNode.getPosition();
return Location.create(mFile, null /*contents*/, pos.getStart(), pos.getEnd());
}
+
+ @Override
+ public void setClientData(@Nullable Object clientData) {
+ mClientData = clientData;
+ }
+
+ @Override
+ @Nullable
+ public Object getClientData() {
+ return mClientData;
+ }
}
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfiguration.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfiguration.java
index 671dd67..77cd115 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfiguration.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfiguration.java
@@ -81,7 +81,7 @@ class ProjectLintConfiguration extends DefaultConfiguration {
@Override
public Severity getSeverity(Issue issue) {
Severity severity = super.getSeverity(issue);
- if (mFatalOnly && severity != Severity.ERROR) {
+ if (mFatalOnly && severity != Severity.FATAL) {
return Severity.IGNORE;
}
return severity;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/LintPreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/LintPreferencePage.java
index f52d719..bd2423e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/LintPreferencePage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/LintPreferencePage.java
@@ -147,7 +147,7 @@ public class LintPreferencePage extends PropertyPage implements IWorkbenchPrefer
mCheckExportCheckbox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false,
2, 1));
mCheckExportCheckbox.setSelection(true);
- mCheckExportCheckbox.setText("Run full error check when exporting app");
+ mCheckExportCheckbox.setText("Run full error check when exporting app and abort if fatal errors are found");
Label separator = new Label(container, SWT.SEPARATOR | SWT.HORIZONTAL);
separator.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1));
@@ -227,7 +227,7 @@ public class LintPreferencePage extends PropertyPage implements IWorkbenchPrefer
mSeverityCombo = new Combo(container, SWT.READ_ONLY);
mSeverityCombo.setItems(new String[] {
- "(Default)", "Error", "Warning", "Information", "Ignore"
+ "(Default)", "Fatal", "Error", "Warning", "Information", "Ignore"
});
GridData gdSeverityCombo = new GridData(SWT.FILL, SWT.TOP, false, false, 1, 1);
gdSeverityCombo.widthHint = 90;
@@ -679,6 +679,7 @@ public class LintPreferencePage extends PropertyPage implements IWorkbenchPrefer
ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
switch (severity) {
+ case FATAL:
case ERROR:
return sharedImages.getImage(ISharedImages.IMG_OBJS_ERROR_TSK);
case WARNING:
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizardState.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizardState.java
index 6b8a1fb..33f9884 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizardState.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizardState.java
@@ -215,7 +215,7 @@ public class NewProjectWizardState {
}
if (newPackageName != null && newPackageName.length() > 0) {
- packageName = newPackageName;;
+ packageName = newPackageName;
}
if (activity != null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger.tests/src/com/android/ide/eclipse/gltrace/format/GLMessageFormatterTest.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger.tests/src/com/android/ide/eclipse/gltrace/format/GLMessageFormatterTest.java
index 305e8ac..984cbee 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger.tests/src/com/android/ide/eclipse/gltrace/format/GLMessageFormatterTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger.tests/src/com/android/ide/eclipse/gltrace/format/GLMessageFormatterTest.java
@@ -39,6 +39,7 @@ public class GLMessageFormatterTest {
"const GLchar*, glGetString, GLenum name",
"void, glMultMatrixf, const GLfloat* m",
"GLenum, eglBindAPI, GLEnum arg",
+ "void, glGetActiveAttrib, GLenum* type",
"void, glTexImage2D, GLint level, GLsizei width, const GLvoid* pixels");
private static GLMessageFormatter sGLMessageFormatter;
@@ -126,6 +127,19 @@ public class GLMessageFormatterTest {
assertEquals(expected, actual);
}
+ @Test
+ public void testMessageWithEnumPointer() {
+ //void, glGetActiveAttrib, GLenum* type
+ GLMessage msg = constructGLMessage(null,
+ Function.glGetActiveAttrib,
+ createIntegerPointerDataType(GLEnum.GL_FLOAT_MAT4.value));
+
+ String expected = "glGetActiveAttrib(type = [GL_FLOAT_MAT4])";
+ String actual = sGLMessageFormatter.formatGLMessage(msg);
+
+ assertEquals(expected, actual);
+ }
+
private DataType createStringDataType(String retValue) {
return DataType.newBuilder()
.addCharValue(ByteString.copyFromUtf8(retValue))
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.gldebugger/plugin.xml
index bcc3871..d693916 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/plugin.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/plugin.xml
@@ -19,7 +19,7 @@
category="com.android.ide.eclipse.gltrace"
class="com.android.ide.eclipse.gltrace.views.FrameBufferView"
icon="icons/opengl.png"
- id="com.android.ide.eclipse.gltrace.views.GLFrameBuffer"
+ id="com.android.ide.eclipse.gltrace.views.FrameBuffer"
name="Framebuffer"
restorable="true">
</view>
@@ -33,10 +33,10 @@
</view>
<view
category="com.android.ide.eclipse.gltrace"
- class="com.android.ide.eclipse.gltrace.views.TextureView"
+ class="com.android.ide.eclipse.gltrace.views.DetailsView"
icon="icons/opengl.png"
- id="com.android.ide.eclipse.gltrace.views.Texture"
- name="Texture"
+ id="com.android.ide.eclipse.gltrace.views.Details"
+ name="Details"
restorable="true">
</view>
</extension>
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/CollectTraceAction.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/CollectTraceAction.java
index 04916b5..58e78b4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/CollectTraceAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/CollectTraceAction.java
@@ -206,9 +206,9 @@ public class CollectTraceAction implements IWorkbenchWindowActionDelegate {
}
private void disableGLTrace(IDevice device) {
- // The only way to disable is by enabling tracing with an empty package name
+ // The only way to disable is by enabling tracing with an invalid package name
try {
- setGLTraceOn(device, "");
+ setGLTraceOn(device, "no.such.package"); //$NON-NLS-1$
} catch (Exception e) {
// ignore any exception
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTracePerspective.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTracePerspective.java
index 71b929b..d8f1a0b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTracePerspective.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTracePerspective.java
@@ -16,9 +16,9 @@
package com.android.ide.eclipse.gltrace;
+import com.android.ide.eclipse.gltrace.views.DetailsView;
import com.android.ide.eclipse.gltrace.views.FrameBufferView;
import com.android.ide.eclipse.gltrace.views.StateView;
-import com.android.ide.eclipse.gltrace.views.TextureView;
import org.eclipse.ui.IFolderLayout;
import org.eclipse.ui.IPageLayout;
@@ -44,7 +44,7 @@ public class GLTracePerspective implements IPerspectiveFactory {
// Add the Texture View in the 3rd column
IFolderLayout column3 = layout.createFolder(FB_FOLDER_ID, IPageLayout.RIGHT, 0.6f,
STATE_FOLDER_ID);
- column3.addView(TextureView.ID);
+ column3.addView(DetailsView.ID);
// Add the OpenGL Framebuffer view below the texture view (bottom of 3rd column)
IFolderLayout column3bottom = layout.createFolder(TEXTURE_VIEW_FOLDER_ID,
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java
index 6eac701..debd086 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java
@@ -24,6 +24,7 @@ import com.android.ide.eclipse.gltrace.editors.GLCallGroups.GLCallNode;
import com.android.ide.eclipse.gltrace.model.GLCall;
import com.android.ide.eclipse.gltrace.model.GLFrame;
import com.android.ide.eclipse.gltrace.model.GLTrace;
+import com.android.ide.eclipse.gltrace.views.DetailsPage;
import com.android.ide.eclipse.gltrace.views.FrameBufferViewPage;
import org.eclipse.core.runtime.IProgressMonitor;
@@ -276,7 +277,9 @@ public class GLFunctionTraceViewer extends EditorPart implements ISelectionProvi
mDurationMinimap.setCallRangeForCurrentFrame(mCallStartIndex, mCallEndIndex);
// update FB view
- mFrameBufferViewPage.setSelectedFrame(selectedFrame - 1);
+ if (mFrameBufferViewPage != null) {
+ mFrameBufferViewPage.setSelectedFrame(selectedFrame - 1);
+ }
}
/**
@@ -561,8 +564,7 @@ public class GLFunctionTraceViewer extends EditorPart implements ISelectionProvi
return true;
}
- GLCall call = ((GLCallNode) element).getCall();
- String text = call.getFunction().toString();
+ String text = getTextUnderNode((GLCallNode) element);
if (mPatterns.size() == 0) {
// match if there are no regex filters
@@ -579,6 +581,23 @@ public class GLFunctionTraceViewer extends EditorPart implements ISelectionProvi
return false;
}
+
+ /** Obtain a string representation of all functions under a given tree node. */
+ private String getTextUnderNode(GLCallNode element) {
+ String func = element.getCall().getFunction().toString();
+ if (!element.hasChildren()) {
+ return func;
+ }
+
+ StringBuilder sb = new StringBuilder(100);
+ sb.append(func);
+
+ for (GLCallNode child : element.getChildren()) {
+ sb.append(getTextUnderNode(child));
+ }
+
+ return sb.toString();
+ }
}
@Override
@@ -626,4 +645,8 @@ public class GLFunctionTraceViewer extends EditorPart implements ISelectionProvi
return mFrameBufferViewPage;
}
+
+ public DetailsPage getDetailsPage() {
+ return new DetailsPage(mTrace);
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/format/GLMessageFormatter.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/format/GLMessageFormatter.java
index 2b7c491..c0a11fe 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/format/GLMessageFormatter.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/format/GLMessageFormatter.java
@@ -115,7 +115,7 @@ public class GLMessageFormatter {
}
private String formatPointer(DataType var, Type typeSpec) {
- if (var.getType() != typeSpec) {
+ if (var.getType() != typeSpec && !isEnumTypeWithIntData(var, typeSpec)) {
// the type of the data in the message does not match expected specification.
// in such a case, just print the data as a pointer and don't try to interpret it.
if (var.getIntValueCount() > 0) {
@@ -126,7 +126,7 @@ public class GLMessageFormatter {
}
// Display as array if possible
- switch (var.getType()) {
+ switch (typeSpec) {
case BOOL:
return var.getBoolValueList().toString();
case FLOAT:
@@ -135,6 +135,15 @@ public class GLMessageFormatter {
return var.getIntValueList().toString();
case CHAR:
return var.getCharValueList().get(0).toStringUtf8();
+ case ENUM:
+ List<Integer> vals = var.getIntValueList();
+ StringBuilder sb = new StringBuilder(vals.size() * 5);
+ sb.append('[');
+ for (Integer v: vals) {
+ sb.append(GLEnum.valueOf(v.intValue()));
+ }
+ sb.append(']');
+ return sb.toString();
}
// We have a pointer, but we don't have the data pointed to.
@@ -145,4 +154,8 @@ public class GLMessageFormatter {
return String.format("0x%x", var.getIntValue(0)); //$NON-NLS-1$
}
}
+
+ private boolean isEnumTypeWithIntData(DataType var, Type typeSpec) {
+ return var.getType() == Type.INT && typeSpec == Type.ENUM;
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLObjectProperty.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLObjectProperty.java
new file mode 100644
index 0000000..84ad6ec
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLObjectProperty.java
@@ -0,0 +1,66 @@
+/*
+ * 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.ide.eclipse.gltrace.state;
+
+import com.google.common.base.Joiner;
+
+import java.util.List;
+
+public class GLObjectProperty extends GLAbstractAtomicProperty {
+ private final Object mDefaultValue;
+ private Object mCurrentValue;
+
+ private static final Joiner JOINER = Joiner.on(", "); //$NON-NLS-1$
+
+ public GLObjectProperty(GLStateType type, Object defaultValue) {
+ super(type);
+
+ mDefaultValue = mCurrentValue = defaultValue;
+ }
+
+ @Override
+ public boolean isDefault() {
+ return mDefaultValue == mCurrentValue;
+ }
+
+ @Override
+ public void setValue(Object newValue) {
+ mCurrentValue = newValue;
+ }
+
+ @Override
+ public String getStringValue() {
+ if (mCurrentValue == null) {
+ return "null";
+ } else {
+ if (mCurrentValue instanceof List<?>) {
+ return "[" + JOINER.join((List<?>) mCurrentValue) + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ return mCurrentValue.toString();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getType() + "=" + getStringValue(); //$NON-NLS-1$
+ }
+
+ @Override
+ public Object getValue() {
+ return mCurrentValue;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLState.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLState.java
index c5bbd0b..dfc922a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLState.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLState.java
@@ -19,6 +19,8 @@ package com.android.ide.eclipse.gltrace.state;
import com.android.ide.eclipse.gldebugger.GLEnum;
import com.android.ide.eclipse.gltrace.state.GLIntegerProperty.DisplayRadix;
+import java.util.Collections;
+
public class GLState {
/** # of texture units modelled in the GL State. */
public static final int TEXTURE_UNIT_COUNT = 8;
@@ -252,12 +254,37 @@ public class GLState {
IGLProperty attachedShaders = new GLSparseArrayProperty(GLStateType.ATTACHED_SHADERS,
attachedShaderId);
+ IGLProperty attributeName = new GLStringProperty(GLStateType.ATTRIBUTE_NAME, "");
+ IGLProperty attributeType = new GLEnumProperty(GLStateType.ATTRIBUTE_TYPE,
+ GLEnum.GL_FLOAT_MAT4);
+ IGLProperty attributeSize = new GLIntegerProperty(GLStateType.ATTRIBUTE_SIZE,
+ Integer.valueOf(1));
+ IGLProperty attributeValue = new GLObjectProperty(GLStateType.ATTRIBUTE_VALUE,
+ Collections.emptyList());
+ IGLProperty perAttributeProperty = new GLCompositeProperty(GLStateType.PER_ATTRIBUTE_STATE,
+ attributeName, attributeType, attributeSize, attributeValue);
+ IGLProperty attributes = new GLSparseArrayProperty(GLStateType.ACTIVE_ATTRIBUTES,
+ perAttributeProperty);
+
+ IGLProperty uniformName = new GLStringProperty(GLStateType.UNIFORM_NAME, "");
+ IGLProperty uniformType = new GLEnumProperty(GLStateType.UNIFORM_TYPE,
+ GLEnum.GL_FLOAT_MAT4);
+ IGLProperty uniformSize = new GLIntegerProperty(GLStateType.UNIFORM_SIZE,
+ Integer.valueOf(1));
+ IGLProperty uniformValue = new GLObjectProperty(GLStateType.UNIFORM_VALUE,
+ Collections.emptyList());
+ IGLProperty perUniformProperty = new GLCompositeProperty(GLStateType.PER_UNIFORM_STATE,
+ uniformName, uniformType, uniformSize, uniformValue);
+ IGLProperty uniforms = new GLSparseArrayProperty(GLStateType.ACTIVE_UNIFORMS,
+ perUniformProperty);
+
IGLProperty perProgramState = new GLCompositeProperty(GLStateType.PER_PROGRAM_STATE,
- attachedShaders);
+ attachedShaders, attributes, uniforms);
IGLProperty programs = new GLSparseArrayProperty(GLStateType.PROGRAMS, perProgramState);
- return new GLCompositeProperty(GLStateType.PROGRAM_STATE, currentProgram, programs);
+ return new GLCompositeProperty(GLStateType.PROGRAM_STATE,
+ currentProgram, programs);
}
private IGLProperty createShaderState() {
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLStateType.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLStateType.java
index 4e0decb..42016fa 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLStateType.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLStateType.java
@@ -120,6 +120,18 @@ public enum GLStateType {
PER_PROGRAM_STATE("Per Program State"),
ATTACHED_SHADERS("Attached Shaders"),
ATTACHED_SHADER_ID("Attached Shader ID"),
+ ACTIVE_ATTRIBUTES("Attributes"),
+ PER_ATTRIBUTE_STATE("Per Attribute State"),
+ ATTRIBUTE_NAME("Name"),
+ ATTRIBUTE_TYPE("Type"),
+ ATTRIBUTE_SIZE("Size"),
+ ATTRIBUTE_VALUE("Value"),
+ ACTIVE_UNIFORMS("Uniforms"),
+ PER_UNIFORM_STATE("Per Uniform State"),
+ UNIFORM_NAME("Name"),
+ UNIFORM_TYPE("Type"),
+ UNIFORM_SIZE("Size"),
+ UNIFORM_VALUE("Value"),
SHADERS("Shader Objects"),
PER_SHADER_STATE("Per Shader State"),
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/CurrentProgramPropertyAccessor.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/CurrentProgramPropertyAccessor.java
new file mode 100644
index 0000000..b977d87
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/CurrentProgramPropertyAccessor.java
@@ -0,0 +1,60 @@
+/*
+ * 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.ide.eclipse.gltrace.state.transforms;
+
+import com.android.ide.eclipse.gltrace.state.GLIntegerProperty;
+import com.android.ide.eclipse.gltrace.state.GLStateType;
+import com.android.ide.eclipse.gltrace.state.IGLProperty;
+
+public class CurrentProgramPropertyAccessor implements IGLPropertyAccessor {
+ private final int mContextId;
+ private final GLStateType mStateCategory;
+ private final int mLocation;
+ private final GLStateType mStateType;
+ private final IGLPropertyAccessor mCurrentProgramAccessor;
+
+ public CurrentProgramPropertyAccessor(int contextid, GLStateType stateCategory,
+ int location, GLStateType stateType) {
+ mContextId = contextid;
+ mStateCategory = stateCategory;
+ mLocation = location;
+ mStateType = stateType;
+
+ mCurrentProgramAccessor = GLPropertyAccessor.makeAccessor(contextid,
+ GLStateType.PROGRAM_STATE,
+ GLStateType.CURRENT_PROGRAM);
+ }
+ @Override
+ public IGLProperty getProperty(IGLProperty state) {
+ // obtain the current program
+ IGLProperty currentProgramProperty = mCurrentProgramAccessor.getProperty(state);
+ if (!(currentProgramProperty instanceof GLIntegerProperty)) {
+ return null;
+ }
+
+ Integer program = (Integer) currentProgramProperty.getValue();
+
+ // now access the required program property
+ return GLPropertyAccessor.makeAccessor(mContextId,
+ GLStateType.PROGRAM_STATE,
+ GLStateType.PROGRAMS,
+ program,
+ mStateCategory,
+ Integer.valueOf(mLocation),
+ mStateType).getProperty(state);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/SparseArrayElementAddTransform.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/SparseArrayElementAddTransform.java
index 1d6be8c..b7547bc 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/SparseArrayElementAddTransform.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/SparseArrayElementAddTransform.java
@@ -21,11 +21,12 @@ import com.android.ide.eclipse.gltrace.state.IGLProperty;
/**
* A {@link SparseArrayElementAddTransform} changes given state by adding an
- * element to a sparse array.
+ * element to a sparse array, if there is no item with the same key already.
*/
public class SparseArrayElementAddTransform implements IStateTransform {
private IGLPropertyAccessor mAccessor;
private int mKey;
+ private IGLProperty mOldValue;
public SparseArrayElementAddTransform(IGLPropertyAccessor accessor, int key) {
mAccessor = accessor;
@@ -36,7 +37,11 @@ public class SparseArrayElementAddTransform implements IStateTransform {
public void apply(IGLProperty currentState) {
GLSparseArrayProperty propertyArray = getArray(currentState);
if (propertyArray != null) {
- propertyArray.add(mKey);
+ mOldValue = propertyArray.getProperty(mKey);
+ if (mOldValue == null) {
+ // add only if there is no item with this key already present
+ propertyArray.add(mKey);
+ }
}
}
@@ -44,7 +49,10 @@ public class SparseArrayElementAddTransform implements IStateTransform {
public void revert(IGLProperty currentState) {
GLSparseArrayProperty propertyArray = getArray(currentState);
if (propertyArray != null) {
- propertyArray.delete(mKey);
+ if (mOldValue == null) {
+ // delete only if we actually added this key
+ propertyArray.delete(mKey);
+ }
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/StateTransformFactory.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/StateTransformFactory.java
index d248bd7..3cc1004 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/StateTransformFactory.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/StateTransformFactory.java
@@ -105,6 +105,34 @@ public class StateTransformFactory {
return transformsForGlAttachShader(msg);
case glDetachShader:
return transformsForGlDetachShader(msg);
+ case glGetActiveAttrib:
+ return transformsForGlGetActiveAttrib(msg);
+ case glGetActiveUniform:
+ return transformsForGlGetActiveUniform(msg);
+ case glUniform1i:
+ case glUniform2i:
+ case glUniform3i:
+ case glUniform4i:
+ return transformsForGlUniform(msg, false);
+ case glUniform1f:
+ case glUniform2f:
+ case glUniform3f:
+ case glUniform4f:
+ return transformsForGlUniform(msg, true);
+ case glUniform1iv:
+ case glUniform2iv:
+ case glUniform3iv:
+ case glUniform4iv:
+ return transformsForGlUniformv(msg, false);
+ case glUniform1fv:
+ case glUniform2fv:
+ case glUniform3fv:
+ case glUniform4fv:
+ return transformsForGlUniformv(msg, true);
+ case glUniformMatrix2fv:
+ case glUniformMatrix3fv:
+ case glUniformMatrix4fv:
+ return transformsForGlUniformMatrix(msg);
// Shader State Transformations
case glCreateShader:
@@ -863,6 +891,155 @@ public class StateTransformFactory {
return Collections.singletonList(transform);
}
+ private static List<IStateTransform> transformsForGlGetActiveAttribOrUniform(
+ GLMessage msg, boolean isAttrib) {
+ // void glGetActive[Attrib|Uniform](GLuint program, GLuint index, GLsizei bufsize,
+ // GLsizei* length, GLint* size, GLenum* type, GLchar* name);
+ int program = msg.getArgs(0).getIntValue(0);
+ int size = msg.getArgs(4).getIntValue(0);
+ GLEnum type = GLEnum.valueOf(msg.getArgs(5).getIntValue(0));
+ String name = msg.getArgs(6).getCharValue(0).toStringUtf8();
+
+ // The 2nd argument (index) does not give the correct location of the
+ // attribute/uniform in device. The actual location is obtained from
+ // the getAttribLocation or getUniformLocation calls. The trace library
+ // appends this value as an additional last argument to this call.
+ int location = msg.getArgs(7).getIntValue(0);
+
+ GLStateType activeInput;
+ GLStateType inputName;
+ GLStateType inputType;
+ GLStateType inputSize;
+
+ if (isAttrib) {
+ activeInput = GLStateType.ACTIVE_ATTRIBUTES;
+ inputName = GLStateType.ATTRIBUTE_NAME;
+ inputType = GLStateType.ATTRIBUTE_TYPE;
+ inputSize = GLStateType.ATTRIBUTE_SIZE;
+ } else {
+ activeInput = GLStateType.ACTIVE_UNIFORMS;
+ inputName = GLStateType.UNIFORM_NAME;
+ inputType = GLStateType.UNIFORM_TYPE;
+ inputSize = GLStateType.UNIFORM_SIZE;
+ }
+
+ IStateTransform addAttribute = new SparseArrayElementAddTransform(
+ GLPropertyAccessor.makeAccessor(msg.getContextId(),
+ GLStateType.PROGRAM_STATE,
+ GLStateType.PROGRAMS,
+ Integer.valueOf(program),
+ activeInput),
+ Integer.valueOf(location));
+ IStateTransform setAttributeName = new PropertyChangeTransform(
+ GLPropertyAccessor.makeAccessor(msg.getContextId(),
+ GLStateType.PROGRAM_STATE,
+ GLStateType.PROGRAMS,
+ Integer.valueOf(program),
+ activeInput,
+ Integer.valueOf(location),
+ inputName),
+ name);
+ IStateTransform setAttributeType = new PropertyChangeTransform(
+ GLPropertyAccessor.makeAccessor(msg.getContextId(),
+ GLStateType.PROGRAM_STATE,
+ GLStateType.PROGRAMS,
+ Integer.valueOf(program),
+ activeInput,
+ Integer.valueOf(location),
+ inputType),
+ type);
+ IStateTransform setAttributeSize = new PropertyChangeTransform(
+ GLPropertyAccessor.makeAccessor(msg.getContextId(),
+ GLStateType.PROGRAM_STATE,
+ GLStateType.PROGRAMS,
+ Integer.valueOf(program),
+ activeInput,
+ Integer.valueOf(location),
+ inputSize),
+ Integer.valueOf(size));
+ return Arrays.asList(addAttribute, setAttributeName, setAttributeType, setAttributeSize);
+ }
+
+ private static List<IStateTransform> transformsForGlGetActiveAttrib(GLMessage msg) {
+ return transformsForGlGetActiveAttribOrUniform(msg, true);
+ }
+
+ private static List<IStateTransform> transformsForGlGetActiveUniform(GLMessage msg) {
+ return transformsForGlGetActiveAttribOrUniform(msg, false);
+ }
+
+ private static List<IStateTransform> transformsForGlUniformMatrix(GLMessage msg) {
+ // void glUniformMatrix[2|3|4]fv(GLint location, GLsizei count, GLboolean transpose,
+ // const GLfloat *value);
+ int location = msg.getArgs(0).getIntValue(0);
+ List<Float> uniforms = msg.getArgs(3).getFloatValueList();
+
+ IStateTransform setValues = new PropertyChangeTransform(
+ new CurrentProgramPropertyAccessor(msg.getContextId(),
+ GLStateType.ACTIVE_UNIFORMS,
+ location,
+ GLStateType.UNIFORM_VALUE),
+ uniforms);
+
+ return Collections.singletonList(setValues);
+ }
+
+ private static List<IStateTransform> transformsForGlUniformv(GLMessage msg, boolean isFloats) {
+ // void glUniform1fv(GLint location, GLsizei count, const GLfloat *value);
+ int location = msg.getArgs(0).getIntValue(0);
+ List<?> uniforms;
+ if (isFloats) {
+ uniforms = msg.getArgs(2).getFloatValueList();
+ } else {
+ uniforms = msg.getArgs(2).getIntValueList();
+ }
+
+ IStateTransform setValues = new PropertyChangeTransform(
+ new CurrentProgramPropertyAccessor(msg.getContextId(),
+ GLStateType.ACTIVE_UNIFORMS,
+ location,
+ GLStateType.UNIFORM_VALUE),
+ uniforms);
+
+ return Collections.singletonList(setValues);
+ }
+
+ private static List<IStateTransform> transformsForGlUniform(GLMessage msg, boolean isFloats) {
+ // void glUniform1f(GLint location, GLfloat v0);
+ // void glUniform2f(GLint location, GLfloat v0, GLfloat v1);
+ // .. 3f
+ // .. 4f
+ // void glUniform1i(GLint location, GLfloat v0);
+ // void glUniform2i(GLint location, GLfloat v0, GLfloat v1);
+ // .. 3i
+ // .. 4i
+
+ int location = msg.getArgs(0).getIntValue(0);
+ List<?> uniforms;
+ if (isFloats) {
+ List<Float> args = new ArrayList<Float>(msg.getArgsCount() - 1);
+ for (int i = 1; i < msg.getArgsCount(); i++) {
+ args.add(Float.valueOf(msg.getArgs(1).getFloatValue(0)));
+ }
+ uniforms = args;
+ } else {
+ List<Integer> args = new ArrayList<Integer>(msg.getArgsCount() - 1);
+ for (int i = 1; i < msg.getArgsCount(); i++) {
+ args.add(Integer.valueOf(msg.getArgs(1).getIntValue(0)));
+ }
+ uniforms = args;
+ }
+
+ IStateTransform setValues = new PropertyChangeTransform(
+ new CurrentProgramPropertyAccessor(msg.getContextId(),
+ GLStateType.ACTIVE_UNIFORMS,
+ location,
+ GLStateType.UNIFORM_VALUE),
+ uniforms);
+
+ return Collections.singletonList(setValues);
+ }
+
private static List<IStateTransform> transformsForGlCreateShader(GLMessage msg) {
// GLuint glCreateShader(GLenum shaderType);
GLEnum shaderType = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/DetailsPage.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/DetailsPage.java
new file mode 100644
index 0000000..1fb64b4
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/DetailsPage.java
@@ -0,0 +1,203 @@
+/*
+ * 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.ide.eclipse.gltrace.views;
+
+import com.android.ide.eclipse.gltrace.editors.GLCallGroups.GLCallNode;
+import com.android.ide.eclipse.gltrace.editors.GLFunctionTraceViewer;
+import com.android.ide.eclipse.gltrace.model.GLCall;
+import com.android.ide.eclipse.gltrace.model.GLTrace;
+import com.android.ide.eclipse.gltrace.state.IGLProperty;
+
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StackLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.part.IPageSite;
+import org.eclipse.ui.part.Page;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class DetailsPage extends Page implements ISelectionListener {
+ private final GLTrace mTrace;
+
+ private IToolBarManager mToolBarManager;
+ private Composite mTopComposite;
+ private StackLayout mStackLayout;
+ private Composite mBlankComposite;
+
+ private List<IStateDetailsProvider> mStateDetailProviders = Arrays.asList(
+ new ShaderSourceDetailsProvider(),
+ new TextureImageDetailsProvider());
+
+ public DetailsPage(GLTrace trace) {
+ mTrace = trace;
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ mTopComposite = new Composite(parent, SWT.NONE);
+ mStackLayout = new StackLayout();
+ mTopComposite.setLayout(mStackLayout);
+ mTopComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ mBlankComposite = new Composite(mTopComposite, SWT.NONE);
+
+ mToolBarManager = getSite().getActionBars().getToolBarManager();
+
+ for (IStateDetailsProvider provider : mStateDetailProviders) {
+ provider.createControl(mTopComposite);
+
+ for (IContributionItem item: provider.getToolBarItems()) {
+ mToolBarManager.add(item);
+ }
+ }
+
+ setDetailsProvider(null);
+ }
+
+ private void setDetailsProvider(IStateDetailsProvider provider) {
+ for (IContributionItem item: mToolBarManager.getItems()) {
+ item.setVisible(false);
+ }
+
+ if (provider == null) {
+ setTopControl(mBlankComposite);
+ } else {
+ setTopControl(provider.getControl());
+
+ for (IContributionItem item: provider.getToolBarItems()) {
+ item.setVisible(true);
+ }
+ }
+
+ mToolBarManager.update(true);
+ }
+
+ private void setTopControl(Control c) {
+ mStackLayout.topControl = c;
+ mTopComposite.layout();
+ }
+
+ @Override
+ public Control getControl() {
+ return mTopComposite;
+ }
+
+ @Override
+ public void init(IPageSite pageSite) {
+ super.init(pageSite);
+ pageSite.getPage().addSelectionListener(this);
+ }
+
+ @Override
+ public void dispose() {
+ getSite().getPage().removeSelectionListener(this);
+
+ for (IStateDetailsProvider provider : mStateDetailProviders) {
+ provider.disposeControl();
+ }
+
+ super.dispose();
+ }
+
+ @Override
+ public void setFocus() {
+ }
+
+ @Override
+ public void selectionChanged(IWorkbenchPart part, ISelection selection) {
+ if (part instanceof GLFunctionTraceViewer) {
+ GLCall selectedCall = getSelectedCall((GLFunctionTraceViewer) part, selection);
+ if (selectedCall == null) {
+ return;
+ }
+
+ callSelected(selectedCall);
+ return;
+ } else if (part instanceof StateView) {
+ IGLProperty selectedProperty = getSelectedProperty((StateView) part, selection);
+ if (selectedProperty == null) {
+ return;
+ }
+
+ stateVariableSelected(selectedProperty);
+ return;
+ }
+
+ return;
+ }
+
+ private void stateVariableSelected(IGLProperty property) {
+ for (IStateDetailsProvider p : mStateDetailProviders) {
+ if (p.isApplicable(property)) {
+ p.updateControl(property);
+ setDetailsProvider(p);
+ return;
+ }
+ }
+
+ setDetailsProvider(null);
+ return;
+ }
+
+ private void callSelected(GLCall selectedCall) {
+ }
+
+ private GLCall getSelectedCall(GLFunctionTraceViewer part, ISelection selection) {
+ if (part.getTrace() != mTrace) {
+ return null;
+ }
+
+ if (!(selection instanceof TreeSelection)) {
+ return null;
+ }
+
+ Object data = ((TreeSelection) selection).getFirstElement();
+ if (data instanceof GLCallNode) {
+ return ((GLCallNode) data).getCall();
+ } else {
+ return null;
+ }
+ }
+
+ private IGLProperty getSelectedProperty(StateView view, ISelection selection) {
+ if (!(selection instanceof IStructuredSelection)) {
+ return null;
+ }
+
+ IStructuredSelection ssel = (IStructuredSelection) selection;
+ @SuppressWarnings("rawtypes")
+ List objects = ssel.toList();
+ if (objects.size() > 0) {
+ Object data = objects.get(0);
+ if (data instanceof IGLProperty) {
+ return (IGLProperty) data;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/DetailsView.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/DetailsView.java
new file mode 100644
index 0000000..015cf61
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/DetailsView.java
@@ -0,0 +1,51 @@
+/*
+ * 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.ide.eclipse.gltrace.views;
+
+import com.android.ide.eclipse.gltrace.editors.GLFunctionTraceViewer;
+
+import org.eclipse.ui.IWorkbenchPart;
+
+public class DetailsView extends GLPageBookView {
+ public static final String ID = "com.android.ide.eclipse.gltrace.views.Details"; //$NON-NLS-1$
+
+ public DetailsView() {
+ super(""); //$NON-NLS-1$
+ }
+
+ @Override
+ protected PageRec doCreatePage(IWorkbenchPart part) {
+ if (!(part instanceof GLFunctionTraceViewer)) {
+ return null;
+ }
+
+ GLFunctionTraceViewer viewer = (GLFunctionTraceViewer) part;
+ DetailsPage page = viewer.getDetailsPage();
+
+ initPage(page);
+ page.createControl(getPageBook());
+
+ return new PageRec(part, page);
+ }
+
+ @Override
+ protected void doDestroyPage(IWorkbenchPart part, PageRec pageRecord) {
+ DetailsPage page = (DetailsPage) pageRecord.page;
+ page.dispose();
+ pageRecord.dispose();
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FitToCanvasAction.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FitToCanvasAction.java
new file mode 100644
index 0000000..ccd2cbe
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FitToCanvasAction.java
@@ -0,0 +1,40 @@
+/*
+ * 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.ide.eclipse.gltrace.views;
+
+import com.android.ide.eclipse.gldebugger.Activator;
+import com.android.ide.eclipse.gltrace.widgets.ImageCanvas;
+
+import org.eclipse.jface.action.Action;
+
+public class FitToCanvasAction extends Action {
+ private ImageCanvas mImageCanvas;
+
+ public FitToCanvasAction(boolean fitByDefault, ImageCanvas canvas) {
+ super("Fit to Canvas", Activator.getImageDescriptor("/icons/zoomfit.png")); //$NON-NLS-2$
+ setToolTipText("Fit Image to Canvas");
+ mImageCanvas = canvas;
+
+ setChecked(fitByDefault);
+ mImageCanvas.setFitToCanvas(fitByDefault);
+ }
+
+ @Override
+ public void run() {
+ mImageCanvas.setFitToCanvas(isChecked());
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferView.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferView.java
index 308134b..50eafd2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferView.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferView.java
@@ -25,7 +25,7 @@ import org.eclipse.ui.IWorkbenchPart;
* currently displayed frame.
*/
public class FrameBufferView extends GLPageBookView {
- public static final String ID = "com.android.ide.eclipse.gltrace.views.FrameBufferView"; //$NON-NLS-1$
+ public static final String ID = "com.android.ide.eclipse.gltrace.views.FrameBuffer"; //$NON-NLS-1$
public FrameBufferView() {
super("Open a GL Trace file to view the framebuffer contents.");
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferViewPage.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferViewPage.java
index 67ec762..09b43b2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferViewPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferViewPage.java
@@ -16,14 +16,12 @@
package com.android.ide.eclipse.gltrace.views;
-import com.android.ide.eclipse.gldebugger.Activator;
import com.android.ide.eclipse.gltrace.editors.GLFunctionTraceViewer;
import com.android.ide.eclipse.gltrace.editors.GLCallGroups.GLCallNode;
import com.android.ide.eclipse.gltrace.model.GLCall;
import com.android.ide.eclipse.gltrace.model.GLTrace;
import com.android.ide.eclipse.gltrace.widgets.ImageCanvas;
-import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.TreeSelection;
@@ -31,7 +29,6 @@ import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.part.IPageSite;
@@ -50,9 +47,7 @@ public class FrameBufferViewPage extends Page implements ISelectionListener {
public void createControl(Composite parent) {
mImageCanvas = new ImageCanvas(parent);
- IActionBars actionBars = getSite().getActionBars();
- IToolBarManager toolbarManager = actionBars.getToolBarManager();
-
+ IToolBarManager toolbarManager = getSite().getActionBars().getToolBarManager();
toolbarManager.add(new FitToCanvasAction(true, mImageCanvas));
}
@@ -99,7 +94,6 @@ public class FrameBufferViewPage extends Page implements ISelectionListener {
}
if (selectedCall == null) {
- System.out.println("null selection");
return;
}
@@ -122,22 +116,4 @@ public class FrameBufferViewPage extends Page implements ISelectionListener {
setImage(mTrace.getImage(call));
}
}
-
- private static class FitToCanvasAction extends Action {
- private ImageCanvas mImageCanvas;
-
- public FitToCanvasAction(boolean fit, ImageCanvas canvas) {
- super("Fit to Canvas", Activator.getImageDescriptor("/icons/zoomfit.png")); //$NON-NLS-2$
- setToolTipText("Fit Image to Canvas");
- mImageCanvas = canvas;
-
- setChecked(fit);
- mImageCanvas.setFitToCanvas(fit);
- }
-
- @Override
- public void run() {
- mImageCanvas.setFitToCanvas(isChecked());
- }
- }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/IStateDetailsProvider.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/IStateDetailsProvider.java
new file mode 100644
index 0000000..7fe7c56
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/IStateDetailsProvider.java
@@ -0,0 +1,45 @@
+/*
+ * 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.ide.eclipse.gltrace.views;
+
+import com.android.ide.eclipse.gltrace.state.IGLProperty;
+
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+import java.util.List;
+
+public interface IStateDetailsProvider {
+ /** Is this provider applicable for given GL state property? */
+ boolean isApplicable(IGLProperty state);
+
+ /** Create the controls to display the details. */
+ void createControl(Composite parent);
+
+ /** Dispose off any created controls. */
+ void disposeControl();
+
+ /** Obtain the top level control used by this detail provider. */
+ Control getControl();
+
+ /** Update the detail view for given GL state property. */
+ void updateControl(IGLProperty state);
+
+ /** Obtain a list of tool bar items to be displayed when this provider is active. */
+ List<IContributionItem> getToolBarItems();
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/ImageViewPart.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/ImageViewPart.java
deleted file mode 100644
index 67b879a..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/ImageViewPart.java
+++ /dev/null
@@ -1,108 +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.ide.eclipse.gltrace.views;
-
-import com.android.ide.eclipse.gldebugger.Activator;
-import com.android.ide.eclipse.gltrace.widgets.ImageCanvas;
-
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.ISelectionListener;
-import org.eclipse.ui.ISelectionService;
-import org.eclipse.ui.IWorkbenchPart;
-import org.eclipse.ui.part.ViewPart;
-
-/**
- * The {@link ImageViewPart} is an abstract implementation of an Eclipse View
- * that holds an image canvas. This class registers itself to listen to the workbench
- * selection service. Subclasses must implement the
- * {@link #selectionChanged(IWorkbenchPart, ISelection)} method, and call {@link #setImage(Image)}
- * to update the image to be displayed.
- */
-public abstract class ImageViewPart extends ViewPart implements ISelectionListener {
- private boolean mFitToCanvasDefault = true;
- private ImageCanvas mImageCanvas;
-
- public ImageViewPart(boolean fitToCanvasByDefault) {
- mFitToCanvasDefault = fitToCanvasByDefault;
- }
-
- @Override
- public void createPartControl(Composite parent) {
- createImageCanvas(parent);
- createToolbar();
-
- ISelectionService selectionService = getSite().getWorkbenchWindow().getSelectionService();
- selectionService.addPostSelectionListener(this);
- }
-
- @Override
- public void dispose() {
- if (mImageCanvas != null) {
- mImageCanvas.dispose();
- mImageCanvas = null;
- }
-
- ISelectionService selectionService = getSite().getWorkbenchWindow().getSelectionService();
- selectionService.removePostSelectionListener(this);
- super.dispose();
- }
-
- @Override
- public void setFocus() {
- if (mImageCanvas != null) {
- mImageCanvas.setFocus();
- }
- }
-
- private class FitToCanvasAction extends Action {
- public FitToCanvasAction() {
- super("Fit to Canvas", Activator.getImageDescriptor("/icons/zoomfit.png")); //$NON-NLS-2$
- setToolTipText("Fit Image to Canvas");
- setChecked(mFitToCanvasDefault);
- }
-
- @Override
- public void run() {
- mImageCanvas.setFitToCanvas(isChecked());
- }
- }
-
- private void createImageCanvas(Composite parent) {
- mImageCanvas = new ImageCanvas(parent);
- mImageCanvas.setFitToCanvas(mFitToCanvasDefault);
- }
-
- private void createToolbar() {
- getViewSite().getActionBars().getToolBarManager().add(new FitToCanvasAction());
- }
-
- protected void setImage(final Image image) {
- Display.getDefault().asyncExec(new Runnable() {
- @Override
- public void run() {
- mImageCanvas.setImage(image);
- }
- });
- };
-
- @Override
- public abstract void selectionChanged(IWorkbenchPart part, ISelection selection);
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/ShaderSourceDetailsProvider.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/ShaderSourceDetailsProvider.java
new file mode 100644
index 0000000..bad3042
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/ShaderSourceDetailsProvider.java
@@ -0,0 +1,102 @@
+/*
+ * 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.ide.eclipse.gltrace.views;
+
+import com.android.ide.eclipse.gltrace.state.GLCompositeProperty;
+import com.android.ide.eclipse.gltrace.state.GLStateType;
+import com.android.ide.eclipse.gltrace.state.GLStringProperty;
+import com.android.ide.eclipse.gltrace.state.IGLProperty;
+
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+
+import java.util.Collections;
+import java.util.List;
+
+public class ShaderSourceDetailsProvider implements IStateDetailsProvider {
+ private Text mTextControl;
+
+ @Override
+ public boolean isApplicable(IGLProperty state) {
+ return getShaderSourceProperty(state) != null;
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ mTextControl = new Text(parent, SWT.BORDER | SWT.READ_ONLY | SWT.MULTI | SWT.WRAP);
+ mTextControl.setEditable(false);
+ }
+
+ @Override
+ public void disposeControl() {
+ }
+
+ @Override
+ public Control getControl() {
+ return mTextControl;
+ }
+
+ @Override
+ public void updateControl(IGLProperty state) {
+ IGLProperty shaderSrcProperty = getShaderSourceProperty(state);
+ if (shaderSrcProperty instanceof GLStringProperty) {
+ String shaderSrc = ((GLStringProperty) shaderSrcProperty).getStringValue();
+ mTextControl.setText(shaderSrc);
+ mTextControl.setEnabled(true);
+ } else {
+ mTextControl.setText(""); //$NON-NLS-1$
+ mTextControl.setEnabled(false);
+ }
+ }
+
+ /**
+ * Get the {@link GLStateType#SHADER_SOURCE} property given a node in
+ * the state hierarchy.
+ * @param state any node in the GL state hierarchy
+ * @return The {@link GLStateType#SHADER_SOURCE} property if a unique instance
+ * of it can be accessed from the given node, null otherwise.
+ * A unique instance can be accessed if the given node is
+ * either the requested node itself, or its parent or sibling.
+ */
+ private IGLProperty getShaderSourceProperty(IGLProperty state) {
+ if (state.getType() == GLStateType.SHADER_SOURCE) {
+ // given node is the requested node
+ return state;
+ }
+
+ if (state.getType() != GLStateType.PER_SHADER_STATE) {
+ // if it is not the parent, then it could be a sibling, in which case
+ // we go up a level to its parent
+ state = state.getParent();
+ }
+
+ if (state != null && state.getType() == GLStateType.PER_SHADER_STATE) {
+ // if it is the parent, we can access the required property
+ return ((GLCompositeProperty) state).getProperty(GLStateType.SHADER_SOURCE);
+ }
+
+ return null;
+ }
+
+ @Override
+ public List<IContributionItem> getToolBarItems() {
+ return Collections.emptyList();
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/TextureImageDetailsProvider.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/TextureImageDetailsProvider.java
new file mode 100644
index 0000000..2b0034b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/TextureImageDetailsProvider.java
@@ -0,0 +1,114 @@
+/*
+ * 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.ide.eclipse.gltrace.views;
+
+import com.android.ide.eclipse.gltrace.state.GLCompositeProperty;
+import com.android.ide.eclipse.gltrace.state.GLStateType;
+import com.android.ide.eclipse.gltrace.state.GLStringProperty;
+import com.android.ide.eclipse.gltrace.state.IGLProperty;
+import com.android.ide.eclipse.gltrace.widgets.ImageCanvas;
+
+import org.eclipse.jface.action.ActionContributionItem;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+
+import java.util.Collections;
+import java.util.List;
+
+public class TextureImageDetailsProvider implements IStateDetailsProvider {
+ private ImageCanvas mImageCanvas;
+ private FitToCanvasAction mFitToCanvasAction;
+ private List<IContributionItem> mToolBarItems;
+
+ @Override
+ public boolean isApplicable(IGLProperty state) {
+ return getTextureImageProperty(state) != null;
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ mImageCanvas = new ImageCanvas(parent);
+ mImageCanvas.setFitToCanvas(false);
+
+ mFitToCanvasAction = new FitToCanvasAction(false, mImageCanvas);
+ mToolBarItems = Collections.singletonList(
+ (IContributionItem) new ActionContributionItem(mFitToCanvasAction));
+ }
+
+ @Override
+ public void disposeControl() {
+ mImageCanvas.dispose();
+ mImageCanvas = null;
+ }
+
+ @Override
+ public Control getControl() {
+ return mImageCanvas;
+ }
+
+ @Override
+ public void updateControl(IGLProperty state) {
+ IGLProperty imageProperty = getTextureImageProperty(state);
+ if (imageProperty == null) {
+ return;
+ }
+
+ String texturePath = ((GLStringProperty) imageProperty).getStringValue();
+ if (texturePath != null) {
+ mImageCanvas.setImage(new Image(Display.getDefault(), texturePath));
+ mImageCanvas.setFitToCanvas(false);
+ return;
+ }
+ }
+
+ /**
+ * Get the {@link GLStateType#TEXTURE_IMAGE} property given a node in
+ * the state hierarchy.
+ * @param state any node in the GL state hierarchy
+ * @return The {@link GLStateType#TEXTURE_IMAGE} property if a unique instance
+ * of it can be accessed from the given node, null otherwise.
+ * A unique instance can be accessed if the given node is
+ * either the requested node itself, or its parent or sibling.
+ */
+ private IGLProperty getTextureImageProperty(IGLProperty state) {
+ if (state.getType() == GLStateType.TEXTURE_IMAGE) {
+ // given node is the requested node
+ return state;
+ }
+
+ if (state.getType() != GLStateType.PER_TEXTURE_STATE) {
+ // if it is not the parent, then it could be a sibling, in which case
+ // we go up a level to its parent
+ state = state.getParent();
+ }
+
+ if (state != null && state.getType() == GLStateType.PER_TEXTURE_STATE) {
+ // if it is the parent, we can access the required property
+ return ((GLCompositeProperty) state).getProperty(GLStateType.TEXTURE_IMAGE);
+ }
+
+ return null;
+ }
+
+ @Override
+ public List<IContributionItem> getToolBarItems() {
+ return mToolBarItems;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/TextureView.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/TextureView.java
deleted file mode 100644
index a355bae..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/TextureView.java
+++ /dev/null
@@ -1,104 +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.ide.eclipse.gltrace.views;
-
-import com.android.ide.eclipse.gltrace.state.GLCompositeProperty;
-import com.android.ide.eclipse.gltrace.state.GLStateType;
-import com.android.ide.eclipse.gltrace.state.GLStringProperty;
-import com.android.ide.eclipse.gltrace.state.IGLProperty;
-
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.IWorkbenchPart;
-
-import java.util.List;
-
-public class TextureView extends ImageViewPart {
- public TextureView() {
- super(false);
- }
-
- public static final String ID = "com.android.ide.eclipse.gltrace.views.Texture"; //$NON-NLS-1$
-
- @Override
- public void selectionChanged(IWorkbenchPart part, ISelection selection) {
- if (!(part instanceof StateView)) {
- return;
- }
-
- if (!(selection instanceof IStructuredSelection)) {
- return;
- }
-
- IStructuredSelection ssel = (IStructuredSelection) selection;
- @SuppressWarnings("rawtypes")
- List objects = ssel.toList();
- if (objects.size() > 0) {
- Object data = objects.get(0);
- if (!(data instanceof IGLProperty)) {
- return;
- }
-
- String textureImagePath = getTextureImage((IGLProperty) data);
- if (textureImagePath == null) {
- setImage(null);
- return;
- }
-
- setImage(getImage(textureImagePath));
- }
- }
-
- private Image getImage(String imagePath) {
- return new Image(Display.getDefault(), imagePath);
- }
-
- /**
- * Extract the TEXTURE_IMAGE property from the gl state hierarchy.
- * The TEXTURE_IMAGE property fits in the hierarchy like so:
- * ...
- * |---PER_TEXTURE_STATE
- * |--
- * |-- TEXTURE_IMAGE
- *
- * So we can extract the TEXTURE_IMAGE if the given property is either PER_TEXTURE_STATE,
- * or a child of PER_TEXTURE_STATE.
- */
- private String getTextureImage(IGLProperty property) {
- // if the selected property is the texture image, then we just return its value
- if (property.getType() == GLStateType.TEXTURE_IMAGE) {
- return ((GLStringProperty) property).getStringValue();
- }
-
- if (property.getType() != GLStateType.PER_TEXTURE_STATE) {
- // See if the property is a child of PER_TEXTURE_STATE
- IGLProperty parent = property.getParent();
- if (parent == null || parent.getType() != GLStateType.PER_TEXTURE_STATE) {
- return null;
- } else {
- property = parent;
- }
- }
-
- // property is now the parent property of TEXTURE_IMAGE
- GLCompositeProperty perTextureState = (GLCompositeProperty) property;
- IGLProperty imageProperty = perTextureState.getProperty(GLStateType.TEXTURE_IMAGE);
- return ((GLStringProperty) imageProperty).getStringValue();
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfigurationTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfigurationTest.java
index 09eb181..96014de 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfigurationTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfigurationTest.java
@@ -213,7 +213,7 @@ public class ProjectLintConfigurationTest extends AdtProjectTest {
}
@Override
- public void log(Throwable exception, String format, Object... args) {
+ public void log(Severity severity, Throwable exception, String format, Object... args) {
}
@Override
diff --git a/lint/cli/src/com/android/tools/lint/HtmlReporter.java b/lint/cli/src/com/android/tools/lint/HtmlReporter.java
index 45aac72..9cc9c6f 100644
--- a/lint/cli/src/com/android/tools/lint/HtmlReporter.java
+++ b/lint/cli/src/com/android/tools/lint/HtmlReporter.java
@@ -334,7 +334,7 @@ class HtmlReporter extends Reporter {
mWriter.write("</div>\n"); //$NON-NLS-1$
mWriter.write("Severity: ");
- if (severity == Severity.ERROR) {
+ if (severity == Severity.ERROR || severity == Severity.FATAL) {
mWriter.write("<span class=\"error\">"); //$NON-NLS-1$
} else if (severity == Severity.WARNING) {
mWriter.write("<span class=\"warning\">"); //$NON-NLS-1$
@@ -507,7 +507,7 @@ class HtmlReporter extends Reporter {
boolean isError = false;
for (Warning warning : warnings) {
- if (warning.severity == Severity.ERROR) {
+ if (warning.severity == Severity.ERROR || warning.severity == Severity.FATAL) {
isError = true;
break;
}
diff --git a/lint/cli/src/com/android/tools/lint/LintCliXmlParser.java b/lint/cli/src/com/android/tools/lint/LintCliXmlParser.java
index 9ee5ee8..e777e99 100644
--- a/lint/cli/src/com/android/tools/lint/LintCliXmlParser.java
+++ b/lint/cli/src/com/android/tools/lint/LintCliXmlParser.java
@@ -16,6 +16,7 @@
package com.android.tools.lint;
+import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.IDomParser;
import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.detector.api.Location;
@@ -161,6 +162,7 @@ public class LintCliXmlParser extends PositionXmlParser implements IDomParser {
private class LocationHandle implements Handle {
private File mFile;
private Node mNode;
+ private Object mClientData;
public LocationHandle(File file, Node node) {
mFile = file;
@@ -176,5 +178,16 @@ public class LintCliXmlParser extends PositionXmlParser implements IDomParser {
return null;
}
+
+ @Override
+ public void setClientData(@Nullable Object clientData) {
+ mClientData = clientData;
+ }
+
+ @Override
+ @Nullable
+ public Object getClientData() {
+ return mClientData;
+ }
}
}
diff --git a/lint/cli/src/com/android/tools/lint/LombokParser.java b/lint/cli/src/com/android/tools/lint/LombokParser.java
index e1a3c3a..15b1073 100644
--- a/lint/cli/src/com/android/tools/lint/LombokParser.java
+++ b/lint/cli/src/com/android/tools/lint/LombokParser.java
@@ -16,6 +16,7 @@
package com.android.tools.lint;
+import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.IJavaParser;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Location;
@@ -113,6 +114,7 @@ public class LombokParser implements IJavaParser {
private class LocationHandle implements Handle {
private File mFile;
private Node mNode;
+ private Object mClientData;
public LocationHandle(File file, Node node) {
mFile = file;
@@ -124,5 +126,17 @@ public class LombokParser implements IJavaParser {
Position pos = mNode.getPosition();
return Location.create(mFile, null /*contents*/, pos.getStart(), pos.getEnd());
}
+
+
+ @Override
+ public void setClientData(@Nullable Object clientData) {
+ mClientData = clientData;
+ }
+
+ @Override
+ @Nullable
+ public Object getClientData() {
+ return mClientData;
+ }
}
}
diff --git a/lint/cli/src/com/android/tools/lint/Main.java b/lint/cli/src/com/android/tools/lint/Main.java
index d319648..1484a1d 100644
--- a/lint/cli/src/com/android/tools/lint/Main.java
+++ b/lint/cli/src/com/android/tools/lint/Main.java
@@ -264,6 +264,9 @@ public class Main extends LintClient {
System.exit(ERRNO_INVALIDARGS);
}
File output = new File(args[++index]);
+ // Get an absolute path such that we can ask its parent directory for
+ // write permission etc.
+ output = output.getAbsoluteFile();
if (output.isDirectory() ||
(!output.exists() && output.getName().indexOf('.') == -1)) {
if (!output.exists()) {
@@ -293,7 +296,7 @@ public class Main extends LintClient {
System.exit(ERRNO_EXISTS);
}
}
- if (!output.getParentFile().canWrite()) {
+ if (output.getParentFile() != null && !output.getParentFile().canWrite()) {
System.err.println("Cannot write HTML output file " + output);
System.exit(ERRNO_EXISTS);
}
@@ -797,7 +800,7 @@ public class Main extends LintClient {
}
@Override
- public void log(Throwable exception, String format, Object... args) {
+ public void log(Severity severity, Throwable exception, String format, Object... args) {
System.out.flush();
if (!mQuiet) {
// Place the error message on a line of its own since we're printing '.' etc
@@ -853,8 +856,13 @@ public class Main extends LintClient {
if (severity == Severity.IGNORE) {
return;
}
- if (severity == Severity.ERROR){
+ if (severity == Severity.FATAL) {
mFatal = true;
+ // From here on, treat the fatal error as an error such that we don't display
+ // both "Fatal:" and "Error:" etc in the error output.
+ severity = Severity.ERROR;
+ }
+ if (severity == Severity.ERROR) {
mErrorCount++;
} else {
mWarningCount++;
diff --git a/lint/cli/src/com/android/tools/lint/MultiProjectHtmlReporter.java b/lint/cli/src/com/android/tools/lint/MultiProjectHtmlReporter.java
index 6dad258..db7b4fb 100644
--- a/lint/cli/src/com/android/tools/lint/MultiProjectHtmlReporter.java
+++ b/lint/cli/src/com/android/tools/lint/MultiProjectHtmlReporter.java
@@ -103,7 +103,7 @@ class MultiProjectHtmlReporter extends HtmlReporter {
int projectErrorCount = 0;
int projectWarningCount = 0;
for (Warning warning: issues) {
- if (warning.severity == Severity.ERROR) {
+ if (warning.severity == Severity.ERROR || warning.severity == Severity.FATAL) {
projectErrorCount++;
} else if (warning.severity == Severity.WARNING) {
projectWarningCount++;
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
index fb62538..48e86b5 100644
--- 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
@@ -23,6 +23,7 @@ import com.android.tools.lint.detector.api.Detector;
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 org.w3c.dom.Document;
@@ -92,14 +93,31 @@ public abstract class LintClient {
@Nullable Object data);
/**
- * Send an exception to the log
+ * 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);
@@ -159,6 +177,7 @@ public abstract class LintClient {
/**
* 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
*/
@@ -168,6 +187,17 @@ public abstract class LintClient {
}
/**
+ * 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 getEclipseClasspath(project, "lib"); //$NON-NLS-1$
+ }
+
+ /**
* Returns the {@link SdkInfo} to use for the given project.
*
* @param project the project to look up an {@link SdkInfo} for
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
index c02d735..d43d02e 100644
--- 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
@@ -26,6 +26,7 @@ import static com.android.tools.lint.detector.api.LintConstants.RES_FOLDER;
import static com.android.tools.lint.detector.api.LintConstants.SUPPRESS_ALL;
import static com.android.tools.lint.detector.api.LintConstants.SUPPRESS_LINT;
import static com.android.tools.lint.detector.api.LintConstants.TOOLS_URI;
+import static org.objectweb.asm.Opcodes.ASM4;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -51,6 +52,7 @@ import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
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;
@@ -731,13 +733,7 @@ public class LintDriver {
return;
}
- if (mScope.contains(Scope.CLASS_FILE)) {
- List<Detector> detectors = mScopeDetectors.get(Scope.CLASS_FILE);
- if (detectors != null && detectors.size() > 0) {
- List<File> binFolders = project.getJavaClassFolders();
- checkClasses(project, main, binFolders, detectors);
- }
- }
+ checkClasses(project, main);
if (mCanceled) {
return;
@@ -762,6 +758,32 @@ public class LintDriver {
}
}
+ /**
+ * 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);
+ }
+
@Nullable
private static List<Detector> union(
@Nullable List<Detector> list1,
@@ -786,17 +808,120 @@ public class LintDriver {
}
}
- private void checkClasses(
- @NonNull Project project,
- @Nullable Project main,
- @NonNull List<File> binFolders,
- @NonNull List<Detector> checks) {
- if (binFolders.size() == 0) {
+ /** Check the classes in this project (and if applicable, in any library projects */
+ private void checkClasses(Project project, Project main) {
+ if (!(mScope.contains(Scope.CLASS_FILE) || mScope.contains(Scope.JAVA_LIBRARIES))) {
+ 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);
+ } else {
+ libraryEntries = Collections.emptyList();
+ }
+
+ List<File> classFolders = project.getJavaClassFolders();
+ List<ClassEntry> classEntries;
+ if (classFolders.size() == 0) {
//mClient.log(null, "Warning: Class-file checks are enabled, but no " +
// "output folders found. Does the project need to be built first?");
+ classEntries = Collections.emptyList();
+ } else {
+ classEntries = new ArrayList<ClassEntry>(64);
+ findClasses(classEntries, classFolders);
+ }
+
+ if (getPhase() == 1) {
+ mSuperClassMap = getSuperMap(libraryEntries, classEntries);
}
- for (File classPathEntry : binFolders) {
+ // 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 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) {
+ for (ClassEntry entry : entries) {
+ ClassReader reader = new ClassReader(entry.bytes);
+ ClassNode classNode = new ClassNode();
+ reader.accept(classNode, 0 /* flags */);
+ if (isSuppressed(null, classNode)) {
+ // Class was annotated with suppress all -- no need to look any further
+ continue;
+ }
+
+ ClassContext context = new ClassContext(this, project, main,
+ entry.file, entry.jarFile, entry.binDir, entry.bytes,
+ classNode, scope == Scope.JAVA_LIBRARIES /*fromLibrary*/);
+ runClassDetectors(context, classDetectors);
+
+ if (mCanceled) {
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ 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) {
+ ClassReader reader = new ClassReader(entry.bytes);
+ int flags = ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;
+ reader.accept(visitor, flags);
+ }
+ }
+
+ /** 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;
try {
@@ -806,11 +931,12 @@ public class LintDriver {
while (entry != null) {
String name = entry.getName();
if (name.endsWith(DOT_CLASS)) {
- byte[] b = ByteStreams.toByteArray(zis);
try {
- File file = new File(entry.getName());
- checkClassFile(b, project, main, file, jarFile, jarFile,
- checks);
+ 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;
@@ -828,63 +954,52 @@ public class LintDriver {
}
continue;
- }
-
- File binDir = classPathEntry;
- List<File> classFiles = new ArrayList<File>();
- addClassFiles(binDir, classFiles);
+ } else if (classPathEntry.isDirectory()) {
+ File binDir = classPathEntry;
+ List<File> classFiles = new ArrayList<File>();
+ addClassFiles(binDir, classFiles);
- for (File file : classFiles) {
- try {
- byte[] bytes = Files.toByteArray(file);
- if (bytes != null) {
- checkClassFile(bytes, project, main, file, null /*jarFile*/, binDir,
- checks);
+ for (File file : classFiles) {
+ try {
+ byte[] bytes = Files.toByteArray(file);
+ if (bytes != null) {
+ entries.add(new ClassEntry(file, null /* jarFile*/, binDir, bytes));
+ }
+ } catch (IOException e) {
+ mClient.log(e, null);
+ continue;
}
- } catch (IOException e) {
- mClient.log(e, null);
- continue;
- }
- if (mCanceled) {
- return;
+ if (mCanceled) {
+ return;
+ }
}
+ } else {
+ mClient.log(null, "Ignoring class path entry %1$s", classPathEntry);
}
}
}
- private void checkClassFile(
- byte[] bytes,
- @NonNull Project project,
- @Nullable Project main,
- @NonNull File file,
- @NonNull File jarFile,
- @NonNull File binDir,
- @NonNull List<Detector> checks) {
- ClassReader reader = new ClassReader(bytes);
- ClassNode classNode = new ClassNode();
- reader.accept(classNode, 0 /*flags*/);
-
- if (isSuppressed(null, classNode)) {
- // Class was annotated with suppress all -- no need to look any further
- return;
- }
-
- ClassContext context = new ClassContext(this, project, main, file, jarFile,
- binDir, bytes, classNode);
- for (Detector detector : checks) {
- if (detector.appliesTo(context, file)) {
- fireEvent(EventType.SCANNING_FILE, context);
- detector.beforeCheckFile(context);
+ private void runClassDetectors(ClassContext context, @NonNull List<Detector> checks) {
+ try {
+ File file = context.file;
+ ClassNode classNode = context.getClassNode();
+ for (Detector detector : checks) {
+ if (detector.appliesTo(context, file)) {
+ fireEvent(EventType.SCANNING_FILE, context);
+ detector.beforeCheckFile(context);
- Detector.ClassScanner scanner = (Detector.ClassScanner) detector;
- scanner.checkClass(context, classNode);
- detector.afterCheckFile(context);
- }
+ Detector.ClassScanner scanner = (Detector.ClassScanner) detector;
+ scanner.checkClass(context, classNode);
+ detector.afterCheckFile(context);
+ }
- if (mCanceled) {
- return;
+ if (mCanceled) {
+ return;
+ }
}
+ } catch (Exception e) {
+ mClient.log(e, null);
}
}
@@ -1161,8 +1276,8 @@ public class LintDriver {
@Override
- public void log(@Nullable Throwable exception, @Nullable String format,
- @Nullable Object... args) {
+ public void log(@NonNull Severity severity, @Nullable Throwable exception,
+ @Nullable String format, @Nullable Object... args) {
mDelegate.log(exception, format, args);
}
@@ -1185,6 +1300,11 @@ public class LintDriver {
}
@Override
+ public List<File> getJavaLibraries(Project project) {
+ return mDelegate.getJavaLibraries(project);
+ }
+
+ @Override
@Nullable
public IDomParser getDomParser() {
return mDelegate.getDomParser();
@@ -1502,4 +1622,20 @@ public class LintDriver {
return false;
}
+
+ /** A pending class to be analyzed by {@link #checkClasses} */
+ private static class 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;
+ }
+ }
}
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
index ec6bd6a..c76d44d 100644
--- 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
@@ -181,7 +181,11 @@ class XmlVisitor {
NamedNodeMap attributes = element.getAttributes();
for (int i = 0, n = attributes.getLength(); i < n; i++) {
Attr attribute = (Attr) attributes.item(i);
- List<Detector.XmlScanner> list = mAttributeToCheck.get(attribute.getLocalName());
+ 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);
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
index a318fc7..75aaa0b 100644
--- 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
@@ -137,31 +137,31 @@ public final class Category implements Comparable<Category> {
}
/** Issues related to correctness */
- public static final Category CORRECTNESS = Category.create("Correctness", 10);
+ public static final Category CORRECTNESS = Category.create("Correctness", 100);
/** Issues related to security */
- public static final Category SECURITY = Category.create("Security", 9);
+ public static final Category SECURITY = Category.create("Security", 90);
/** Issues related to performance */
- public static final Category PERFORMANCE = Category.create("Performance", 8);
+ public static final Category PERFORMANCE = Category.create("Performance", 80);
/** Issues related to usability */
- public static final Category USABILITY = Category.create("Usability", 7);
+ public static final Category USABILITY = Category.create("Usability", 70);
/** Issues related to accessibility */
- public static final Category A11Y = Category.create("Accessibility", 6);
+ public static final Category A11Y = Category.create("Accessibility", 60);
/** Issues related to internationalization */
- public static final Category I18N = Category.create("Internationalization", 5);
+ 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, 7);
+ 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, 8);
+ 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, 10);
+ 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
index 8a4fb2d..a2899d8 100644
--- 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
@@ -52,21 +52,27 @@ public class ClassContext extends Context {
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 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 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
*/
public ClassContext(
@NonNull LintDriver driver,
@@ -76,12 +82,14 @@ public class ClassContext extends Context {
@Nullable File jarFile,
@NonNull File binDir,
@NonNull byte[] bytes,
- @NonNull ClassNode classNode) {
+ @NonNull ClassNode classNode,
+ boolean fromLibrary) {
super(driver, project, main, file);
mJarFile = jarFile;
mBinDir = binDir;
mBytes = bytes;
mClassNode = classNode;
+ mFromLibrary = fromLibrary;
}
/**
@@ -116,6 +124,15 @@ public class ClassContext extends Context {
}
/**
+ * 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
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java
index e1e3c54..9d9cf6d 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java
@@ -41,7 +41,7 @@ public class LintConstants {
public static final String TAG_SERVICE = "service"; //$NON-NLS-1$
public static final String TAG_USES_PERMISSION = "uses-permission";//$NON-NLS-1$
public static final String TAG_APPLICATION = "application"; //$NON-NLS-1$
- public static final String TAG_INTENT_FILTER = " intent-filter"; //$NON-NLS-1$
+ public static final String TAG_INTENT_FILTER = "intent-filter"; //$NON-NLS-1$
public static final String TAG_USES_SDK = "uses-sdk"; //$NON-NLS-1$
public static final String TAG_ACTIVITY = "activity"; //$NON-NLS-1$
public static final String TAG_GRANT_PERMISSION = "grant-uri-permission"; //$NON-NLS-1$
@@ -250,6 +250,7 @@ public class LintConstants {
public static final String ANDROID_STRING_RESOURCE_PREFIX = "@android:string/"; //$NON-NLS-1$
// Packages
+ public static final String ANDROID_PKG_PREFIX = "android."; //$NON-NLS-1$
public static final String WIDGET_PKG_PREFIX = "android.widget."; //$NON-NLS-1$
public static final String VIEW_PKG_PREFIX = "android.view."; //$NON-NLS-1$
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
index d18ab71..9d61047 100644
--- 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
@@ -35,6 +35,7 @@ public class Location {
private final Position mEnd;
private String mMessage;
private Location mSecondary;
+ private Object mClientData;
/**
* (Private constructor, use one of the factory methods
@@ -143,6 +144,28 @@ public class Location {
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() {
@@ -352,6 +375,25 @@ public class Location {
*/
@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 */
@@ -360,6 +402,7 @@ public class Location {
private String mContents;
private int mStartOffset;
private int mEndOffset;
+ private Object mClientData;
/**
* Constructs a new {@link DefaultLocationHandle}
@@ -380,5 +423,16 @@ public class Location {
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;
+ }
}
}
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
index e2795ed..910ee2f 100644
--- 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
@@ -82,6 +82,7 @@ public class Project {
private List<File> mFiles;
private List<File> mJavaSourceFolders;
private List<File> mJavaClassFolders;
+ private List<File> mJavaLibraries;
private List<Project> mDirectLibraries;
private List<Project> mAllLibraries;
@@ -268,6 +269,28 @@ public class Project {
}
/**
+ * 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
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
index 9e5ed81..4b9da60 100644
--- 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
@@ -77,7 +77,8 @@ public enum Scope {
PROGUARD_FILE,
/**
- * The analysis considers classes in the libraries for this project.
+ * The analysis considers classes in the libraries for this project. These
+ * will be analyzed before the classes themselves.
*/
JAVA_LIBRARIES;
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
index e2add3a..d0bbd7b 100644
--- 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
@@ -28,8 +28,13 @@ import com.google.common.annotations.Beta;
@Beta
public enum Severity {
/**
- * Errors: Use sparingly because a warning marked as an error will be
- * considered fatal and will abort Export APK etc in ADT
+ * Fatal: Use sparingly because a warning marked as fatal will be
+ * considered critical and will abort Export APK etc in ADT
+ */
+ FATAL("Fatal"),
+
+ /**
+ * Errors: The issue is known to be a real error that must be addressed.
*/
ERROR("Error"),
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java
index 7053038..9e744e4 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java
@@ -76,7 +76,7 @@ public class ApiDetector extends LayoutDetector implements Detector.ClassScanner
6,
Severity.ERROR,
ApiDetector.class,
- EnumSet.of(Scope.CLASS_FILE, Scope.JAVA_LIBRARIES, Scope.RESOURCE_FILE));
+ EnumSet.of(Scope.CLASS_FILE, Scope.RESOURCE_FILE));
private ApiLookup mApiDatabase;
private int mMinApi = -1;
@@ -151,7 +151,7 @@ public class ApiDetector extends LayoutDetector implements Detector.ClassScanner
// ---- Implements ClassScanner ----
- @SuppressWarnings({"rawtypes","unchecked"})
+ @SuppressWarnings("rawtypes")
@Override
public void checkClass(final ClassContext context, ClassNode classNode) {
if (mApiDatabase == null) {
@@ -165,7 +165,7 @@ public class ApiDetector extends LayoutDetector implements Detector.ClassScanner
// Workaround for the fact that beforeCheckProject is too early
int classMinSdk = getLocalMinSdk(classNode.invisibleAnnotations);
if (classMinSdk == -1) {
- classMinSdk = getMinSdk(context);;
+ classMinSdk = getMinSdk(context);
}
List methodList = classNode.methods;
@@ -232,24 +232,31 @@ public class ApiDetector extends LayoutDetector implements Detector.ClassScanner
String owner = node.owner;
String desc = node.desc;
- // Handle inherited methods. This does not work if there are multiple
- // local classes in the inheritance chain. To solve this in general we'll
- // need to make two passes: the first one to gather inheritance information
- // for local classes, and then perform the check here resolving up to
- // the API classes.
+ // No need to check methods in this local class; we know they
+ // won't be an API match
if (node.getOpcode() == Opcodes.INVOKEVIRTUAL
&& owner.equals(classNode.name)) {
owner = classNode.superName;
}
- int api = mApiDatabase.getCallVersion(owner, name, desc);
- if (api > minSdk) {
- String fqcn = owner.replace('/', '.') + '#' + name;
- String message = String.format(
- "Call requires API level %1$d (current min is %2$d): %3$s",
- api, minSdk, fqcn);
- report(context, message, node, method, name, null);
- }
+ do {
+ int api = mApiDatabase.getCallVersion(owner, name, desc);
+ if (api > minSdk) {
+ String fqcn = owner.replace('/', '.') + '#' + name;
+ String message = String.format(
+ "Call requires API level %1$d (current min is %2$d): %3$s",
+ api, minSdk, fqcn);
+ report(context, message, node, method, name, null);
+ }
+
+ // For virtual dispatch, walk up the inheritance chain checking
+ // each inherited method
+ if (node.getOpcode() == Opcodes.INVOKEVIRTUAL) {
+ owner = context.getDriver().getSuperClass(owner);
+ } else {
+ owner = null;
+ }
+ } while (owner != null);
} else if (type == AbstractInsnNode.FIELD_INSN) {
FieldInsnNode node = (FieldInsnNode) instruction;
String name = node.name;
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiLookup.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiLookup.java
index 02e31ab..0c3aaee 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiLookup.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiLookup.java
@@ -546,6 +546,22 @@ public class ApiLookup {
}
/**
+ * Quick determination whether a given class name is possibly interesting; this
+ * is a quick package prefix check to determine whether we need to consider
+ * the class at all. This let's us do less actual searching for the vast majority
+ * of APIs (in libraries, application code etc) that have nothing to do with the
+ * APIs in our packages.
+ * @param name the class name in VM format (e.g. using / instead of .)
+ * @return true if the owner is <b>possibly</b> relevant
+ */
+ public boolean isRelevantClass(String name) {
+ // TODO: Add quick switching here. This is tied to the database file so if
+ // we end up with unexpected prefixes there, this could break. For that reason,
+ // for now we consider everything relevant.
+ return true;
+ }
+
+ /**
* Returns the API version required by the given class reference,
* or -1 if this is not a known API class. Note that it may return -1
* for classes introduced in version 1; internally the database only
@@ -558,6 +574,10 @@ public class ApiLookup {
* it's unknown <b>or version 1</b>.
*/
public int getClassVersion(@NonNull String className) {
+ if (!isRelevantClass(className)) {
+ return -1;
+ }
+
if (mData != null) {
int classNumber = findClass(className);
if (classNumber != -1) {
@@ -601,6 +621,10 @@ public class ApiLookup {
@NonNull String owner,
@NonNull String name,
@NonNull String desc) {
+ if (!isRelevantClass(owner)) {
+ return -1;
+ }
+
if (mData != null) {
int classNumber = findClass(owner);
if (classNumber != -1) {
@@ -637,6 +661,10 @@ public class ApiLookup {
public int getFieldVersion(
@NonNull String owner,
@NonNull String name) {
+ if (!isRelevantClass(owner)) {
+ return -1;
+ }
+
if (mData != null) {
int classNumber = findClass(owner);
if (classNumber != -1) {
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
index ed5586d..e3deb3f 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
@@ -53,7 +53,8 @@ public class BuiltinIssueRegistry extends IssueRegistry {
private static final List<Issue> sIssues;
static {
- List<Issue> issues = new ArrayList<Issue>(60);
+ final int initialCapacity = 77;
+ List<Issue> issues = new ArrayList<Issue>(initialCapacity);
issues.add(AccessibilityDetector.ISSUE);
issues.add(MathDetector.ISSUE);
@@ -97,6 +98,7 @@ public class BuiltinIssueRegistry extends IssueRegistry {
issues.add(HardcodedDebugModeDetector.ISSUE);
issues.add(ManifestOrderDetector.ORDER);
issues.add(ManifestOrderDetector.USES_SDK);
+ issues.add(ManifestOrderDetector.MULTIPLE_USES_SDK);
issues.add(SecurityDetector.EXPORTED_SERVICE);
issues.add(SecurityDetector.OPEN_PROVIDER);
issues.add(SecurityDetector.WORLD_WRITEABLE);
@@ -125,7 +127,14 @@ public class BuiltinIssueRegistry extends IssueRegistry {
issues.add(ViewTypeDetector.ISSUE);
issues.add(WrongImportDetector.ISSUE);
issues.add(ViewConstructorDetector.ISSUE);
+ issues.add(NamespaceDetector.CUSTOMVIEW);
+ issues.add(NamespaceDetector.UNUSED);
+ issues.add(NamespaceDetector.TYPO);
issues.add(AlwaysShowActionDetector.ISSUE);
+ issues.add(JavaPerformanceDetector.PAINT_ALLOC);
+ issues.add(JavaPerformanceDetector.USE_SPARSEARRAY);
+
+ assert initialCapacity >= issues.size() : issues.size();
addCustomIssues(issues);
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/DetectMissingPrefix.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DetectMissingPrefix.java
index d0cf2d4..6ea7899 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/DetectMissingPrefix.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DetectMissingPrefix.java
@@ -16,6 +16,7 @@
package com.android.tools.lint.checks;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_PKG_PREFIX;
import static com.android.tools.lint.detector.api.LintConstants.ATTR_CLASS;
import static com.android.tools.lint.detector.api.LintConstants.ATTR_LAYOUT;
import static com.android.tools.lint.detector.api.LintConstants.ATTR_STYLE;
@@ -114,6 +115,6 @@ public class DetectMissingPrefix extends LayoutDetector {
return true;
}
- return tag.indexOf('.') != -1 && !tag.startsWith("android."); //$NON-NLS-1$
+ return tag.indexOf('.') != -1 && !tag.startsWith(ANDROID_PKG_PREFIX);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/DuplicateIdDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DuplicateIdDetector.java
index 9c8ddea..7253009 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/DuplicateIdDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DuplicateIdDetector.java
@@ -393,7 +393,7 @@ public class DuplicateIdDetector extends LayoutDetector {
@Override
public void visitAttribute(XmlContext context, Attr attribute) {
- assert attribute.getLocalName().equals(ATTR_ID);
+ assert attribute.getName().equals(ATTR_ID) || attribute.getLocalName().equals(ATTR_ID);
String id = attribute.getValue();
if (mIds.contains(id)) {
Location location = context.getLocation(attribute);
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/FieldGetterDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/FieldGetterDetector.java
index 8d401e1..8bbc749 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/FieldGetterDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/FieldGetterDetector.java
@@ -68,7 +68,7 @@ public class FieldGetterDetector extends Detector implements Detector.ClassScann
setEnabledByDefault(false).setMoreInfo(
"http://developer.android.com/guide/practices/design/performance.html#internal_get_set"); //$NON-NLS-1$
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link FieldGetterDetector} check */
public FieldGetterDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java
index a2788ab..762a4b5 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java
@@ -51,11 +51,11 @@ public class GridLayoutDetector extends LayoutDetector {
"of a GridLayout's rowCount or columnCount is usually an unintentional error.",
Category.CORRECTNESS,
4,
- Severity.ERROR,
+ Severity.FATAL,
GridLayoutDetector.class,
Scope.RESOURCE_FILE_SCOPE);
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link GridLayoutDetector} check */
public GridLayoutDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/IconDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/IconDetector.java
index 00df53c..42ed1b8 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/IconDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/IconDetector.java
@@ -253,7 +253,7 @@ public class IconDetector extends Detector implements Detector.XmlScanner {
private String mApplicationIcon;
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link IconDetector} check */
public IconDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java
new file mode 100644
index 0000000..86242e0
--- /dev/null
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java
@@ -0,0 +1,454 @@
+/*
+ * 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.checks;
+
+import com.android.tools.lint.detector.api.Category;
+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.Scope;
+import com.android.tools.lint.detector.api.Severity;
+import com.android.tools.lint.detector.api.Speed;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import lombok.ast.AstVisitor;
+import lombok.ast.BinaryExpression;
+import lombok.ast.BinaryOperator;
+import lombok.ast.ConstructorInvocation;
+import lombok.ast.Expression;
+import lombok.ast.ForwardingAstVisitor;
+import lombok.ast.If;
+import lombok.ast.MethodDeclaration;
+import lombok.ast.MethodInvocation;
+import lombok.ast.Node;
+import lombok.ast.StrictListAccessor;
+import lombok.ast.Throw;
+import lombok.ast.TypeReference;
+import lombok.ast.TypeReferencePart;
+import lombok.ast.UnaryExpression;
+import lombok.ast.VariableDefinition;
+import lombok.ast.VariableReference;
+
+/**
+ * Looks for performance issues in Java files, such as memory allocations during
+ * drawing operations and using HashMap instead of SparseArray.
+ */
+public class JavaPerformanceDetector extends Detector implements Detector.JavaScanner {
+ /** Allocating objects during a paint method */
+ public static final Issue PAINT_ALLOC = Issue.create(
+ "DrawAllocation", //$NON-NLS-1$
+ "Looks for memory allocations within drawing code",
+
+ "You should avoid allocating objects during a drawing or layout operation. These " +
+ "are called frequently, so a smooth UI can be interrupted by garbage collection " +
+ "pauses caused by the object allocations.\n" +
+ "\n" +
+ "The way this is generally handled is to allocate the needed objects up front " +
+ "and to reuse them for each drawing operation.\n" +
+ "\n" +
+ "Some methods allocate memory on your behalf (such as Bitmap.create), and these " +
+ "should be handled in the same way.",
+
+ Category.PERFORMANCE,
+ 9,
+ Severity.WARNING,
+ JavaPerformanceDetector.class,
+ Scope.JAVA_FILE_SCOPE);
+
+ /** Using HashMaps where SparseArray would be better */
+ public static final Issue USE_SPARSEARRAY = Issue.create(
+ "UseSparseArrays", //$NON-NLS-1$
+ "Looks for opportunities to replace HashMaps with the more efficient SparseArray",
+
+ "For maps where the keys are of type integer, it's typically more efficient to " +
+ "use the Android SparseArray API. This check identifies scenarios where you might " +
+ "want to consider using SparseArray instead of HashMap for better performance.\n" +
+ "\n" +
+ "This is *particularly* useful when the value types are primitives like ints, " +
+ "where you can use SparseIntArray and avoid auto-boxing the values from int to " +
+ "Integer.\n" +
+ "\n" +
+ "If you need to construct a HashMap because you need to call an API outside of " +
+ "your control which requires a Map, you can suppress this warning using for " +
+ "example the @SuppressLint annotation.",
+
+ Category.PERFORMANCE,
+ 4,
+ Severity.WARNING,
+ JavaPerformanceDetector.class,
+ Scope.JAVA_FILE_SCOPE);
+
+ private static final String INT = "int"; //$NON-NLS-1$
+ private static final String INTEGER = "Integer"; //$NON-NLS-1$
+ private static final String BOOL = "boolean"; //$NON-NLS-1$
+ private static final String BOOLEAN = "Boolean"; //$NON-NLS-1$
+ private static final String LONG = "Long"; //$NON-NLS-1$
+ private static final String HASH_MAP = "HashMap"; //$NON-NLS-1$
+ private static final String CANVAS = "Canvas"; //$NON-NLS-1$
+ private static final String ON_DRAW = "onDraw"; //$NON-NLS-1$
+ private static final String ON_LAYOUT = "onLayout"; //$NON-NLS-1$
+ private static final String ON_MEASURE = "onMeasure"; //$NON-NLS-1$
+
+ /** Constructs a new {@link JavaPerformanceDetector} check */
+ public JavaPerformanceDetector() {
+ }
+
+ @Override
+ public boolean appliesTo(Context context, File file) {
+ return true;
+ }
+
+ @Override
+ public Speed getSpeed() {
+ return Speed.FAST;
+ }
+
+ // ---- Implements JavaScanner ----
+
+ @Override
+ public List<Class<? extends Node>> getApplicableNodeTypes() {
+ List<Class<? extends Node>> types = new ArrayList<Class<? extends Node>>(3);
+ types.add(ConstructorInvocation.class);
+ types.add(MethodDeclaration.class);
+ types.add(MethodInvocation.class);
+ return types;
+ }
+
+ @Override
+ public AstVisitor createJavaVisitor(JavaContext context) {
+ return new PerformanceVisitor(context);
+ }
+
+ private static class PerformanceVisitor extends ForwardingAstVisitor {
+ private final JavaContext mContext;
+ /** Whether allocations should be "flagged" in the current method */
+ private boolean mFlagAllocations;
+ private boolean mCheckMaps;
+ private boolean mCheckAllocations;
+
+
+ public PerformanceVisitor(JavaContext context) {
+ mContext = context;
+
+ mCheckAllocations = context.isEnabled(PAINT_ALLOC);
+ mCheckMaps = context.isEnabled(USE_SPARSEARRAY);
+ assert mCheckAllocations || mCheckMaps; // enforced by infrastructure
+ }
+
+ @Override
+ public boolean visitMethodDeclaration(MethodDeclaration node) {
+ mFlagAllocations = isBlockedAllocationMethod(node);
+
+ return super.visitMethodDeclaration(node);
+ }
+
+ @Override
+ public boolean visitConstructorInvocation(ConstructorInvocation node) {
+ if (mCheckMaps) {
+ TypeReference reference = node.astTypeReference();
+ String typeName = reference.astParts().last().astIdentifier().astValue();
+ // TODO: Should we handle factory method constructions of HashMaps as well,
+ // e.g. via Guava? This is a bit trickier since we need to infer the type
+ // arguments from the calling context.
+ if (typeName.equals(HASH_MAP)) {
+ checkSparseArray(node, reference);
+ }
+ }
+
+ if (mFlagAllocations && !(node.getParent() instanceof Throw) && mCheckAllocations) {
+ // Make sure we're still inside the method declaration that marked
+ // mInDraw as true, in case we've left it and we're in a static
+ // block or something:
+ Node method = node;
+ while (method != null) {
+ if (method instanceof MethodDeclaration) {
+ break;
+ }
+ method = method.getParent();
+ }
+ if (method != null && isBlockedAllocationMethod(((MethodDeclaration) method))
+ && !isLazilyInitialized(node)) {
+ reportAllocation(node);
+ }
+ }
+
+ return super.visitConstructorInvocation(node);
+ }
+
+ private void reportAllocation(Node node) {
+ mContext.report(PAINT_ALLOC, node, mContext.getLocation(node),
+ "Avoid object allocations during draw/layout operations (preallocate and " +
+ "reuse insteaD)", null);
+ }
+
+ @Override
+ public boolean visitMethodInvocation(MethodInvocation node) {
+ if (mFlagAllocations) {
+ // Look for forbidden methods
+ String methodName = node.astName().astValue();
+ if (methodName.equals("createBitmap") //$NON-NLS-1$
+ || methodName.equals("createScaledBitmap")) { //$NON-NLS-1$
+ String operand = node.astOperand().toString();
+ if (operand.equals("Bitmap") //$NON-NLS-1$
+ || operand.equals("android.graphics.Bitmap")) { //$NON-NLS-1$
+ if (!isLazilyInitialized(node)) {
+ reportAllocation(node);
+ }
+ }
+ } else if (methodName.startsWith("decode")) { //$NON-NLS-1$
+ // decodeFile, decodeByteArray, ...
+ String operand = node.astOperand().toString();
+ if (operand.equals("BitmapFactory") //$NON-NLS-1$
+ || operand.equals("android.graphics.BitmapFactory")) { //$NON-NLS-1$
+ if (!isLazilyInitialized(node)) {
+ reportAllocation(node);
+ }
+ }
+ } else if (methodName.equals("getClipBounds")) { //$NON-NLS-1$
+ if (node.astArguments().isEmpty()) {
+ mContext.report(PAINT_ALLOC, node, mContext.getLocation(node),
+ "Avoid object allocations during draw operations: Use " +
+ "Canvas.getClipBounds(Rect) instead of Canvas.getClipBounds() " +
+ "which allocates a temporary Rect", null);
+ }
+ }
+ }
+
+ return super.visitMethodInvocation(node);
+ }
+
+ /**
+ * Check whether the given invocation is done as a lazy initialization,
+ * e.g. {@code if (foo == null) foo = new Foo();}.
+ * <p>
+ * This tries to also handle the scenario where the check is on some
+ * <b>other</b> variable - e.g.
+ * <pre>
+ * if (foo == null) {
+ * foo == init1();
+ * bar = new Bar();
+ * }
+ * </pre>
+ * or
+ * <pre>
+ * if (!initialized) {
+ * initialized = true;
+ * bar = new Bar();
+ * }
+ * </pre>
+ */
+ private boolean isLazilyInitialized(Node node) {
+ Node curr = node.getParent();
+ while (curr != null) {
+ if (curr instanceof MethodDeclaration) {
+ return false;
+ } else if (curr instanceof If) {
+ If ifNode = (If) curr;
+ // See if the if block represents a lazy initialization:
+ // compute all variable names seen in the condition
+ // (e.g. for "if (foo == null || bar != foo)" the result is "foo,bar"),
+ // and then compute all variables assigned to in the if body,
+ // and if there is an overlap, we'll consider the whole if block
+ // guarded (so lazily initialized and an allocation we won't complain
+ // about.)
+ List<String> assignments = new ArrayList<String>();
+ AssignmentTracker visitor = new AssignmentTracker(assignments);
+ ifNode.astStatement().accept(visitor);
+ if (assignments.size() > 0) {
+ List<String> references = new ArrayList<String>();
+ addReferencedVariables(references, ifNode.astCondition());
+ if (references.size() > 0) {
+ SetView<String> intersection = Sets.intersection(
+ new HashSet<String>(assignments),
+ new HashSet<String>(references));
+ return intersection.size() > 0;
+ }
+ }
+ return false;
+
+ }
+ curr = curr.getParent();
+ }
+
+ return false;
+ }
+
+ /** Adds any variables referenced in the given expression into the given list */
+ private static void addReferencedVariables(Collection<String> variables,
+ Expression expression) {
+ if (expression instanceof BinaryExpression) {
+ BinaryExpression binary = (BinaryExpression) expression;
+ addReferencedVariables(variables, binary.astLeft());
+ addReferencedVariables(variables, binary.astRight());
+ } else if (expression instanceof UnaryExpression) {
+ UnaryExpression unary = (UnaryExpression) expression;
+ addReferencedVariables(variables, unary.astOperand());
+ } else if (expression instanceof VariableReference) {
+ VariableReference reference = (VariableReference) expression;
+ variables.add(reference.astIdentifier().astValue());
+ }
+ }
+
+ /**
+ * Returns whether the given method declaration represents a method
+ * where allocating objects is not allowed for performance reasons
+ */
+ private boolean isBlockedAllocationMethod(MethodDeclaration node) {
+ return isOnDrawMethod(node) || isOnMeasureMethod(node) || isOnLayoutMethod(node);
+ }
+
+ /**
+ * Returns true if this method looks like it's overriding android.view.View's
+ * {@code protected void onDraw(Canvas canvas)}
+ */
+ private static boolean isOnDrawMethod(MethodDeclaration node) {
+ if (ON_DRAW.equals(node.astMethodName().astValue())) {
+ StrictListAccessor<VariableDefinition, MethodDeclaration> parameters =
+ node.astParameters();
+ if (parameters != null && parameters.size() == 1) {
+ VariableDefinition arg0 = parameters.first();
+ TypeReferencePart type = arg0.astTypeReference().astParts().last();
+ String typeName = type.getTypeName();
+ if (typeName.equals(CANVAS)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if this method looks like it's overriding
+ * android.view.View's
+ * {@code protected void onLayout(boolean changed, int left, int top,
+ * int right, int bottom)}
+ */
+ private static boolean isOnLayoutMethod(MethodDeclaration node) {
+ if (ON_LAYOUT.equals(node.astMethodName().astValue())) {
+ StrictListAccessor<VariableDefinition, MethodDeclaration> parameters =
+ node.astParameters();
+ if (parameters != null && parameters.size() == 5) {
+ Iterator<VariableDefinition> iterator = parameters.iterator();
+ if (!iterator.hasNext()) {
+ return false;
+ }
+
+ // Ensure that the argument list matches boolean, int, int, int, int
+ TypeReferencePart type = iterator.next().astTypeReference().astParts().last();
+ if (!type.getTypeName().equals(BOOL) || !iterator.hasNext()) {
+ return false;
+ }
+ for (int i = 0; i < 4; i++) {
+ type = iterator.next().astTypeReference().astParts().last();
+ if (!type.getTypeName().equals(INT)) {
+ return false;
+ }
+ if (!iterator.hasNext()) {
+ return i == 3;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if this method looks like it's overriding android.view.View's
+ * {@code protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)}
+ */
+ private static boolean isOnMeasureMethod(MethodDeclaration node) {
+ if (ON_MEASURE.equals(node.astMethodName().astValue())) {
+ StrictListAccessor<VariableDefinition, MethodDeclaration> parameters =
+ node.astParameters();
+ if (parameters != null && parameters.size() == 2) {
+ VariableDefinition arg0 = parameters.first();
+ VariableDefinition arg1 = parameters.last();
+ TypeReferencePart type1 = arg0.astTypeReference().astParts().last();
+ TypeReferencePart type2 = arg1.astTypeReference().astParts().last();
+ return INT.equals(type1.getTypeName()) && INT.equals(type2.getTypeName());
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the given constructor call and type reference refers
+ * to a HashMap constructor call that is eligible for replacement by a
+ * SparseArray call instead
+ */
+ private void checkSparseArray(ConstructorInvocation node, TypeReference reference) {
+ // reference.hasTypeArguments returns false where it should not
+ StrictListAccessor<TypeReference, TypeReference> types = reference.getTypeArguments();
+ if (types != null && types.size() == 2) {
+ TypeReference first = types.first();
+ if (first.getTypeName().equals(INTEGER)) {
+ String valueType = types.last().getTypeName();
+ if (valueType.equals(INTEGER)) {
+ mContext.report(USE_SPARSEARRAY, node, mContext.getLocation(node),
+ "Use new SparseIntArray(...) instead for better performance",
+ null);
+ } else if (valueType.equals(BOOLEAN)) {
+ mContext.report(USE_SPARSEARRAY, node, mContext.getLocation(node),
+ "Use new SparseBooleanArray(...) instead for better performance",
+ null);
+ } else if (valueType.equals(LONG) && mContext.getProject().getMinSdk() >= 17) {
+ mContext.report(USE_SPARSEARRAY, node, mContext.getLocation(node),
+ "Use new SparseLongArray(...) instead for better performance",
+ null);
+ } else {
+ mContext.report(USE_SPARSEARRAY, node, mContext.getLocation(node),
+ String.format(
+ "Use new SparseArray<%1$s>(...) instead for better performance",
+ valueType),
+ null);
+ }
+ }
+ }
+ }
+ }
+
+ /** Visitor which records variable names assigned into */
+ private static class AssignmentTracker extends ForwardingAstVisitor {
+ private final Collection<String> mVariables;
+
+ public AssignmentTracker(Collection<String> variables) {
+ mVariables = variables;
+ }
+
+ @Override
+ public boolean visitBinaryExpression(BinaryExpression node) {
+ BinaryOperator operator = node.astOperator();
+ if (operator == BinaryOperator.ASSIGN || operator == BinaryOperator.OR_ASSIGN) {
+ mVariables.add(node.astLeft().toString());
+ }
+
+ return super.visitBinaryExpression(node);
+ }
+ }
+}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java
index 144a191..97cb5f6 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java
@@ -35,6 +35,7 @@ import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
import java.io.File;
import java.util.Arrays;
@@ -65,7 +66,7 @@ public class ManifestOrderDetector extends Detector implements Detector.XmlScann
/** Missing a {@code <uses-sdk>} element */
public static final Issue USES_SDK = Issue.create(
- "UsesSdk", //$NON-NLS-1$
+ "UsesMinSdkAttributes", //$NON-NLS-1$
"Checks that the minimum SDK and target SDK attributes are defined",
"The manifest should contain a <uses-sdk> element which defines the " +
@@ -80,12 +81,31 @@ public class ManifestOrderDetector extends Detector implements Detector.XmlScann
EnumSet.of(Scope.MANIFEST)).setMoreInfo(
"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html"); //$NON-NLS-1$
- /** Constructs a new accessibility check */
+ /** Missing a {@code <uses-sdk>} element */
+ public static final Issue MULTIPLE_USES_SDK = Issue.create(
+ "MultipleUsesSdk", //$NON-NLS-1$
+ "Checks that the <uses-sdk> element appears at most once",
+
+ "The <uses-sdk> element should appear just once; the tools will *not* merge the " +
+ "contents of all the elements so if you split up the atttributes across multiple " +
+ "elements, only one of them will take effect. To fix this, just merge all the " +
+ "attributes from the various elements into a single <uses-sdk> element.",
+
+ Category.CORRECTNESS,
+ 6,
+ Severity.ERROR,
+ ManifestOrderDetector.class,
+ EnumSet.of(Scope.MANIFEST)).setMoreInfo(
+ "http://developer.android.com/guide/topics/manifest/uses-sdk-element.html"); //$NON-NLS-1$
+
+ /** Constructs a new {@link ManifestOrderDetector} check */
public ManifestOrderDetector() {
}
private boolean mSeenApplication;
- private boolean mSeenUsesSdk;
+
+ /** Number of times we've seen the <uses-sdk> element */
+ private int mSeenUsesSdk;
@Override
public Speed getSpeed() {
@@ -100,12 +120,12 @@ public class ManifestOrderDetector extends Detector implements Detector.XmlScann
@Override
public void beforeCheckFile(Context context) {
mSeenApplication = false;
- mSeenUsesSdk = false;
+ mSeenUsesSdk = 0;
}
@Override
public void afterCheckFile(Context context) {
- if (!mSeenUsesSdk) {
+ if (mSeenUsesSdk == 0) {
context.report(USES_SDK, Location.create(context.file),
"Manifest should specify a minimum API level with " +
"<uses-sdk android:minSdkVersion=\"?\" />; if it really supports " +
@@ -136,7 +156,31 @@ public class ManifestOrderDetector extends Detector implements Detector.XmlScann
public void visitElement(XmlContext context, Element element) {
String tag = element.getTagName();
if (tag.equals(TAG_USES_SDK)) {
- mSeenUsesSdk = true;
+ mSeenUsesSdk++;
+
+ if (mSeenUsesSdk == 2) { // Only warn when we encounter the first one
+ Location location = context.getLocation(element);
+
+ // Link up *all* encountered locations in the document
+ NodeList elements = element.getOwnerDocument().getElementsByTagName(TAG_USES_SDK);
+ Location secondary = null;
+ for (int i = elements.getLength() - 1; i >= 0; i--) {
+ Element e = (Element) elements.item(i);
+ if (e != element) {
+ Location l = context.getLocation(e);
+ l.setSecondary(secondary);
+ l.setMessage("Also appears here");
+ secondary = l;
+ }
+ }
+ location.setSecondary(secondary);
+
+ context.report(MULTIPLE_USES_SDK, element, location,
+ "There should only be a single <uses-sdk> element in the manifest:" +
+ " merge these together", null);
+ return;
+ }
+
if (!element.hasAttributeNS(ANDROID_URI, ATTR_MIN_SDK_VERSION)) {
context.report(USES_SDK, element, context.getLocation(element),
"<uses-sdk> tag should specify a minimum API level with " +
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/MathDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/MathDetector.java
index 5266cc4..3c8d5d1 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/MathDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/MathDetector.java
@@ -63,7 +63,7 @@ public class MathDetector extends Detector implements Detector.ClassScanner {
//"http://developer.android.com/reference/android/util/FloatMath.html"); //$NON-NLS-1$
"http://developer.android.com/guide/practices/design/performance.html#avoidfloat"); //$NON-NLS-1$
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link MathDetector} check */
public MathDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/NamespaceDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/NamespaceDetector.java
new file mode 100644
index 0000000..60ae3a3
--- /dev/null
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/NamespaceDetector.java
@@ -0,0 +1,217 @@
+/*
+ * 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.checks;
+
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_PKG_PREFIX;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI;
+import static com.android.tools.lint.detector.api.LintConstants.XMLNS_PREFIX;
+
+import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.LayoutDetector;
+import com.android.tools.lint.detector.api.LintUtils;
+import com.android.tools.lint.detector.api.Scope;
+import com.android.tools.lint.detector.api.Severity;
+import com.android.tools.lint.detector.api.Speed;
+import com.android.tools.lint.detector.api.XmlContext;
+
+import org.w3c.dom.Attr;
+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 java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Checks for various issues related to XML namespaces
+ */
+public class NamespaceDetector extends LayoutDetector {
+ /** Typos in the namespace */
+ public static final Issue TYPO = Issue.create(
+ "NamespaceTypo", //$NON-NLS-1$
+ "Looks for misspellings in namespace declarations",
+
+ "Accidental misspellings in namespace declarations can lead to some very " +
+ "obscure error messages. This check looks for potential misspellings to " +
+ "help track these down.",
+ Category.CORRECTNESS,
+ 8,
+ Severity.WARNING,
+ NamespaceDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
+
+ /** Unused namespace declarations */
+ public static final Issue UNUSED = Issue.create(
+ "UnusedNamespace", //$NON-NLS-1$
+ "Finds unused namespaces in XML documents",
+
+ "Unused namespace declarations take up space and require processing that is not " +
+ "necessary",
+
+ Category.CORRECTNESS,
+ 1,
+ Severity.WARNING,
+ NamespaceDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
+
+ /** Using custom namespace attributes in a library project */
+ public static final Issue CUSTOMVIEW = Issue.create(
+ "LibraryCustomView", //$NON-NLS-1$
+ "Flags custom views in libraries, which currently do not work",
+
+ "Using a custom view in a library project (where the custom view " +
+ "requires XML attributes from a custom namespace) does not yet " +
+ "work.",
+ Category.CORRECTNESS,
+ 6,
+ Severity.ERROR,
+ NamespaceDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
+
+ /** Prefix relevant for custom namespaces */
+ private static final String XMLNS_ANDROID = "xmlns:android"; //$NON-NLS-1$
+ private static final String XMLNS_A = "xmlns:a"; //$NON-NLS-1$
+ private static final String URI_PREFIX = "http://schemas.android.com/apk/res/"; //$NON-NLS-1$
+
+ private Map<String, Attr> mUnusedNamespaces;
+ private boolean mCheckUnused;
+ private boolean mCheckCustomAttrs;
+
+ /** Constructs a new {@link NamespaceDetector} */
+ public NamespaceDetector() {
+ }
+
+ @Override
+ public Speed getSpeed() {
+ return Speed.FAST;
+ }
+
+ @Override
+ public void visitDocument(XmlContext context, Document document) {
+ boolean haveCustomNamespace = false;
+ Element root = document.getDocumentElement();
+ NamedNodeMap attributes = root.getAttributes();
+ for (int i = 0, n = attributes.getLength(); i < n; i++) {
+ Node item = attributes.item(i);
+ if (item.getNodeName().startsWith(XMLNS_PREFIX)) {
+ String value = item.getNodeValue();
+
+ if (!value.equals(ANDROID_URI)) {
+ Attr attribute = (Attr) item;
+
+ if (value.startsWith(URI_PREFIX)) {
+ haveCustomNamespace = true;
+ if (mUnusedNamespaces == null) {
+ mUnusedNamespaces = new HashMap<String, Attr>();
+ }
+ mUnusedNamespaces.put(item.getNodeName().substring(XMLNS_PREFIX.length()),
+ attribute);
+ }
+
+ String name = attribute.getName();
+ if (!name.equals(XMLNS_ANDROID) && !name.equals(XMLNS_A)) {
+ continue;
+ }
+
+ if (!context.isEnabled(TYPO)) {
+ continue;
+ }
+
+ if (name.equals(XMLNS_A)) {
+ // For the "android" prefix we always assume that the namespace prefix
+ // should be our expected prefix, but for the "a" prefix we make sure
+ // that it's at least "close"; if you're bound it to something completely
+ // different, don't complain.
+ if (LintUtils.editDistance(ANDROID_URI, value) > 4) {
+ continue;
+ }
+ }
+
+ if (value.equalsIgnoreCase(ANDROID_URI)) {
+ context.report(TYPO, attribute, context.getLocation(attribute),
+ String.format(
+ "URI is case sensitive: was \"%1$s\", expected \"%2$s\"",
+ value, ANDROID_URI), null);
+ } else {
+ context.report(TYPO, attribute, context.getLocation(attribute),
+ String.format(
+ "Unexpected namespace URI bound to the \"android\" " +
+ "prefix, was %1$s, expected %2$s", value, ANDROID_URI),
+ null);
+ }
+ }
+ }
+ }
+
+ if (haveCustomNamespace) {
+ mCheckCustomAttrs = context.isEnabled(CUSTOMVIEW) && context.getProject().isLibrary();
+ mCheckUnused = context.isEnabled(UNUSED);
+ checkElement(context, document.getDocumentElement());
+
+ if (mCheckUnused && mUnusedNamespaces.size() > 0) {
+ for (Map.Entry<String, Attr> entry : mUnusedNamespaces.entrySet()) {
+ String prefix = entry.getKey();
+ Attr attribute = entry.getValue();
+ context.report(UNUSED, attribute, context.getLocation(attribute),
+ String.format("Unused namespace %1$s", prefix), null);
+ }
+ }
+ }
+ }
+
+ private void checkElement(XmlContext context, Node node) {
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ if (mCheckCustomAttrs) {
+ String tag = node.getNodeName();
+ if (tag.indexOf('.') != -1
+ // Don't consider android.support.* and android.app.FragmentBreadCrumbs etc
+ && !tag.startsWith(ANDROID_PKG_PREFIX)) {
+ NamedNodeMap attributes = ((Element) node).getAttributes();
+ for (int i = 0, n = attributes.getLength(); i < n; i++) {
+ Attr attribute = (Attr) attributes.item(i);
+ String uri = attribute.getNamespaceURI();
+ if (uri != null && uri.length() > 0 && uri.startsWith(URI_PREFIX)
+ && !uri.equals(ANDROID_URI)) {
+ context.report(CUSTOMVIEW, attribute, context.getLocation(attribute),
+ "Using a custom namespace attributes in a library project does " +
+ "not yet work", null);
+ }
+ }
+ }
+ }
+
+ if (mCheckUnused) {
+ NamedNodeMap attributes = ((Element) node).getAttributes();
+ for (int i = 0, n = attributes.getLength(); i < n; i++) {
+ Attr attribute = (Attr) attributes.item(i);
+ String prefix = attribute.getPrefix();
+ if (prefix != null) {
+ mUnusedNamespaces.remove(prefix);
+ }
+ }
+ }
+
+ NodeList childNodes = node.getChildNodes();
+ for (int i = 0, n = childNodes.getLength(); i < n; i++) {
+ checkElement(context, childNodes.item(i));
+ }
+ }
+ }
+}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/PrivateResourceDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/PrivateResourceDetector.java
index 7f9e859..2b37f82 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/PrivateResourceDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/PrivateResourceDetector.java
@@ -43,7 +43,7 @@ public class PrivateResourceDetector extends ResourceXmlDetector {
"resources under $ANDROID_SK/platforms/android-$VERSION/data/res/.",
Category.CORRECTNESS,
3,
- Severity.ERROR,
+ Severity.FATAL,
PrivateResourceDetector.class,
Scope.RESOURCE_FILE_SCOPE);
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ProguardDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ProguardDetector.java
index 798cf26..95de739 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ProguardDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ProguardDetector.java
@@ -46,7 +46,7 @@ public class ProguardDetector extends Detector {
"not Java (such as possibly CustomViews) can get deleted.",
Category.CORRECTNESS,
8,
- Severity.ERROR,
+ Severity.FATAL,
ProguardDetector.class,
EnumSet.of(Scope.PROGUARD_FILE)).setMoreInfo(
"http://http://code.google.com/p/android/issues/detail?id=16384"); //$NON-NLS-1$
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/SdCardDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/SdCardDetector.java
index 1a765b8..b239d57 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/SdCardDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/SdCardDetector.java
@@ -54,7 +54,7 @@ public class SdCardDetector extends Detector implements Detector.JavaScanner {
Scope.JAVA_FILE_SCOPE).setMoreInfo(
"http://developer.android.com/guide/topics/data/data-storage.html#filesExternal"); //$NON-NLS-1$
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link SdCardDetector} check */
public SdCardDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java
index 1493131..f19f8aa 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java
@@ -107,7 +107,7 @@ public class SecurityDetector extends Detector implements Detector.XmlScanner,
SecurityDetector.class,
Scope.JAVA_FILE_SCOPE);
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link SecurityDetector} check */
public SecurityDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java
index 20fcf6b..3235c9a 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java
@@ -82,6 +82,9 @@ public class StateListDetector extends ResourceXmlDetector {
NamedNodeMap attributes = child.getAttributes();
for (int j = 0; j < attributes.getLength(); j++) {
Attr attribute = (Attr) attributes.item(j);
+ if (attribute.getLocalName() == null) {
+ continue;
+ }
if (attribute.getLocalName().startsWith("state_")) {
hasState = true;
break;
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StringFormatDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StringFormatDetector.java
index 4d1c8ae..665a43b 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StringFormatDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StringFormatDetector.java
@@ -75,7 +75,8 @@ import lombok.ast.VariableDefinitionEntry;
import lombok.ast.VariableReference;
/**
- * Check which looks for accessibility problems like missing content descriptions
+ * Check which looks for problems with formatting strings such as inconsistencies between
+ * translations or between string declaration and string usage in Java.
*/
public class StringFormatDetector extends ResourceXmlDetector implements Detector.JavaScanner {
/** The name of the String.format method */
@@ -149,13 +150,14 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto
* defined multiple times, usually for different translations.
*/
private Map<String, List<Pair<Handle, String>>> mFormatStrings;
+
/**
* List of strings that contain percents that aren't formatting strings; these
* should not be passed to String.format.
*/
private Map<String, Handle> mNotFormatStrings = new HashMap<String, Handle>();
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link StringFormatDetector} check */
public StringFormatDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StyleCycleDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StyleCycleDetector.java
index 6856c14..f248d6e 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StyleCycleDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StyleCycleDetector.java
@@ -48,7 +48,7 @@ public class StyleCycleDetector extends ResourceXmlDetector {
"exceptions.",
Category.CORRECTNESS,
8,
- Severity.ERROR,
+ Severity.FATAL,
StyleCycleDetector.class,
Scope.RESOURCE_FILE_SCOPE).setMoreInfo(
"http://developer.android.com/guide/topics/ui/themes.html#Inheritance"); //$NON-NLS-1$
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TranslationDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TranslationDetector.java
index 41390d7..efa53ad 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TranslationDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TranslationDetector.java
@@ -81,7 +81,7 @@ public class TranslationDetector extends ResourceXmlDetector {
"environment variable ANDROID_LINT_COMPLETE_REGIONS.",
Category.MESSAGES,
8,
- Severity.ERROR,
+ Severity.FATAL,
TranslationDetector.class,
Scope.ALL_RESOURCES_SCOPE);
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ViewConstructorDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ViewConstructorDetector.java
index ef89232..e5d1e7e 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ViewConstructorDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ViewConstructorDetector.java
@@ -67,7 +67,7 @@ public class ViewConstructorDetector extends Detector implements Detector.ClassS
ViewConstructorDetector.class,
EnumSet.of(Scope.CLASS_FILE));
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link ViewConstructorDetector} check */
public ViewConstructorDetector() {
}
@@ -92,40 +92,55 @@ public class ViewConstructorDetector extends Detector implements Detector.ClassS
return;
}
- String superName = classNode.superName;
- if (superName.equals("android/view/View") //$NON-NLS-1$
- || superName.equals("android/view/ViewGroup") //$NON-NLS-1$
- || superName.startsWith("android/widget/") //$NON-NLS-1$
- && !((superName.endsWith("Adapter") //$NON-NLS-1$
- || superName.endsWith("Controller") //$NON-NLS-1$
- || superName.endsWith("Service") //$NON-NLS-1$
- || superName.endsWith("Provider") //$NON-NLS-1$
- || superName.endsWith("Filter")))) { //$NON-NLS-1$
-
- // Look through constructors
- @SuppressWarnings("rawtypes")
- List methods = classNode.methods;
- for (Object methodObject : methods) {
- MethodNode method = (MethodNode) methodObject;
- if (method.name.equals("<init>")) { //$NON-NLS-1$
- String desc = method.desc;
- if (desc.equals(SIG1) || desc.equals(SIG2) || desc.equals(SIG3)) {
- return;
- }
- }
+ if (isViewClass(context, classNode)) {
+ checkConstructors(context, classNode);
+ }
+ }
+
+ private static boolean isViewClass(ClassContext context, ClassNode node) {
+ String superName = node.superName;
+ while (superName != null) {
+ if (superName.equals("android/view/View") //$NON-NLS-1$
+ || superName.equals("android/view/ViewGroup") //$NON-NLS-1$
+ || superName.startsWith("android/widget/") //$NON-NLS-1$
+ && !((superName.endsWith("Adapter") //$NON-NLS-1$
+ || superName.endsWith("Controller") //$NON-NLS-1$
+ || superName.endsWith("Service") //$NON-NLS-1$
+ || superName.endsWith("Provider") //$NON-NLS-1$
+ || superName.endsWith("Filter")))) { //$NON-NLS-1$
+ return true;
}
- // If we get this far, none of the expected constructors were found.
-
- // Use location of one of the constructors?
- String message = String.format(
- "Custom view %1$s is missing constructor used by tools: " +
- "(Context) or (Context,AttributeSet) or (Context,AttributeSet,int)",
- classNode.name);
- File sourceFile = context.getSourceFile();
- Location location = Location.create(sourceFile != null
- ? sourceFile : context.file);
- context.report(ISSUE, location, message, null /*data*/);
+ superName = context.getDriver().getSuperClass(superName);
}
+
+ return false;
+ }
+
+ private void checkConstructors(ClassContext context, ClassNode classNode) {
+ // Look through constructors
+ @SuppressWarnings("rawtypes")
+ List methods = classNode.methods;
+ for (Object methodObject : methods) {
+ MethodNode method = (MethodNode) methodObject;
+ if (method.name.equals("<init>")) { //$NON-NLS-1$
+ String desc = method.desc;
+ if (desc.equals(SIG1) || desc.equals(SIG2) || desc.equals(SIG3)) {
+ return;
+ }
+ }
+ }
+
+ // If we get this far, none of the expected constructors were found.
+
+ // Use location of one of the constructors?
+ String message = String.format(
+ "Custom view %1$s is missing constructor used by tools: " +
+ "(Context) or (Context,AttributeSet) or (Context,AttributeSet,int)",
+ classNode.name);
+ File sourceFile = context.getSourceFile();
+ Location location = Location.create(sourceFile != null
+ ? sourceFile : context.file);
+ context.report(ISSUE, location, message, null /*data*/);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongIdDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongIdDetector.java
index 823eabd..656f4de 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongIdDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongIdDetector.java
@@ -97,7 +97,7 @@ public class WrongIdDetector extends LayoutDetector {
"it.",
Category.CORRECTNESS,
8,
- Severity.ERROR,
+ Severity.FATAL,
WrongIdDetector.class,
Scope.ALL_RESOURCES_SCOPE);
@@ -179,6 +179,7 @@ public class WrongIdDetector extends LayoutDetector {
XmlContext xmlContext = (XmlContext) context;
IDomParser parser = xmlContext.parser;
Handle handle = parser.createLocationHandle(xmlContext, attr);
+ handle.setClientData(attr);
if (mHandles == null) {
mHandles = new ArrayList<Pair<String,Handle>>();
@@ -205,7 +206,6 @@ public class WrongIdDetector extends LayoutDetector {
boolean isBound = idDefined(mGlobalIds, id);
if (!isBound && checkExists && projectScope) {
Handle handle = pair.getSecond();
- Location location = handle.resolve();
boolean isDeclared = idDefined(mDeclaredIds, id);
id = stripIdPrefix(id);
String suggestionMessage;
@@ -229,25 +229,33 @@ public class WrongIdDetector extends LayoutDetector {
"The id \"%1$s\" is not defined anywhere.%2$s",
id, suggestionMessage);
}
- // TODO: Compute applicable node scope
- context.report(UNKNOWN_ID, location, message, null);
+ report(context, UNKNOWN_ID, handle, message);
} else if (checkSameLayout && (!projectScope || isBound)) {
// The id was defined, but in a different layout. Usually not intentional
// (might be referring to a random other view that happens to have the same
// name.)
Handle handle = pair.getSecond();
- Location location = handle.resolve();
- // TODO: Compute applicable node scope
- context.report(UNKNOWN_ID_LAYOUT, location,
+ report(context, UNKNOWN_ID_LAYOUT, handle,
String.format(
"The id \"%1$s\" is not referring to any views in this layout",
- stripIdPrefix(id)),
- null);
+ stripIdPrefix(id)));
}
}
}
}
+ private void report(Context context, Issue issue, Handle handle, String message) {
+ Location location = handle.resolve();
+ Object clientData = handle.getClientData();
+ if (clientData instanceof Attr) {
+ if (context.getDriver().isSuppressed(issue, (Attr) clientData)) {
+ return;
+ }
+ }
+
+ context.report(issue, location, message, null);
+ }
+
@Override
public void visitElement(XmlContext context, Element element) {
if (element.getTagName().equals(RELATIVE_LAYOUT)) {
@@ -272,7 +280,7 @@ public class WrongIdDetector extends LayoutDetector {
@Override
public void visitAttribute(XmlContext context, Attr attribute) {
- assert attribute.getLocalName().equals(ATTR_ID);
+ assert attribute.getName().equals(ATTR_ID) || attribute.getLocalName().equals(ATTR_ID);
String id = attribute.getValue();
mFileIds.add(id);
mGlobalIds.add(id);
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongImportDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongImportDetector.java
index 59e3314..2aa416c 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongImportDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongImportDetector.java
@@ -62,7 +62,7 @@ public class WrongImportDetector extends Detector implements Detector.JavaScanne
WrongImportDetector.class,
Scope.JAVA_FILE_SCOPE);
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link WrongImportDetector} check */
public WrongImportDetector() {
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java
index 36e4106..7f21477 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java
@@ -298,6 +298,10 @@ public abstract class AbstractCheckTest extends TestCase {
}
Severity severity = context.getConfiguration().getSeverity(issue);
+ if (severity == Severity.FATAL) {
+ // Treat fatal errors like errors in the golden files.
+ severity = Severity.ERROR;
+ }
sb.append(severity.getDescription());
sb.append(": ");
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java
index b3050fe..d3a8aad 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java
@@ -146,6 +146,22 @@ public class ApiDetectorTest extends AbstractCheckTest {
));
}
+ public void testInheritLocal() throws Exception {
+ // Test virtual dispatch in a local class which extends some other local class (which
+ // in turn extends an Android API)
+ assertEquals(
+ "ApiCallTest3.java:10: Error: Call requires API level 11 (current min is 1): android.app.Activity#getActionBar",
+
+ lintProject(
+ "apicheck/classpath=>.classpath",
+ "apicheck/minsdk1.xml=>AndroidManifest.xml",
+ "apicheck/Intermediate.java.txt=>src/test/pkg/Intermediate.java",
+ "apicheck/ApiCallTest3.java.txt=>src/test/pkg/ApiCallTest3.java",
+ "apicheck/ApiCallTest3.class.data=>bin/classes/test/pkg/ApiCallTest3.class",
+ "apicheck/Intermediate.class.data=>bin/classes/test/pkg/Intermediate.class"
+ ));
+ }
+
// Test suppressing errors -- on classes, methods etc.
public void testSuppress() throws Exception {
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/FieldGetterDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/FieldGetterDetectorTest.java
index bdb7618..ccbf26b 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/FieldGetterDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/FieldGetterDetectorTest.java
@@ -41,6 +41,21 @@ public class FieldGetterDetectorTest extends AbstractCheckTest {
));
}
+ public void testLibraries() throws Exception {
+ // This tests the infrastructure: it makes sure that we *don't* run this
+ // check in jars that are on the jar library dependency path (testJar() checks
+ // that it *does* work for local jar classes)
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "bytecode/classpath-lib=>.classpath",
+ "bytecode/AndroidManifest.xml=>AndroidManifest.xml",
+ "bytecode/GetterTest.java.txt=>src/test/bytecode/GetterTest.java",
+ "bytecode/GetterTest.jar.data=>libs/library.jar"
+ ));
+ }
+
public void testJar() throws Exception {
assertEquals(
"GetterTest.java:47: Warning: Calling getter method getFoo1() on self is slower than field access (mFoo1)\n" +
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
new file mode 100644
index 0000000..7853ddc
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+@SuppressWarnings("javadoc")
+public class JavaPerformanceDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new JavaPerformanceDetector();
+ }
+
+ public void test() throws Exception {
+ assertEquals(
+ "JavaPerformanceTest.java:103: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:109: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:112: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:113: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:114: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:116: Warning: Avoid object allocations during draw operations: Use Canvas.getClipBounds(Rect) instead of Canvas.getClipBounds() which allocates a temporary Rect\n" +
+ "JavaPerformanceTest.java:140: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:28: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:29: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:70: Warning: Use new SparseArray<String>(...) instead for better performance\n" +
+ "JavaPerformanceTest.java:72: Warning: Use new SparseBooleanArray(...) instead for better performance\n" +
+ "JavaPerformanceTest.java:74: Warning: Use new SparseIntArray(...) instead for better performance",
+
+ lintProject("src/test/pkg/JavaPerformanceTest.java.txt=>" +
+ "src/test/pkg/JavaPerformanceTest.java"));
+ }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ManifestOrderDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ManifestOrderDetectorTest.java
index cebf793..8ecee91 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ManifestOrderDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ManifestOrderDetectorTest.java
@@ -64,4 +64,17 @@ public class ManifestOrderDetectorTest extends AbstractCheckTest {
"missingmin.xml=>AndroidManifest.xml",
"res/values/strings.xml"));
}
+
+ public void testMultipleSdk() throws Exception {
+ assertEquals(
+ "AndroidManifest.xml:7: Warning: <uses-sdk> tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be enabled) with android:targetSdkVersion=\"?\"\n" +
+ "AndroidManifest.xml:9: Warning: <uses-sdk> tag should specify a minimum API level with android:minSdkVersion=\"?\"\n" +
+ "ManifestOrderDetectorTest_testMultipleSdk/AndroidManifest.xml:8: Error: There should only be a single <uses-sdk> element in the manifest: merge these together\n" +
+ "=> ManifestOrderDetectorTest_testMultipleSdk/AndroidManifest.xml:7: Also appears here\n" +
+ "=> ManifestOrderDetectorTest_testMultipleSdk/AndroidManifest.xml:9: Also appears here",
+
+ lintProject(
+ "multiplesdk.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/NamespaceDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/NamespaceDetectorTest.java
new file mode 100644
index 0000000..3570f5c
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/NamespaceDetectorTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+@SuppressWarnings("javadoc")
+public class NamespaceDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new NamespaceDetector();
+ }
+
+ public void testCustom() throws Exception {
+ assertEquals(
+ "customview.xml:16: Error: Using a custom namespace attributes in a library project does not yet work",
+
+ lintProject(
+ "multiproject/library-manifest.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>project.properties",
+ "res/layout/customview.xml"
+ ));
+ }
+
+ public void testCustomOk() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "multiproject/library-manifest.xml=>AndroidManifest.xml",
+
+ // Use a standard project properties instead: no warning since it's
+ // not a library project:
+ //"multiproject/library.properties=>project.properties",
+
+ "res/layout/customview.xml"
+ ));
+ }
+
+ public void testTypo() throws Exception {
+ assertEquals(
+ "wrong_namespace.xml:2: Warning: Unexpected namespace URI bound to the " +
+ "\"android\" prefix, was http://schemas.android.com/apk/res/andriod, " +
+ "expected http://schemas.android.com/apk/res/android",
+
+ lintProject("res/layout/wrong_namespace.xml"));
+ }
+
+ public void testTypo2() throws Exception {
+ assertEquals(
+ "wrong_namespace2.xml:2: Warning: URI is case sensitive: was " +
+ "\"http://schemas.android.com/apk/res/Android\", expected " +
+ "\"http://schemas.android.com/apk/res/android\"",
+
+ lintProject("res/layout/wrong_namespace2.xml"));
+ }
+
+ public void testTypo3() throws Exception {
+ assertEquals(
+ "wrong_namespace3.xml:2: Warning: Unexpected namespace URI bound to the " +
+ "\"android\" prefix, was http://schemas.android.com/apk/res/androi, " +
+ "expected http://schemas.android.com/apk/res/android",
+
+ lintProject("res/layout/wrong_namespace3.xml"));
+ }
+
+ public void testTypoOk() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject("res/layout/wrong_namespace4.xml"));
+ }
+
+ public void testUnused() throws Exception {
+ assertEquals(
+ "unused_namespace.xml:3: Warning: Unused namespace unused1\n" +
+ "unused_namespace.xml:4: Warning: Unused namespace unused2",
+
+ lintProject("res/layout/unused_namespace.xml"));
+ }
+
+ public void testUnusedOk() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject("res/layout/layout1.xml"));
+ }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/SecurityDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/SecurityDetectorTest.java
index 1baed6c..925714e 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/SecurityDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/SecurityDetectorTest.java
@@ -41,6 +41,15 @@ public class SecurityDetectorTest extends AbstractCheckTest {
"res/values/strings.xml"));
}
+ public void testBroken3() throws Exception {
+ // Not defining exported, but have intent-filters
+ assertEquals(
+ "AndroidManifest.xml:12: Warning: Exported service does not require permission",
+ lintProject(
+ "exportservice5.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
public void testOk1() throws Exception {
// Defines a permission on the <service> element
assertEquals(
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ViewConstructorDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ViewConstructorDetectorTest.java
index 3a6b9ec..dccb667 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ViewConstructorDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ViewConstructorDetectorTest.java
@@ -46,4 +46,22 @@ public class ViewConstructorDetectorTest extends AbstractCheckTest {
"bytecode/CustomView3.class.data=>bin/classes/test/bytecode/CustomView3.class"
));
}
+
+ public void testInheritLocal() throws Exception {
+ assertEquals(
+ "CustomViewTest.java: Warning: Custom view test/pkg/CustomViewTest is missing " +
+ "constructor used by tools: (Context) or (Context,AttributeSet) or " +
+ "(Context,AttributeSet,int)",
+
+ lintProject(
+ "bytecode/.classpath=>.classpath",
+ "bytecode/AndroidManifest.xml=>AndroidManifest.xml",
+ "apicheck/Intermediate.java.txt=>src/test/pkg/Intermediate.java.txt",
+ "src/test/pkg/CustomViewTest.java.txt=>src/test/pkg/CustomViewTest.java",
+ "bytecode/CustomViewTest.class.data=>bin/classes/test/pkg/CustomViewTest.class",
+ "apicheck/Intermediate.class.data=>bin/classes/test/pkg/Intermediate.class",
+ "apicheck/Intermediate$IntermediateCustomV.class.data=>" +
+ "bin/classes/test/pkg/Intermediate$IntermediateCustomV.class"
+ ));
+ }
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/WrongIdDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/WrongIdDetectorTest.java
index 337ac5d..5783bd1 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/WrongIdDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/WrongIdDetectorTest.java
@@ -48,4 +48,23 @@ public class WrongIdDetectorTest extends AbstractCheckTest {
lintFiles("wrongid/layout1.xml=>res/layout/layout1.xml"));
}
+
+ public void testSuppressed() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "wrongid/ignorelayout1.xml=>res/layout/layout1.xml",
+ "wrongid/layout2.xml=>res/layout/layout2.xml",
+ "wrongid/ids.xml=>res/values/ids.xml"
+ ));
+ }
+
+ public void testSuppressedSingleFile() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintFiles("wrongid/ignorelayout1.xml=>res/layout/layout1.xml"));
+ }
+
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.class.data
new file mode 100644
index 0000000..3d39e74
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.class.data
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.java.txt
new file mode 100644
index 0000000..d1ea3e4
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.java.txt
@@ -0,0 +1,12 @@
+package test.pkg;
+
+/**
+ * Call test where the parent class is some other project class which in turn
+ * extends the public API
+ */
+public class ApiCallTest3 extends Intermediate {
+ public void foo() {
+ // Virtual call
+ getActionBar(); // API 11
+ }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/Intermediate$IntermediateCustomV.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/Intermediate$IntermediateCustomV.class.data
new file mode 100644
index 0000000..cbf323c
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/Intermediate$IntermediateCustomV.class.data
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/Intermediate.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/Intermediate.class.data
new file mode 100644
index 0000000..8d187da
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/Intermediate.class.data
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/Intermediate.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/Intermediate.java.txt
new file mode 100644
index 0000000..fea5c4f
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/Intermediate.java.txt
@@ -0,0 +1,15 @@
+package test.pkg;
+
+import android.app.Activity;
+import android.widget.Button;
+
+/** Local activity */
+public abstract class Intermediate extends Activity {
+
+ /** Local Custom view */
+ public abstract static class IntermediateCustomV extends Button {
+ public IntermediateCustomV() {
+ super(null);
+ }
+ }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/CustomViewTest.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/CustomViewTest.class.data
new file mode 100644
index 0000000..e145fbc
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/CustomViewTest.class.data
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/classpath-lib b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/classpath-lib
new file mode 100644
index 0000000..e730df8
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/classpath-lib
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="gen"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+ <classpathentry kind="lib" path="libs/library.jar"/>
+ <classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportservice5.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportservice5.xml
new file mode 100644
index 0000000..c9b9a78
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportservice5.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="foo.bar2"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk android:minSdkVersion="14" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name" >
+ <service
+ android:label="@string/app_name"
+ android:name="com.sample.service.serviceClass"
+ android:process=":remote" >
+ <intent-filter >
+ <action android:name="com.sample.service.serviceClass" >
+ </action>
+ </intent-filter>
+ </service>
+ </application>
+
+</manifest>
+
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/multiplesdk.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/multiplesdk.xml
new file mode 100644
index 0000000..950cf4d
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/multiplesdk.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.bytecode"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk android:minSdkVersion="5" />
+ <uses-sdk android:targetSdkVersion="14" />
+ <uses-sdk android:maxSdkVersion="15" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name" >
+ <activity
+ android:name=".BytecodeTestsActivity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/customview.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/customview.xml
new file mode 100644
index 0000000..976d636
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/customview.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:other="http://schemas.foo.bar.com/other"
+ xmlns:foo="http://schemas.android.com/apk/res/foo"
+ android:id="@+id/newlinear"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <foo.bar.Baz
+ android:id="@+id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button1"
+ foo:misc="Custom attribute"
+ tools:ignore="HardcodedText" >
+ </foo.bar.Baz>
+
+ <!-- Wrong namespace uri prefix: Don't warn -->
+ <foo.bar.Baz
+ android:id="@+id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button1"
+ other:misc="Custom attribute"
+ tools:ignore="HardcodedText" >
+ </foo.bar.Baz>
+
+</LinearLayout>
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/unused_namespace.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/unused_namespace.xml
new file mode 100644
index 0000000..f633e4b
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/unused_namespace.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<foo.bar.LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:unused1="http://schemas.android.com/apk/res/unused1"
+ xmlns:unused2="http://schemas.android.com/apk/res/unused1"
+ xmlns:unused3="http://foo.bar.com/foo"
+ xmlns:notunused="http://schemas.android.com/apk/res/notunused"
+ xmlns:tools="http://schemas.android.com/tools" >
+
+ <foo.bar.Button
+ notunused:foo="Foo"
+ tools:ignore="HardcodedText" >
+ </foo.bar.Button>
+
+</foo.bar.LinearLayout>
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace.xml
new file mode 100644
index 0000000..c6e2143
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/andriod"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <include
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ layout="@layout/layout2" />
+
+ <Button
+ android:id="@+id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button" />
+
+ <Button
+ android:id="@+id/button2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button" />
+
+</LinearLayout>
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace2.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace2.xml
new file mode 100644
index 0000000..49dc611
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace2.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/Android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <include
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ layout="@layout/layout2" />
+
+ <Button
+ android:id="@+id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button" />
+
+ <Button
+ android:id="@+id/button2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button" />
+
+</LinearLayout>
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace3.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace3.xml
new file mode 100644
index 0000000..02252b4
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace3.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:a="http://schemas.android.com/apk/res/androi"
+ a:layout_width="match_parent"
+ a:layout_height="match_parent"
+ a:orientation="vertical" >
+
+ <include
+ a:layout_width="wrap_content"
+ a:layout_height="wrap_content"
+ layout="@layout/layout2" />
+
+ <Button
+ a:id="@+id/button1"
+ a:layout_width="wrap_content"
+ a:layout_height="wrap_content"
+ a:text="Button" />
+
+ <Button
+ a:id="@+id/button2"
+ a:layout_width="wrap_content"
+ a:layout_height="wrap_content"
+ a:text="Button" />
+
+</LinearLayout>
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace4.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace4.xml
new file mode 100644
index 0000000..4142622
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/wrong_namespace4.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This file does *not* have a wrong namespace: it's testdata to make sure we don't complain when "a" is defined for something unrelated -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:a="http://something/very/different"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <include
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ layout="@layout/layout2" />
+
+ <Button
+ android:id="@+id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button" />
+
+ <Button
+ android:id="@+id/button2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button" />
+
+</LinearLayout>
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/CustomViewTest.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/CustomViewTest.java.txt
new file mode 100644
index 0000000..1862ccc
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/CustomViewTest.java.txt
@@ -0,0 +1,6 @@
+package test.pkg;
+
+import test.pkg.Intermediate.IntermediateCustomV;
+
+public class CustomViewTest extends IntermediateCustomV {
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt
new file mode 100644
index 0000000..5cd99d9
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt
@@ -0,0 +1,147 @@
+package test.pkg;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.Button;
+/** Some test data for the JavaPerformanceDetector */
+@SuppressWarnings("unused")
+public class JavaPerformanceTest extends Button {
+ public JavaPerformanceTest(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ private Rect cachedRect;
+
+ @Override
+ protected void onDraw(android.graphics.Canvas canvas) {
+ super.onDraw(canvas);
+
+ // Various allocations:
+ new String("foo");
+ String s = new String("bar");
+
+ // This one should not be reported:
+ @SuppressLint("DrawAllocation")
+ Integer i = new Integer(5);
+
+ // Cached object initialized lazily: should not complain about these
+ if (cachedRect == null) {
+ cachedRect = new Rect(0, 0, 100, 100);
+ }
+ if (cachedRect == null || cachedRect.width() != 50) {
+ cachedRect = new Rect(0, 0, 50, 100);
+ }
+
+ boolean b = Boolean.valueOf(true); // auto-boxing
+ dummy(1, 2);
+
+ // Non-allocations
+ super.animate();
+ dummy2(1, 2);
+ int x = 4 + '5';
+
+ // This will involve allocations, but we don't track
+ // inter-procedural stuff here
+ someOtherMethod();
+ }
+
+ void dummy(Integer foo, int bar) {
+ dummy2(foo, bar);
+ }
+
+ void dummy2(int foo, int bar) {
+ }
+
+ void someOtherMethod() {
+ // Allocations are okay here
+ new String("foo");
+ String s = new String("bar");
+ boolean b = Boolean.valueOf(true); // auto-boxing
+
+ // Sparse array candidates
+ Map<Integer, String> myMap = new HashMap<Integer, String>();
+ // Should use SparseBooleanArray
+ Map<Integer, Boolean> myBoolMap = new HashMap<Integer, Boolean>();
+ // Should use SparseIntArray
+ Map<Integer, Integer> myIntMap = new java.util.HashMap<Integer, Integer>();
+
+ // This one should not be reported:
+ @SuppressLint("UseSparseArrays")
+ Map<Integer, Object> myOtherMap = new HashMap<Integer, Object>();
+ }
+
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec,
+ boolean x) { // wrong signature
+ new String("not an error");
+ }
+
+ protected void onMeasure(int widthMeasureSpec) { // wrong signature
+ new String("not an error");
+ }
+
+ protected void onLayout(boolean changed, int left, int top, int right,
+ int bottom, int wrong) { // wrong signature
+ new String("not an error");
+ }
+
+ protected void onLayout(boolean changed, int left, int top, int right) {
+ // wrong signature
+ new String("not an error");
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right,
+ int bottom) {
+ new String("flag me");
+ }
+
+ @SuppressWarnings("null") // not real code
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ new String("flag me");
+
+ // Forbidden factory methods:
+ Bitmap.createBitmap(100, 100, null);
+ android.graphics.Bitmap.createScaledBitmap(null, 100, 100, false);
+ BitmapFactory.decodeFile(null);
+ Canvas canvas = null;
+ canvas.getClipBounds(); // allocates on your behalf
+ canvas.getClipBounds(null); // NOT an error
+
+ final int layoutWidth = getWidth();
+ final int layoutHeight = getHeight();
+ if (mAllowCrop && (mOverlay == null || mOverlay.getWidth() != layoutWidth ||
+ mOverlay.getHeight() != layoutHeight)) {
+ mOverlay = Bitmap.createBitmap(layoutWidth, layoutHeight, Bitmap.Config.ARGB_8888);
+ mOverlayCanvas = new Canvas(mOverlay);
+ }
+
+ if (widthMeasureSpec == 42) {
+ throw new IllegalStateException("Test"); // NOT an allocation
+ }
+
+ // More lazy init tests
+ boolean initialized = false;
+ if (!initialized) {
+ new String("foo");
+ initialized = true;
+ }
+
+ // NOT lazy initialization
+ if (!initialized || mOverlay == null) {
+ new String("foo");
+ }
+}
+
+ private boolean mAllowCrop;
+ private Canvas mOverlayCanvas;
+ private Bitmap mOverlay;
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml
new file mode 100644
index 0000000..bc6d5fd
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/RelativeLayout1"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <!-- my_id1 is defined in ids.xml, my_id2 is defined in main2, my_id3 does not exist -->
+
+ <Button
+ android:id="@+id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/button5"
+ android:layout_alignLeft="@+id/my_id2"
+ android:layout_alignParentTop="true"
+ android:layout_alignRight="@+id/my_id3"
+ android:layout_alignTop="@+id/my_id1"
+ android:text="Button"
+ tools:ignore="UnknownIdInLayout,UnknownId" />
+
+ <Button
+ android:id="@+id/button2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@+id/button1"
+ android:text="Button" />
+
+ <Button
+ android:id="@+id/button3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@+id/button2"
+ android:text="Button" />
+
+ <Button
+ android:id="@+id/button4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@+id/button3"
+ android:text="Button" />
+
+</RelativeLayout>
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java
index fde9b90..49395ef 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java
@@ -328,9 +328,11 @@ class PackagesDiffLogic {
PkgItem item = itemIt.next();
if (mItemsToRemove.contains(item)) {
itemIt.remove();
+ hasChanged = true;
} else if (item.hasUpdatePkg() &&
mUpdatesToRemove.containsKey(item.getUpdatePkg())) {
item.removeUpdate();
+ hasChanged = true;
}
}
}
@@ -471,6 +473,14 @@ class PackagesDiffLogic {
return hasChanged;
}
+ /**
+ * {@link PkgState}s to check in {@link #processSource(UpdateOp, SdkSource, Package[])}.
+ * The order matters.
+ * When installing the diff will have both the new and the installed item and we
+ * need to merge with the installed one before the new one.
+ */
+ private final static PkgState[] PKG_STATES = { PkgState.INSTALLED, PkgState.NEW };
+
/** Process all remote packages. Returns true if something changed. */
private boolean processSource(UpdateOp op, SdkSource source, Package[] packages) {
boolean hasChanged = false;
@@ -478,7 +488,7 @@ class PackagesDiffLogic {
nextPkg: for (Package newPkg : packages) {
for (PkgCategory cat : cats) {
- for (PkgState state : PkgState.values()) {
+ for (PkgState state : PKG_STATES) {
for (Iterator<PkgItem> currItemIt = cat.getItems().iterator();
currItemIt.hasNext(); ) {
PkgItem currItem = currItemIt.next();
@@ -528,7 +538,7 @@ class PackagesDiffLogic {
}
break;
case INSTALLED:
- // if newPkg.revision <= mainPkg.revision: it's already installed, ignore.
+ // if newPkg.revision<=mainPkg.revision: it's already installed, ignore.
if (newPkg.getRevision() > mainPkg.getRevision()) {
// This is a new update for the main package.
if (currItem.mergeUpdate(newPkg)) {
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PkgItem.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PkgItem.java
index 2c8b2d2..e943819 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PkgItem.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PkgItem.java
@@ -40,8 +40,8 @@ public class PkgItem implements Comparable<PkgItem> {
* a given remote package and the local repository.
*/
public enum PkgState {
- // Implementation detail: order matters. Installed items must be dealt with before
- // new items and the order of PkgState.values() matters.
+ // Implementation detail: if this is changed then PackageDiffLogic#STATES
+ // and PackageDiffLogic#processSource() need to be changed accordingly.
/**
* Package is locally installed and may or may not have an update.
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java
index 3090884..8444f9f 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java
@@ -25,6 +25,7 @@ import com.android.sdkuilib.ui.GridDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
@@ -160,8 +161,9 @@ public final class ProgressView implements IProgressUiProvider {
// Process the app's event loop whilst we wait for the thread to finish
while (!mProgressBar.isDisposed() && t.isAlive()) {
- if (!mProgressBar.getDisplay().readAndDispatch()) {
- mProgressBar.getDisplay().sleep();
+ Display display = mProgressBar.getDisplay();
+ if (!mProgressBar.isDisposed() && !display.readAndDispatch()) {
+ display.sleep();
}
}
}
diff --git a/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogicTest.java b/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogicTest.java
index c2b320d..eafe5ac 100755
--- a/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogicTest.java
+++ b/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogicTest.java
@@ -240,7 +240,7 @@ public class PackagesDiffLogicTest extends TestCase {
new MockEmptyPackage(src1, "type1", 1)
}));
- assertFalse(m.updateEnd(true /*sortByApi*/));
+ assertTrue(m.updateEnd(true /*sortByApi*/));
assertEquals(
"PkgCategoryApi <API=TOOLS, label=Tools, #items=0>\n" +