aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintDeltaProcessor.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintDeltaProcessor.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintDeltaProcessor.java201
1 files changed, 201 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintDeltaProcessor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintDeltaProcessor.java
new file mode 100644
index 000000000..ebb9a591c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintDeltaProcessor.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.eclipse.adt.internal.lint;
+
+import static com.android.SdkConstants.DOT_CLASS;
+import static com.android.SdkConstants.DOT_JAVA;
+import static com.android.SdkConstants.EXT_JAVA;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AdtUtils;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor;
+import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IFileListener;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarkerDelta;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.swt.widgets.Display;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Delta processor for Java files, which runs single-file lints if it finds that
+ * the currently active file has been updated.
+ */
+public class LintDeltaProcessor implements Runnable {
+ private List<IResource> mFiles;
+ private IFile mActiveFile;
+
+ private LintDeltaProcessor() {
+ // Get the active editor file, if any
+ Display display = AdtPlugin.getDisplay();
+ if (display == null || display.isDisposed()) {
+ return;
+ }
+ if (display.getThread() != Thread.currentThread()) {
+ display.syncExec(this);
+ } else {
+ run();
+ }
+ }
+
+ /**
+ * Creates a new {@link LintDeltaProcessor}
+ *
+ * @return a visitor
+ */
+ @NonNull
+ public static LintDeltaProcessor create() {
+ return new LintDeltaProcessor();
+ }
+
+ /**
+ * Process the given delta: update lint on any Java source and class files found.
+ *
+ * @param delta the delta describing recently changed files
+ */
+ public void process(@NonNull IResourceDelta delta) {
+ if (mActiveFile == null || !mActiveFile.getName().endsWith(DOT_JAVA)) {
+ return;
+ }
+
+ mFiles = new ArrayList<IResource>();
+ gatherFiles(delta);
+
+ if (!mFiles.isEmpty()) {
+ EclipseLintRunner.startLint(mFiles, mActiveFile, null,
+ false /*fatalOnly*/, false /*show*/);
+ }
+ }
+
+ /**
+ * Process edits in the given file: update lint on the Java source provided
+ * it's the active file.
+ *
+ * @param file the file that was changed
+ */
+ public void process(@NonNull IFile file) {
+ if (mActiveFile == null || !mActiveFile.getName().endsWith(DOT_JAVA)) {
+ return;
+ }
+
+ if (file.equals(mActiveFile)) {
+ mFiles = Collections.<IResource>singletonList(file);
+ EclipseLintRunner.startLint(mFiles, mActiveFile, null,
+ false /*fatalOnly*/, false /*show*/);
+ }
+ }
+
+ /**
+ * Collect .java and .class files to be run in lint. Only collects files
+ * that match the active editor.
+ */
+ private void gatherFiles(@NonNull IResourceDelta delta) {
+ IResource resource = delta.getResource();
+ String name = resource.getName();
+ if (name.endsWith(DOT_JAVA)) {
+ if (resource.equals(mActiveFile)) {
+ mFiles.add(resource);
+ }
+ } else if (name.endsWith(DOT_CLASS)) {
+ // Make sure this class corresponds to the .java file, meaning it has
+ // the same basename, or that it is an inner class of a class that
+ // matches the same basename. (We could potentially make sure the package
+ // names match too, but it's unlikely that the class names match without a
+ // package match, and there's no harm in including some extra classes here,
+ // since lint will resolve full paths and the resource markers won't go
+ // to the wrong place, we simply end up analyzing some extra files.)
+ String className = mActiveFile.getName();
+ if (name.regionMatches(0, className, 0, className.length() - DOT_JAVA.length())) {
+ if (name.length() == className.length() - DOT_JAVA.length() + DOT_CLASS.length()
+ || name.charAt(className.length() - DOT_JAVA.length()) == '$') {
+ mFiles.add(resource);
+ }
+ }
+ } else {
+ IResourceDelta[] children = delta.getAffectedChildren();
+ if (children != null && children.length > 0) {
+ for (IResourceDelta d : children) {
+ gatherFiles(d);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void run() {
+ // Get the active file: this must be run on the GUI thread
+ mActiveFile = AdtUtils.getActiveFile();
+ }
+
+ /**
+ * Start listening to the resource monitor
+ *
+ * @param resourceMonitor the resource monitor
+ */
+ public static void startListening(@NonNull GlobalProjectMonitor resourceMonitor) {
+ // Add a file listener which finds out when files have changed. This is listening
+ // specifically for saves of Java files, in order to run incremental lint on them.
+ // Note that the {@link PostCompilerBuilder} already handles incremental lint files
+ // on Java files - and runs it for both the .java and .class files.
+ //
+ // However, if Project > Build Automatically is turned off, then the PostCompilerBuilder
+ // isn't run after a save. THAT's what the below is for: it will run and *only*
+ // run lint incrementally if build automatically is off.
+ assert sListener == null; // Should only be called once on plugin activation
+ sListener = new IFileListener() {
+ @Override
+ public void fileChanged(@NonNull IFile file,
+ @NonNull IMarkerDelta[] markerDeltas,
+ int kind, @Nullable String extension, int flags, boolean isAndroidProject) {
+ if (!isAndroidProject || flags == IResourceDelta.MARKERS) {
+ // If not an Android project or ONLY the markers changed.
+ // Ignore these since they happen
+ // when we add markers for lint errors found in the current file,
+ // which would cause us to repeatedly enter this method over and over
+ // again.
+ return;
+ }
+ if (EXT_JAVA.equals(extension)
+ && !ResourceManager.isAutoBuilding()
+ && AdtPrefs.getPrefs().isLintOnSave()) {
+ LintDeltaProcessor.create().process(file);
+ }
+ }
+ };
+ resourceMonitor.addFileListener(sListener, IResourceDelta.ADDED | IResourceDelta.CHANGED);
+ }
+
+ /**
+ * Stop listening to the resource monitor
+ *
+ * @param resourceMonitor the resource monitor
+ */
+ public static void stopListening(@NonNull GlobalProjectMonitor resourceMonitor) {
+ assert sListener != null;
+ resourceMonitor.removeFileListener(sListener);
+ sListener = null;
+ }
+
+ private static IFileListener sListener;
+}