/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * 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.providers.contacts.aggregation.util; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * Helper class for contacts aggregation. */ public class ContactAggregatorHelper { private ContactAggregatorHelper() {} /** * If two connected components have disjoint accounts, merge them. * If there is any uncertainty, keep them separate. */ @VisibleForTesting public static void mergeComponentsWithDisjointAccounts(Set> connectedRawContactSets, Map rawContactsToAccounts) { // Index to rawContactIds mapping final Map> rawContactIds = new HashMap<>(); // AccountId to indices mapping final Map> accounts = new HashMap<>(); int index = 0; for (Set rIds : connectedRawContactSets) { rawContactIds.put(index, rIds); for (Long rId : rIds) { long acctId = rawContactsToAccounts.get(rId); Set s = accounts.get(acctId); if (s == null) { s = new HashSet(); } s.add(index); accounts.put(acctId, s); } index++; } final Set mergedSet = new HashSet<>(); connectedRawContactSets.clear(); for (Long accountId : accounts.keySet()) { final Set s = accounts.get(accountId); if (s.size() > 1) { for (Integer i : s) { final Set rIdSet = rawContactIds.get(i); if (rIdSet != null) { connectedRawContactSets.add(rawContactIds.get(i)); rawContactIds.remove(i); } } } else { Set ids = rawContactIds.get(Iterables.getOnlyElement(s)); if (ids != null) { mergedSet.addAll(ids); } } } connectedRawContactSets.add(mergedSet); } /** * Given a set of raw contact ids {@code rawContactIdSet} and the connection among them * {@code matchingRawIdPairs}, find the connected components. */ @VisibleForTesting public static Set> findConnectedComponents(Set rawContactIdSet, Multimap matchingRawIdPairs) { Set> connectedRawContactSets = new HashSet<>(); Set visited = new HashSet<>(); for (Long id : rawContactIdSet) { if (!visited.contains(id)) { Set set = new HashSet<>(); findConnectedComponentForRawContact(matchingRawIdPairs, visited, id, set); connectedRawContactSets.add(set); } } return connectedRawContactSets; } private static void findConnectedComponentForRawContact(Multimap connections, Set visited, Long rawContactId, Set results) { visited.add(rawContactId); results.add(rawContactId); for (long match : connections.get(rawContactId)) { if (!visited.contains(match)) { findConnectedComponentForRawContact(connections, visited, match, results); } } } }