blob: 19309eb16beac2428dcf811e70e32206d03d1f39 [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.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Keeps track of types, and paths that define them.
*
* <p>For a type, it returns paths that define it. Typically, there is only a single path, but there
* might be multiple.
*
* <p>For a path, it returns a set of types that it defines. In case of a .class file, this will be
* a single type, but for a .jar, there are multiple.
*/
final class TypePaths {
/**
* Map from path to types defined in that path. For jars this is list of types in that jar, for
* class files, this is a single type.
*/
@NonNull private final Map<Path, Set<String>> pathToTypes = Maps.newHashMap();
/**
* Map from type to paths defining that type. Each type should be defined only in one file, but
* there is no mechanism to enforce that. This field should be accessed only using {@link
* #reverseMapping()}.
*/
@NonNull private final Map<String, Set<Path>> typeToPaths = Maps.newHashMap();
boolean isReverseMappingValid = false;
void add(@NonNull Path path, @NonNull String internalName) {
Set<String> types = pathToTypes.getOrDefault(path, getNewSetForPath(path));
types.add(internalName);
pathToTypes.put(path, types);
invalidateReverseMapping();
}
@NonNull
Set<String> getTypes(@NonNull Path path) {
return pathToTypes.getOrDefault(path, ImmutableSet.of());
}
/**
* Remove path information, and a list of types that should be removed. Not all types defined in
* a path will be removed, as a single type might be defined in multiple paths.
*/
@Nullable
Set<String> remove(@NonNull Path path, @NonNull Set<Path> removedPaths) {
Set<String> allInPath = pathToTypes.remove(path);
if (allInPath == null) {
return ImmutableSet.of();
}
invalidateReverseMapping();
Set<String> toRemove = new HashSet<>(allInPath.size());
for (String type : allInPath) {
Set<Path> definedInPaths = getPaths(type);
if (Sets.difference(definedInPaths, removedPaths).isEmpty()) {
// all paths containing this type have been removed
toRemove.add(type);
}
}
return toRemove;
}
@NonNull
Set<Path> getPaths(@NonNull String internalName) {
return reverseMapping().getOrDefault(internalName, ImmutableSet.of());
}
private void invalidateReverseMapping() {
isReverseMappingValid = false;
}
@NonNull
private Map<String, Set<Path>> reverseMapping() {
if (isReverseMappingValid) {
return typeToPaths;
}
typeToPaths.clear();
for (Map.Entry<Path, Set<String>> pathToType : pathToTypes.entrySet()) {
for (String type : pathToType.getValue()) {
Set<Path> paths =
typeToPaths.getOrDefault(type, Sets.newHashSetWithExpectedSize(1));
paths.add(pathToType.getKey());
typeToPaths.put(type, paths);
}
}
isReverseMappingValid = true;
return typeToPaths;
}
@NonNull
private static Set<String> getNewSetForPath(@NonNull Path path) {
if (path.toString().endsWith(SdkConstants.DOT_CLASS)) {
return Sets.newHashSetWithExpectedSize(1);
} else {
return Sets.newHashSet();
}
}
}