Allow the visitor to visit a subset of its children instead of all of them.

Test Plan: Unit tests.
diff --git a/src/com/facebook/buck/android/AndroidTransitiveDependencyGraph.java b/src/com/facebook/buck/android/AndroidTransitiveDependencyGraph.java
index 4ca46c3..f8127ab 100644
--- a/src/com/facebook/buck/android/AndroidTransitiveDependencyGraph.java
+++ b/src/com/facebook/buck/android/AndroidTransitiveDependencyGraph.java
@@ -99,7 +99,7 @@
     // Visit all of the transitive dependencies to populate the above collections.
     new AbstractDependencyVisitor(rulesToTraverseForTransitiveDeps.get()) {
       @Override
-      public boolean visit(BuildRule rule) {
+      public ImmutableSet<BuildRule> visit(BuildRule rule) {
         // We need to include the transitive closure of the compiled .class files when dex'ing, as
         // well as the third-party jars that they depend on.
         // Update pathsToThirdPartyJars.
@@ -107,7 +107,7 @@
           PrebuiltJarRule prebuiltJarRule = (PrebuiltJarRule) rule;
           pathsToThirdPartyJarsBuilder.add(prebuiltJarRule.getBinaryJar());
         }
-        return rule.getProperties().is(LIBRARY);
+        return maybeVisitAllDeps(rule, rule.getProperties().is(LIBRARY));
       }
     }.start();
 
@@ -161,7 +161,7 @@
     // Visit all of the transitive dependencies to populate the above collections.
     new AbstractDependencyVisitor(rulesToTraverseForTransitiveDeps.get()) {
       @Override
-      public boolean visit(BuildRule rule) {
+      public ImmutableSet<BuildRule> visit(BuildRule rule) {
         // We need to include the transitive closure of the compiled .class files when dex'ing, as
         // well as the third-party jars that they depend on.
         // Update pathsToThirdPartyJars.
@@ -176,7 +176,7 @@
           // In the rare event that a PrebuiltNativeLibraryBuildRule has deps, it is likely another
           // NativeLibraryRule that will need to be included in the final APK, so traverse the deps.
           if (rule.getBuildable() instanceof PrebuiltNativeLibrary) {
-            return true;
+            return rule.getDeps();
           }
         } else if (rule instanceof AndroidResourceRule) {
           AndroidResourceRule androidRule = (AndroidResourceRule) rule;
@@ -197,7 +197,7 @@
             Optionals.addIfPresent(androidLibraryRule.getManifestFile(), manifestFiles);
           }
         }
-        return rule.getProperties().is(LIBRARY);
+        return maybeVisitAllDeps(rule, rule.getProperties().is(LIBRARY));
       }
     }.start();
 
