blob: df264bbe93d3fb98e37d0c684f8bc11246b5dc11 [file] [log] [blame]
/*
* Copyright (C) 2017 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.builder.desugaring;
import com.android.annotations.NonNull;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.ArrayDeque;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
/**
* Keeps track of the type desugaring dependencies. This is required in order to determine a set of
* types that might need to be reprocessed if some type changes.
*
* <p>For every type T, this class can calculate set of types whose desugaring depends on T, by
* following transitive dependencies between types.
*
* <p>For details when a dependency between two types exists, see {@link DesugaringData}.
*/
final class TypeDependencies {
/** Map from type to types it depends on in the desugaring process. */
@NonNull private final Map<String, Set<String>> typeToDependencies = Maps.newHashMap();
/**
* Map from type to types that depend on it in the desugaring process. This field should be
* accessed only using {@link #reverseMapping()}.
*/
@NonNull private final Map<String, Set<String>> typeToDependents = Maps.newHashMap();
boolean isReverseMappingValid = false;
void add(@NonNull String dependent, @NonNull Set<String> dependencies) {
Set<String> myDependencies = typeToDependencies.getOrDefault(dependent, new HashSet<>());
myDependencies.addAll(dependencies);
typeToDependencies.put(dependent, myDependencies);
invalidateReverseMapping();
}
@NonNull
Set<String> getDependencies(@NonNull String type) {
return typeToDependencies.getOrDefault(type, ImmutableSet.of());
}
@NonNull
Set<String> getDependents(@NonNull String type) {
return reverseMapping().getOrDefault(type, ImmutableSet.of());
}
@NonNull
Set<String> getAllDependents(@NonNull String type) {
return collectNeighbours(type, this::getDependents);
}
@NonNull
Set<String> getAllDependencies(@NonNull String type) {
return collectNeighbours(type, this::getDependencies);
}
/**
* Transitively collect neighbours of {@code start} node in a graph.
*
* @param start Start node.
* @param getNeighbours A function giving the neighbours of a node.
* @return all nodes having a direct or indirect neighbour relation from {@code start} node.
* This does not include {@code start} node.
*/
private static Set<String> collectNeighbours(
@NonNull String start, @NonNull Function<String, Set<String>> getNeighbours) {
// BFS traversal: we start from start, traverse all of its neighbours, then neighbours of
// its neighbours, and so on.
Set<String> children = Sets.newHashSet();
ArrayDeque<String> dequeue = new ArrayDeque<>();
dequeue.add(start);
while (!dequeue.isEmpty()) {
String current = dequeue.removeFirst();
Set<String> neighbours = getNeighbours.apply(current);
for (String neighbour : neighbours) {
if ((!neighbour.equals(start)) && children.add(neighbour)) {
dequeue.addLast(neighbour);
}
}
}
return children;
}
private void invalidateReverseMapping() {
isReverseMappingValid = false;
}
@NonNull
private Map<String, Set<String>> reverseMapping() {
if (isReverseMappingValid) {
return typeToDependents;
}
typeToDependents.clear();
for (Map.Entry<String, Set<String>> typeToDeps : typeToDependencies.entrySet()) {
for (String dependency : typeToDeps.getValue()) {
Set<String> myDependents =
typeToDependents.getOrDefault(dependency, Sets.newHashSet());
myDependents.add(typeToDeps.getKey());
this.typeToDependents.put(dependency, myDependents);
}
}
isReverseMappingValid = true;
return typeToDependents;
}
void remove(@NonNull String removedType) {
typeToDependencies.remove(removedType);
invalidateReverseMapping();
}
}