in java/src/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculator.java [216:356]
private void calculateJavaSourceDirectories(
BlazeContext context,
WorkspaceRoot workspaceRoot,
ArtifactLocationDecoder artifactLocationDecoder,
WorkspacePath directoryRoot,
Collection<SourceArtifact> javaArtifacts,
Collection<JavaPackageReader> javaPackageReaders,
Collection<BlazeSourceDirectory> result) {
List<SourceRoot> sourceRootsPerFile = Lists.newArrayList();
// Get java sources
List<ListenableFuture<SourceRoot>> sourceRootFutures = Lists.newArrayList();
for (final SourceArtifact sourceArtifact : javaArtifacts) {
ListenableFuture<SourceRoot> future =
executorService.submit(
() ->
sourceRootForJavaSource(
context, artifactLocationDecoder, sourceArtifact, javaPackageReaders));
sourceRootFutures.add(future);
}
try {
for (SourceRoot sourceRoot : Futures.allAsList(sourceRootFutures).get()) {
if (sourceRoot != null) {
sourceRootsPerFile.add(sourceRoot);
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
context.setCancelled();
} catch (ExecutionException e) {
throw new IllegalStateException("Could not read sources", e);
}
// Sort source roots into their respective directories
Map<WorkspacePath, Multiset<SourceRoot>> sourceDirectoryToSourceRoots = new HashMap<>();
for (SourceRoot sourceRoot : sourceRootsPerFile) {
sourceDirectoryToSourceRoots
.computeIfAbsent(sourceRoot.workspacePath, k -> HashMultiset.create())
.add(sourceRoot);
}
// Create a mapping from directory to package prefix
Map<WorkspacePath, SourceRoot> workspacePathToSourceRoot = Maps.newHashMap();
for (WorkspacePath workspacePath : sourceDirectoryToSourceRoots.keySet()) {
Multiset<SourceRoot> sources = sourceDirectoryToSourceRoots.get(workspacePath);
Multiset<String> packages = HashMultiset.create();
for (Multiset.Entry<SourceRoot> entry : sources.entrySet()) {
packages.setCount(entry.getElement().packagePrefix, entry.getCount());
}
final String directoryPackagePrefix;
// Common case -- all source files agree on a single package
if (packages.elementSet().size() == 1) {
directoryPackagePrefix = packages.elementSet().iterator().next();
} else {
String preferredPackagePrefix = PackagePrefixCalculator.packagePrefixOf(workspacePath);
directoryPackagePrefix = pickMostFrequentlyOccurring(packages, preferredPackagePrefix);
}
SourceRoot candidateRoot = new SourceRoot(workspacePath, directoryPackagePrefix);
workspacePathToSourceRoot.put(workspacePath, candidateRoot);
}
// First, create a graph of the directory structure from root to each source file
Map<WorkspacePath, SourceRootDirectoryNode> sourceRootDirectoryNodeMap = Maps.newHashMap();
SourceRootDirectoryNode rootNode = new SourceRootDirectoryNode(directoryRoot, null);
sourceRootDirectoryNodeMap.put(directoryRoot, rootNode);
for (SourceRoot sourceRoot : workspacePathToSourceRoot.values()) {
final String sourcePathRelativeToDirectoryRoot =
sourcePathRelativeToDirectoryRoot(directoryRoot, sourceRoot.workspacePath);
List<String> pathComponents =
!Strings.isNullOrEmpty(sourcePathRelativeToDirectoryRoot)
? PATH_SPLITTER.splitToList(sourcePathRelativeToDirectoryRoot)
: ImmutableList.of();
SourceRootDirectoryNode previousNode = rootNode;
for (int i = 0; i < pathComponents.size(); ++i) {
final WorkspacePath workspacePath =
getWorkspacePathFromPathComponents(directoryRoot, pathComponents, i + 1);
SourceRootDirectoryNode node = sourceRootDirectoryNodeMap.get(workspacePath);
if (node == null) {
node = new SourceRootDirectoryNode(workspacePath, pathComponents.get(i));
sourceRootDirectoryNodeMap.put(workspacePath, node);
previousNode.children.add(node);
}
previousNode = node;
}
}
// Add package prefix votes at each directory node
for (SourceRoot sourceRoot : workspacePathToSourceRoot.values()) {
final String sourcePathRelativeToDirectoryRoot =
sourcePathRelativeToDirectoryRoot(directoryRoot, sourceRoot.workspacePath);
List<String> packageComponents = PACKAGE_SPLITTER.splitToList(sourceRoot.packagePrefix);
List<String> pathComponents =
!Strings.isNullOrEmpty(sourcePathRelativeToDirectoryRoot)
? PATH_SPLITTER.splitToList(sourcePathRelativeToDirectoryRoot)
: ImmutableList.of();
int packageIndex = packageComponents.size();
int pathIndex = pathComponents.size();
while (pathIndex >= 0 && packageIndex >= 0) {
final WorkspacePath workspacePath =
getWorkspacePathFromPathComponents(directoryRoot, pathComponents, pathIndex);
SourceRootDirectoryNode node = sourceRootDirectoryNodeMap.get(workspacePath);
String packagePrefix = PACKAGE_JOINER.join(packageComponents.subList(0, packageIndex));
// If this is the source root containing Java files, we *have* to pick its package prefix
// Otherwise just add a vote
if (sourceRoot.workspacePath.equals(workspacePath)) {
node.forcedPackagePrefix = packagePrefix;
} else {
node.packagePrefixVotes.add(packagePrefix);
}
String pathComponent = pathIndex > 0 ? pathComponents.get(pathIndex - 1) : "";
String packageComponent = packageIndex > 0 ? packageComponents.get(packageIndex - 1) : "";
if (!pathComponent.equals(packageComponent)) {
break;
}
--packageIndex;
--pathIndex;
}
}
Map<WorkspacePath, SourceRoot> sourceRoots = Maps.newHashMap();
SourceRootDirectoryNode root = sourceRootDirectoryNodeMap.get(directoryRoot);
visitDirectoryNode(sourceRoots, root, null);
for (SourceRoot sourceRoot : sourceRoots.values()) {
result.add(
BlazeSourceDirectory.builder(workspaceRoot.fileForPath(sourceRoot.workspacePath))
.setPackagePrefix(sourceRoot.packagePrefix)
.setGenerated(false)
.build());
}
}