blob: 9c19dceca7a289005393a637d740c4eb2d60c387 [file] [log] [blame]
/*
* 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<Set<Long>> connectedRawContactSets,
Map<Long, Long> rawContactsToAccounts) {
// Index to rawContactIds mapping
final Map<Integer, Set<Long>> rawContactIds = new HashMap<>();
// AccountId to indices mapping
final Map<Long, Set<Integer>> accounts = new HashMap<>();
int index = 0;
for (Set<Long> rIds : connectedRawContactSets) {
rawContactIds.put(index, rIds);
for (Long rId : rIds) {
long acctId = rawContactsToAccounts.get(rId);
Set<Integer> s = accounts.get(acctId);
if (s == null) {
s = new HashSet<Integer>();
}
s.add(index);
accounts.put(acctId, s);
}
index++;
}
connectedRawContactSets.clear();
for (Long accountId : accounts.keySet()) {
final Set<Integer> s = accounts.get(accountId);
if (s.size() > 1) {
for (Integer i : s) {
final Set<Long> rIdSet = rawContactIds.get(i);
if (rIdSet != null && !rIdSet.isEmpty()) {
connectedRawContactSets.add(rIdSet);
rawContactIds.remove(i);
}
}
}
}
final Set<Long> mergedSet = new HashSet<>();
for (Long accountId : accounts.keySet()) {
final Set<Integer> s = accounts.get(accountId);
if (s.size() == 1) {
Set<Long> ids = rawContactIds.get(Iterables.getOnlyElement(s));
if (ids != null && !ids.isEmpty()) {
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<Set<Long>> findConnectedComponents(Set<Long> rawContactIdSet, Multimap<Long,
Long> matchingRawIdPairs) {
Set<Set<Long>> connectedRawContactSets = new HashSet<>();
Set<Long> visited = new HashSet<>();
for (Long id : rawContactIdSet) {
if (!visited.contains(id)) {
Set<Long> set = new HashSet<>();
findConnectedComponentForRawContact(matchingRawIdPairs, visited, id, set);
connectedRawContactSets.add(set);
}
}
return connectedRawContactSets;
}
private static void findConnectedComponentForRawContact(Multimap<Long, Long> connections,
Set<Long> visited, Long rawContactId, Set<Long> results) {
visited.add(rawContactId);
results.add(rawContactId);
for (long match : connections.get(rawContactId)) {
if (!visited.contains(match)) {
findConnectedComponentForRawContact(connections, visited, match, results);
}
}
}
}