Baku - Usability improvements

RecyclerView is now a compile dependency rather than provided. Since the
unit of classloading is a class, RecyclerView is required whenever a
collection binding builder is used, even if used to bind to a
ListView. It's thus still not always required, but this requirement
would be surprising to a developer, and it's better to let ProGuard
strip it out later if possible.

Fixed a case where the type requirement on a prefix binding builder
would throw NPE instead of IllegalStateException.

Prefix binding builder and associated adapters now take Comparator
rather than Ordering (which implements Comparator).

Change-Id: I53c6991e265951b1f1634d228fcb8b4e5330992e
diff --git a/baku-toolkit/build.gradle b/baku-toolkit/build.gradle
index 6f8507c..62608d7 100644
--- a/baku-toolkit/build.gradle
+++ b/baku-toolkit/build.gradle
@@ -6,11 +6,13 @@
         mavenCentral()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:1.5.0'
-        classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
-        classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.+'
-        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6'
-        classpath 'me.tatarka:gradle-retrolambda:3.2.4'
+        classpath (
+                'com.android.tools.build:gradle:1.5.0',
+                'com.github.dcendents:android-maven-gradle-plugin:1.3',
+                'com.jakewharton.sdkmanager:gradle-plugin:0.12.0',
+                'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6',
+                'me.tatarka:gradle-retrolambda:3.2.4'
+        )
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
diff --git a/baku-toolkit/clean-cloudsync b/baku-toolkit/clean-cloudsync
index 87dd8d3..deadc90 100755
--- a/baku-toolkit/clean-cloudsync
+++ b/baku-toolkit/clean-cloudsync
@@ -5,7 +5,7 @@
 
 
 if [ ${host-} ]; then
-    ssh="/usr/bin/ssh -i /home/$USER/.ssh/google_compute_engine $host"
+    ssh="/usr/bin/ssh -i $HOME/.ssh/google_compute_engine $host"
 
     fullinstallation=`$ssh ls /tmp/dmrun/dm/dmroot/app'*'`
     installation=${fullinstallation:13}
diff --git a/baku-toolkit/lib/build.gradle b/baku-toolkit/lib/build.gradle
index 7829abf..fafc703 100644
--- a/baku-toolkit/lib/build.gradle
+++ b/baku-toolkit/lib/build.gradle
@@ -39,7 +39,6 @@
 
 dependencies {
     provided(
-            'com.android.support:recyclerview-v7:23.0.1',
             /*
             https://projectlombok.org/setup/android.html
             Follow Android Studio instructions at the bottom of the page to install the Lombok
@@ -64,6 +63,12 @@
     }
 
     compile(
+            /*
+            Ideally this would be optional, but this unexpectedly becomes required if you touch
+            the Baku CollectionBinding Builder, even if you don't actually try to bind to a
+            RecyclerView.
+             */
+            'com.android.support:recyclerview-v7:23.0.1',
             'com.jakewharton.rxbinding:rxbinding:0.3.0',
             'commons-io:commons-io:2.4',
             'io.reactivex:rxandroid:1.1.0',
diff --git a/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/CollectionAdapterBuilder.java b/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/CollectionAdapterBuilder.java
index d11f93a..0c4d7b2 100644
--- a/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/CollectionAdapterBuilder.java
+++ b/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/CollectionAdapterBuilder.java
@@ -4,6 +4,7 @@
 
 package io.v.baku.toolkit.bind;
 
+import android.support.annotation.IdRes;
 import android.support.v7.widget.RecyclerView;
 import android.view.View;
 import android.widget.ListView;
@@ -88,4 +89,12 @@
             throw new IllegalArgumentException("No default binding for view " + view);
         }
     }
+
+    /**
+     * Binds to the view identified by {@code viewId}.
+     * @see #bindTo(View)
+     */
+    public RangeAdapter bindTo(final @IdRes int viewId) {
+        return bindTo(mBase.mActivity.findViewById(viewId));
+    }
 }
diff --git a/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/PrefixBindingBuilder.java b/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/PrefixBindingBuilder.java
index b1085f3..600c3f5 100644
--- a/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/PrefixBindingBuilder.java
+++ b/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/PrefixBindingBuilder.java
@@ -6,6 +6,8 @@
 
 import com.google.common.collect.Ordering;
 
+import java.util.Comparator;
+
 import io.v.rx.syncbase.RangeWatchBatch;
 import io.v.rx.syncbase.RxTable;
 import io.v.v23.syncbase.nosql.PrefixRange;