diff --git a/src/com/facebook/buck/android/UberRDotJavaUtil.java b/src/com/facebook/buck/android/UberRDotJavaUtil.java
index 0d01ee2..7d82313 100644
--- a/src/com/facebook/buck/android/UberRDotJavaUtil.java
+++ b/src/com/facebook/buck/android/UberRDotJavaUtil.java
@@ -161,7 +161,7 @@
     AbstractDependencyVisitor visitor = new AbstractDependencyVisitor(buildRule) {
 
       @Override
-      public boolean visit(BuildRule rule) {
+      public ImmutableSet<BuildRule> visit(BuildRule rule) {
         if (rule instanceof HasAndroidResourceDeps) {
           HasAndroidResourceDeps androidResourceRule = (HasAndroidResourceDeps)rule;
           if (androidResourceRule.getRes() != null) {
@@ -171,7 +171,7 @@
 
         // Only certain types of rules should be considered as part of this traversal.
         BuildRuleType type = rule.getType();
-        return TRAVERSABLE_TYPES.contains(type);
+        return maybeVisitAllDeps(rule, TRAVERSABLE_TYPES.contains(type));
       }
 
     };
diff --git a/src/com/facebook/buck/command/Project.java b/src/com/facebook/buck/command/Project.java
index bcdb904..1ae7754 100644
--- a/src/com/facebook/buck/command/Project.java
+++ b/src/com/facebook/buck/command/Project.java
@@ -736,7 +736,7 @@
       private final LinkedHashSet<DependentModule> modulesToAdd = Sets.newLinkedHashSet();
 
       @Override
-      public boolean visit(BuildRule dep) {
+      public ImmutableSet<BuildRule> visit(BuildRule dep) {
         boolean depShouldExportDeps = dep.getExportDeps() || rule.getProperties().is(PACKAGING);
         boolean shouldVisitDeps = (dep.getProperties().is(LIBRARY) && (depShouldExportDeps))
             || (dep instanceof AndroidResourceRule)
@@ -786,7 +786,7 @@
           String moduleName = getIntellijNameForRule(dep);
           dependentModule = DependentModule.newModule(dep.getBuildTarget(), moduleName);
         } else if (dep.getFullyQualifiedName().startsWith(ANDROID_GEN_BUILD_TARGET_PREFIX)) {
-          return shouldVisitDeps;
+          return maybeVisitAllDeps(dep, shouldVisitDeps);
         } else if (dep instanceof JavaLibraryRule) {
           String moduleName = getIntellijNameForRule(dep);
           dependentModule = DependentModule.newModule(dep.getBuildTarget(), moduleName);
@@ -795,9 +795,9 @@
           dependentModule = DependentModule.newModule(dep.getBuildTarget(), moduleName);
         } else if (dep.getBuildable() instanceof GenAidl) {
           // This will likely be handled appropriately by the IDE's Android plugin.
-          return shouldVisitDeps;
+          return maybeVisitAllDeps(dep, shouldVisitDeps);
         } else {
-          return shouldVisitDeps;
+          return maybeVisitAllDeps(dep, shouldVisitDeps);
         }
 
         if (isForTests) {
@@ -821,7 +821,7 @@
           modulesToAdd.add(dependentModule);
         }
 
-        return shouldVisitDeps;
+        return maybeVisitAllDeps(dep, shouldVisitDeps);
       }
 
       @Override
diff --git a/src/com/facebook/buck/python/PythonBinaryRule.java b/src/com/facebook/buck/python/PythonBinaryRule.java
index a69972e..6a38113 100644
--- a/src/com/facebook/buck/python/PythonBinaryRule.java
+++ b/src/com/facebook/buck/python/PythonBinaryRule.java
@@ -88,19 +88,19 @@
     new AbstractDependencyVisitor(this) {
 
       @Override
-      public boolean visit(BuildRule rule) {
+      public ImmutableSet<BuildRule> visit(BuildRule rule) {
         Buildable buildable = rule.getBuildable();
         if (buildable instanceof PythonLibrary) {
           PythonLibrary pythonLibrary = (PythonLibrary) buildable;
 
           Path pythonPathEntry = pythonLibrary.getPythonPathDirectory();
           entries.add(pythonPathEntry);
-          return true;
+          return getDeps();
         }
 
         // AbstractDependencyVisitor will start from this (PythonBinaryRule) so make sure it
         // descends to its dependencies even though it is not a library rule.
-        return rule == pythonBinaryRule;
+        return maybeVisitAllDeps(rule, rule == pythonBinaryRule);
       }
 
     }.start();
diff --git a/src/com/facebook/buck/rules/AbstractDependencyVisitor.java b/src/com/facebook/buck/rules/AbstractDependencyVisitor.java
index cc93661..95f744b 100644
--- a/src/com/facebook/buck/rules/AbstractDependencyVisitor.java
+++ b/src/com/facebook/buck/rules/AbstractDependencyVisitor.java
@@ -16,6 +16,7 @@
 
 package com.facebook.buck.rules;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
@@ -53,14 +54,17 @@
         continue;
       }
 
-      boolean shouldVisitDeps = visit(currentRule);
+      ImmutableSet<BuildRule> depsToVisit = visit(currentRule);
       explored.add(currentRule);
 
-      if (shouldVisitDeps) {
-        for (BuildRule dep : currentRule.getDeps()) {
-          if (!explored.contains(dep)) {
-            toExplore.add(dep);
-          }
+      for (BuildRule dep : depsToVisit) {
+        Preconditions.checkState(currentRule.getDeps().contains(dep),
+            "%s said that it should visit %s, but %s is not in its deps.",
+            currentRule,
+            dep,
+            dep);
+        if (!explored.contains(dep)) {
+          toExplore.add(dep);
         }
       }
     }
@@ -74,8 +78,16 @@
   }
 
   /**
+   * To perform a full traversal of the {@code rule}'s transitive dependencies, this rule
+   * should return {@code rule.getDeps()}.
+   *
    * @param rule Visited build rule
-   * @return whether to traverse the deps of the current rule
+   * @return The set of children to visit after visiting this node.  This set must be a subset of
+   *    {@code rule.getDeps()}
    */
-  public abstract boolean visit(BuildRule rule);
+  public abstract ImmutableSet<BuildRule> visit(BuildRule rule);
+
+  public static ImmutableSet<BuildRule> maybeVisitAllDeps(BuildRule rule, boolean visitDeps) {
+    return visitDeps ? rule.getDeps() : ImmutableSet.<BuildRule>of();
+  }
 }
diff --git a/test/com/facebook/buck/rules/AbstractDependencyVisitorTest.java b/test/com/facebook/buck/rules/AbstractDependencyVisitorTest.java
index d8f2b29..09b9477 100644
--- a/test/com/facebook/buck/rules/AbstractDependencyVisitorTest.java
+++ b/test/com/facebook/buck/rules/AbstractDependencyVisitorTest.java
@@ -21,9 +21,11 @@
 import com.facebook.buck.java.FakeJavaLibraryRule;
 import com.facebook.buck.model.BuildTarget;
 import com.facebook.buck.model.BuildTargetPattern;
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
 import org.junit.Test;
@@ -60,9 +62,9 @@
     final List<BuildRule> buildRuleTraversalOrder = Lists.newArrayList();
     new AbstractDependencyVisitor(initialRule) {
       @Override
-      public boolean visit(BuildRule rule) {
+      public ImmutableSet<BuildRule> visit(BuildRule rule) {
         buildRuleTraversalOrder.add(rule);
-        return true;
+        return rule.getDeps();
       }
     }.start();
 
@@ -81,6 +83,56 @@
         buildRuleTraversalOrder);
   }
 
+  @Test
+  public void testSubsetWorks() {
+    // The dependency graph of build rules being built up is as follows:
+    //
+    //                       10
+    //                /    /   \     \
+    //              6    7       8     9
+    //            /   \        /   \     \
+    //          5       4    3       2     1
+    //
+    BuildRule rule1 = createRule("1");
+    BuildRule rule2 = createRule("2");
+    BuildRule rule3 = createRule("3");
+    BuildRule rule4 = createRule("4");
+    BuildRule rule5 = createRule("5");
+
+    BuildRule rule6 = createRule("6", rule5, rule4);
+    BuildRule rule7 = createRule("7");
+    BuildRule rule8 = createRule("8", rule3, rule2);
+    BuildRule rule9 = createRule("9", rule1);
+
+    BuildRule initialRule = createRule("10", rule6, rule7, rule8, rule9);
+
+    final List<BuildRule> buildRuleTraversalOrder = Lists.newArrayList();
+
+    // This visitor only visits dependencies whose node names are even numbers.
+    new AbstractDependencyVisitor(initialRule) {
+      @Override
+      public ImmutableSet<BuildRule> visit(BuildRule rule) {
+        buildRuleTraversalOrder.add(rule);
+        return ImmutableSet.copyOf(Iterables.filter(rule.getDeps(), new Predicate<BuildRule>() {
+          @Override
+          public boolean apply(BuildRule input) {
+            return Integer.parseInt(input.getBuildTarget().getShortName()) % 2 == 0;
+          }
+        }));
+      }
+    }.start();
+
+    assertEquals(
+        "Dependencies should be explored depth-first, only containing rules whose rule name is " +
+            "an even number",
+        ImmutableList.of(initialRule,
+            rule6,
+            rule8,
+            rule4,
+            rule2),
+        buildRuleTraversalOrder);
+  }
+
   private FakeJavaLibraryRule createRule(String name, BuildRule... deps) {
     ImmutableSet<BuildTargetPattern> visibilityPatterns = ImmutableSet.of();
     FakeJavaLibraryRule rule = new FakeJavaLibraryRule(