Add a CompareOrUpdateResult class.
This is like CompareResult, but also has the concept of an item
being "updated" (i.e., the same, but with different properties,
in the old and the new list of items). This is needed to support
updating routes.
Bug: 142892223
Test: new unit test
Change-Id: Ide5e31acb9baa6a5d3f50e9965d4019eb7c82cd4
diff --git a/staticlibs/src_frameworkcommon/android/net/util/LinkPropertiesUtils.java b/staticlibs/src_frameworkcommon/android/net/util/LinkPropertiesUtils.java
index 59d88ac..c50ca0b 100644
--- a/staticlibs/src_frameworkcommon/android/net/util/LinkPropertiesUtils.java
+++ b/staticlibs/src_frameworkcommon/android/net/util/LinkPropertiesUtils.java
@@ -26,8 +26,10 @@
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
import java.util.Objects;
+import java.util.function.Function;
/**
* Collection of link properties utilities.
@@ -65,6 +67,60 @@
}
/**
+ * Generic class to compare two lists of items of type {@code T} whose properties can change.
+ * The items to be compared must provide a way to calculate a corresponding key of type
+ * {@code K} such that if (and only if) an old and a new item have the same key, then the new
+ * item is an update of the old item. Both the old list and the new list may not contain more
+ * than one item with the same key, and may not contain any null items.
+ *
+ * @param <K> A class that represents the key of the items to be compared.
+ * @param <T> The class that represents the object to be compared.
+ */
+ public static class CompareOrUpdateResult<K, T> {
+ public final List<T> added = new ArrayList<>();
+ public final List<T> removed = new ArrayList<>();
+ public final List<T> updated = new ArrayList<>();
+
+ /**
+ * Compares two lists of items.
+ * @param oldItems the old list of items.
+ * @param newItems the new list of items.
+ * @param keyCalculator a {@link Function} that calculates an item's key.
+ */
+ public CompareOrUpdateResult(Collection<T> oldItems, Collection<T> newItems,
+ Function<T, K> keyCalculator) {
+ HashMap<K, T> updateTracker = new HashMap<>();
+
+ for (T oldItem : oldItems) {
+ updateTracker.put(keyCalculator.apply(oldItem), oldItem);
+ }
+
+ for (T newItem : newItems) {
+ T oldItem = updateTracker.remove(keyCalculator.apply(newItem));
+ if (oldItem != null) {
+ if (!oldItem.equals(newItem)) {
+ // Update of existing item.
+ updated.add(newItem);
+ }
+ } else {
+ // New item.
+ added.add(newItem);
+ }
+ }
+
+ removed.addAll(updateTracker.values());
+ }
+
+ @Override
+ public String toString() {
+ return "removed=[" + TextUtils.join(",", removed)
+ + "] added=[" + TextUtils.join(",", added)
+ + "] updated=[" + TextUtils.join(",", updated)
+ + "]";
+ }
+ }
+
+ /**
* Compares the addresses in {@code left} LinkProperties with {@code right}
* LinkProperties, examining only addresses on the base link.
*
diff --git a/staticlibs/tests/unit/src/android/net/util/LinkPropertiesUtilsTest.java b/staticlibs/tests/unit/src/android/net/util/LinkPropertiesUtilsTest.java
index 6e06ee8..e2cde34 100644
--- a/staticlibs/tests/unit/src/android/net/util/LinkPropertiesUtilsTest.java
+++ b/staticlibs/tests/unit/src/android/net/util/LinkPropertiesUtilsTest.java
@@ -26,6 +26,7 @@
import android.net.LinkProperties;
import android.net.ProxyInfo;
import android.net.RouteInfo;
+import android.net.util.LinkPropertiesUtils.CompareOrUpdateResult;
import android.net.util.LinkPropertiesUtils.CompareResult;
import androidx.test.runner.AndroidJUnit4;
@@ -34,6 +35,11 @@
import org.junit.runner.RunWith;
import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.function.Function;
@RunWith(AndroidJUnit4.class)
public final class LinkPropertiesUtilsTest {
@@ -185,4 +191,58 @@
assertEquals(linkAddr1, results.removed.get(0));
assertEquals(linkAddr2, results.added.get(0));
}
-}
+
+ private void assertSameElements(List<String> expected, List<String> actual) {
+ HashSet<String> expectedSet = new HashSet(expected);
+ assertEquals("expected list contains duplicates", expectedSet.size(), expected.size());
+ HashSet<String> actualSet = new HashSet(actual);
+ assertEquals("actual list contains duplicates", actualSet.size(), actual.size());
+ assertEquals(expectedSet, actualSet);
+ }
+
+ private void assertCompareOrUpdateResult(CompareOrUpdateResult result,
+ List<String> expectedAdded, List<String> expectedRemoved,
+ List<String> expectedUpdated) {
+ assertSameElements(expectedAdded, result.added);
+ assertSameElements(expectedRemoved, result.removed);
+ assertSameElements(expectedUpdated, result.updated);
+ }
+
+ private List<String> strArray(String... strs) {
+ return Arrays.asList(strs);
+ }
+
+ @Test
+ public void testCompareOrUpdateResult() {
+ // As the item type, use a simple string. An item is defined to be an update of another item
+ // if the string starts with the same alphabetical characters.
+ // Extracting the key from the object is just a regexp.
+ Function<String, String> extractPrefix = (s) -> s.replaceFirst("^([a-z]+).*", "$1");
+ assertEquals("goodbye", extractPrefix.apply("goodbye1234"));
+
+ List<String> oldItems = strArray("hello123", "goodbye5678", "howareyou669");
+ List<String> newItems = strArray("hello123", "goodbye000", "verywell");
+
+ final List<String> emptyList = new ArrayList<>();
+
+ // Items -> empty: everything removed.
+ CompareOrUpdateResult<String, String> result =
+ new CompareOrUpdateResult<String, String>(oldItems, emptyList, extractPrefix);
+ assertCompareOrUpdateResult(result,
+ emptyList, strArray("hello123", "howareyou669", "goodbye5678"), emptyList);
+
+ // Empty -> items: everything added.
+ result = new CompareOrUpdateResult<String, String>(emptyList, newItems, extractPrefix);
+ assertCompareOrUpdateResult(result,
+ strArray("hello123", "goodbye000", "verywell"), emptyList, emptyList);
+
+ // Empty -> empty: no change.
+ result = new CompareOrUpdateResult<String, String>(newItems, newItems, extractPrefix);
+ assertCompareOrUpdateResult(result, emptyList, emptyList, emptyList);
+
+ // Added, removed, updated at the same time.
+ result = new CompareOrUpdateResult<>(oldItems, newItems, extractPrefix);
+ assertCompareOrUpdateResult(result,
+ strArray("verywell"), strArray("howareyou669"), strArray("goodbye000"));
+ }
+}
\ No newline at end of file