@@ -21,7 +23,7 @@
         extends CollectionAdapterBuilder<PrefixBindingBuilder<T, A>, RxTable.Row<T>, A> {
     private Class<T> mType;
     private PrefixRange mPrefix;
-    private Ordering<? super RxTable.Row<T>> mOrdering;
+    private Comparator<? super RxTable.Row<T>> mOrdering;
     private Func1<String, Boolean> mKeyFilter;
 
     public PrefixBindingBuilder(final CollectionBinding.Builder base) {
@@ -38,9 +40,11 @@
     }
 
     /**
+     * The element type for the collection, i.e. the value type for rows matching the prefix and key
+     * filter.
+     * <p>
      * This setter is minimally typesafe; after setting the {@code type}, clients should
-     * probably also update {@code ordering} and {@code viewAdapter}. If intending to use a
-     * collection binding that requires a
+     * probably also update {@code ordering} and {@code viewAdapter}.
      */
     public <U> PrefixBindingBuilder<U, A> type(final Class<U> type) {
         @SuppressWarnings("unchecked")
@@ -68,12 +72,19 @@
         return new TextViewAdapter(mBase.getDefaultViewAdapterContext()).map(RxTable.Row::getValue);
     }
 
+    private Class<T> getType() {
+        if (mType == null) {
+            throw new IllegalStateException("Missing required type property");
+        }
+        return mType;
+    }
+
     /**
      * For comparable {@code T}, default to natural ordering on values. Otherwise, default to
      * natural ordering on row names.
      */
     private Ordering<? super RxTable.Row<? extends T>> getDefaultOrdering() {
-        if (mOrdering == null && Comparable.class.isAssignableFrom(mType)) {
+        if (mOrdering == null && Comparable.class.isAssignableFrom(getType())) {
             return Ordering.natural().onResultOf(r -> (Comparable) r.getValue());
         } else {
             return Ordering.natural().onResultOf(RxTable.Row::getRowName);
@@ -86,14 +97,11 @@
     }
 
     public Observable<RangeWatchBatch<T>> buildPrefixWatch() {
-        if (mType == null) {
-            throw new IllegalStateException("Missing required type property");
-        }
         return mBase.mRxTable.watch(mPrefix == null? RowRange.prefix("") : mPrefix,
-                mKeyFilter, mType);
+                mKeyFilter, getType());
     }
 
-    private Ordering<? super RxTable.Row<T>> getOrdering() {
+    private Comparator<? super RxTable.Row<T>> getOrdering() {
         return mOrdering == null ? getDefaultOrdering() : mOrdering;
     }
 
diff --git a/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/PrefixListAccumulator.java b/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/PrefixListAccumulator.java
index 9bd3c57..117fc87 100644
--- a/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/PrefixListAccumulator.java
+++ b/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/PrefixListAccumulator.java
@@ -10,6 +10,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.ConcurrentModificationException;
 import java.util.HashMap;
 import java.util.List;
@@ -35,13 +36,17 @@
 
     private final Map<String, T> mRows = new HashMap<>();
     private final List<RxTable.Row<T>> mSorted = new ArrayList<>();
-    private final Ordering<? super RxTable.Row<T>> mOrdering;
+    private final Comparator<? super RxTable.Row<T>> mOrdering;
 
-    public PrefixListAccumulator(final Ordering<? super RxTable.Row<T>> ordering) {
+    public PrefixListAccumulator(final Comparator<? super RxTable.Row<T>> ordering) {
         // ensure deterministic ordering by always applying secondary order on row name
-        mOrdering = ordering.compound(Ordering.natural().onResultOf(RxTable.Row::getRowName));
+        mOrdering = Ordering.from(ordering).compound(
+                Ordering.natural().onResultOf(RxTable.Row::getRowName));
     }
 
+    /**
+     * The generic wildcard is for the benefit of subclass overrides.
+     */
     public Observable<? extends PrefixListAccumulator<T>> scanFrom(
             final Observable<RangeWatchBatch<T>> watch) {
         return watch
diff --git a/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/PrefixListDeltaAccumulator.java b/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/PrefixListDeltaAccumulator.java
index d804799..7d5a232 100644
--- a/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/PrefixListDeltaAccumulator.java
+++ b/baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/PrefixListDeltaAccumulator.java
@@ -6,7 +6,7 @@
 
 import android.support.v7.widget.RecyclerView;
 
-import com.google.common.collect.Ordering;
+import java.util.Comparator;
 
 import io.v.rx.syncbase.RangeWatchBatch;
 import io.v.rx.syncbase.RxTable;
@@ -24,7 +24,7 @@
     private Consumer<RecyclerView.Adapter> mDeltas;
     private final NumericIdMapper mIds = new NumericIdMapper();
 
-    public PrefixListDeltaAccumulator(final Ordering<? super RxTable.Row<T>> ordering) {
+    public PrefixListDeltaAccumulator(final Comparator<? super RxTable.Row<T>> ordering) {
         super(ordering);
     }
 
diff --git a/baku-toolkit/lib/src/test/java/io/v/baku/toolkit/bind/PrefixBindingBuilderTest.java b/baku-toolkit/lib/src/test/java/io/v/baku/toolkit/bind/PrefixBindingBuilderTest.java
new file mode 100644
index 0000000..15757d2
--- /dev/null
+++ b/baku-toolkit/lib/src/test/java/io/v/baku/toolkit/bind/PrefixBindingBuilderTest.java
@@ -0,0 +1,18 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.baku.toolkit.bind;
+
+import android.widget.ListView;
+
+import org.junit.Test;
+
+public class PrefixBindingBuilderTest {
+    @Test(expected = IllegalStateException.class)
+    public void testMissingType() {
+        CollectionBinding.builder()
+                .onPrefix("foo")
+                .bindTo((ListView)null);
+    }
+}