From 9c0c6a5c0505bd8cc9de4d0a06f03a0964bdd94e Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 21 Jan 2020 15:35:11 +0900 Subject: [PATCH] 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 --- .../android/net/util/LinkPropertiesUtils.java | 56 +++++++++++++++++ .../net/util/LinkPropertiesUtilsTest.java | 62 ++++++++++++++++++- 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/staticlibs/src_frameworkcommon/android/net/util/LinkPropertiesUtils.java b/staticlibs/src_frameworkcommon/android/net/util/LinkPropertiesUtils.java index 59d88accc8..c50ca0b650 100644 --- a/staticlibs/src_frameworkcommon/android/net/util/LinkPropertiesUtils.java +++ b/staticlibs/src_frameworkcommon/android/net/util/LinkPropertiesUtils.java @@ -26,8 +26,10 @@ import android.text.TextUtils; 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. @@ -64,6 +66,60 @@ public final class LinkPropertiesUtils { } } + /** + * 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 A class that represents the key of the items to be compared. + * @param The class that represents the object to be compared. + */ + public static class CompareOrUpdateResult { + public final List added = new ArrayList<>(); + public final List removed = new ArrayList<>(); + public final List 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 oldItems, Collection newItems, + Function keyCalculator) { + HashMap 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 6e06ee877b..e2cde34a98 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.LinkAddress; 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.Test; 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 @@ public final class LinkPropertiesUtilsTest { assertEquals(linkAddr1, results.removed.get(0)); assertEquals(linkAddr2, results.added.get(0)); } -} + + private void assertSameElements(List expected, List actual) { + HashSet expectedSet = new HashSet(expected); + assertEquals("expected list contains duplicates", expectedSet.size(), expected.size()); + HashSet actualSet = new HashSet(actual); + assertEquals("actual list contains duplicates", actualSet.size(), actual.size()); + assertEquals(expectedSet, actualSet); + } + + private void assertCompareOrUpdateResult(CompareOrUpdateResult result, + List expectedAdded, List expectedRemoved, + List expectedUpdated) { + assertSameElements(expectedAdded, result.added); + assertSameElements(expectedRemoved, result.removed); + assertSameElements(expectedUpdated, result.updated); + } + + private List 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 extractPrefix = (s) -> s.replaceFirst("^([a-z]+).*", "$1"); + assertEquals("goodbye", extractPrefix.apply("goodbye1234")); + + List oldItems = strArray("hello123", "goodbye5678", "howareyou669"); + List newItems = strArray("hello123", "goodbye000", "verywell"); + + final List emptyList = new ArrayList<>(); + + // Items -> empty: everything removed. + CompareOrUpdateResult result = + new CompareOrUpdateResult(oldItems, emptyList, extractPrefix); + assertCompareOrUpdateResult(result, + emptyList, strArray("hello123", "howareyou669", "goodbye5678"), emptyList); + + // Empty -> items: everything added. + result = new CompareOrUpdateResult(emptyList, newItems, extractPrefix); + assertCompareOrUpdateResult(result, + strArray("hello123", "goodbye000", "verywell"), emptyList, emptyList); + + // Empty -> empty: no change. + result = new CompareOrUpdateResult(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