All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.telenav.cactus.maven.tree.ProjectTreeCache Maven / Gradle / Ivy
Go to download
The cactus-maven-plugin project codifies Telenav Open Source processes.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// © 2011-2022 Telenav, Inc.
//
// 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
//
// https://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.telenav.cactus.maven.tree;
import com.mastfrog.util.preconditions.Exceptions;
import com.telenav.cactus.git.Branches;
import com.telenav.cactus.git.GitCheckout;
import com.telenav.cactus.git.Heads;
import com.telenav.cactus.git.SubmoduleStatus;
import com.telenav.cactus.maven.model.GroupId;
import com.telenav.cactus.maven.model.Pom;
import com.telenav.cactus.scope.ProjectFamily;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static com.telenav.cactus.scope.ProjectFamily.familyOf;
/**
* Internal cache for the ProjectTree, so we don't re-execute expensive git
* operations if nothing has changed.
*
* @author Tim Boudreau
*/
final class ProjectTreeCache
{
final Map> infoForGroupAndArtifact = new ConcurrentHashMap<>();
final Map> projectsByRepository = new ConcurrentHashMap<>();
final Map checkoutForPom = new ConcurrentHashMap<>();
final Map> branches = new HashMap<>();
final Map dirty = new ConcurrentHashMap<>();
final Map dirtyIgnoring = new ConcurrentHashMap<>();
final Map allBranches = new ConcurrentHashMap<>();
final Map> branchByGroupId = new HashMap<>();
final Map detachedHeads = new ConcurrentHashMap<>();
final Set nonMavenCheckouts = new HashSet<>();
final Map remoteHeads = new HashMap<>();
final Map> checkoutsForProjectFamily = new ConcurrentHashMap<>();
final Set families = new HashSet<>();
Boolean rootIsRoot;
private final ProjectTree outer;
ProjectTreeCache(final ProjectTree outer)
{
this.outer = outer;
}
public boolean rootIsSubmoduleRoot()
{
if (rootIsRoot == null)
{
rootIsRoot = outer.root.isSubmoduleRoot();
}
return rootIsRoot;
}
public Set allProjectFamilies()
{
if (families.isEmpty())
{
allPoms().forEach(pom ->
{
families.add(ProjectFamily.familyOf(pom));
});
}
return families;
}
public Heads remoteHeads(GitCheckout checkout)
{
return remoteHeads.computeIfAbsent(checkout,
GitCheckout::remoteHeads);
}
public Set checkoutsContainingGroupId(String groupId)
{
Set all = new HashSet<>();
projectsByRepository.forEach((repo, projectSet) ->
{
for (Pom project : projectSet)
{
if (groupId.equals(project.coordinates().groupId))
{
all.add(repo);
break;
}
}
});
return all;
}
public Set checkoutsInProjectFamily(Set family)
{
switch (family.size())
{
case 0:
return new HashSet<>(allCheckouts());
case 1:
return checkoutsInProjectFamily(family.iterator().next());
default:
Set result = new HashSet<>();
for (ProjectFamily f : family)
{
result.addAll(checkoutsInProjectFamily(f));
}
return result;
}
}
public Set checkoutsInProjectFamily(ProjectFamily family)
{
return checkoutsForProjectFamily.computeIfAbsent(family,
key ->
{
Set all = new HashSet<>();
projectsByRepository.forEach((repo, projectSet) ->
{
for (Pom project : projectSet)
{
if (family.equals(ProjectFamily.fromGroupId(project
.groupId()
.text())))
{
all.add(repo);
break;
}
}
});
return all;
});
}
private static boolean containsParentFamilyOf(GroupId groupId,
Set s)
{
for (ProjectFamily p : s)
{
if (p.isParentFamilyOf(groupId))
{
return true;
}
}
return false;
}
public Set checkoutsInProjectFamilyOrChildProjectFamily(
String groupId)
{
ProjectFamily parent = ProjectFamily.fromGroupId(groupId);
Set all = new HashSet<>();
projectsByRepository.forEach((repo, projectSet) ->
{
for (Pom p : projectSet)
{
if (familyOf(p).equals(parent)
|| parent.isParentFamilyOf(p.groupId()))
{
all.add(repo);
break;
}
}
});
return all;
}
public Set checkoutsInProjectFamilyOrChildProjectFamily(
Set family)
{
Set all = new HashSet<>();
projectsByRepository.forEach((repo, projectSet) ->
{
if (family.isEmpty())
{
all.add(repo);
return;
}
for (Pom project : projectSet)
{
ProjectFamily pomFamily = ProjectFamily.familyOf(project
.groupId());
if (family.contains(pomFamily) || containsParentFamilyOf(project
.groupId(), family))
{
all.add(repo);
break;
}
}
});
return all;
}
public Set nonMavenCheckouts()
{
return Collections.unmodifiableSet(nonMavenCheckouts);
}
public boolean isDetachedHead(GitCheckout checkout)
{
return detachedHeads.computeIfAbsent(checkout,
GitCheckout::isDetachedHead);
}
public Optional mostCommonBranchForGroupId(String groupId)
{
// Cache these since they are expensive to compute
return branchByGroupId.computeIfAbsent(groupId,
this::_mostCommonBranchForGroupId);
}
private Optional _mostCommonBranchForGroupId(String groupId)
{
// Collect the number of times a branch name is used in a checkout
// we have
Map branchNameCounts = new HashMap<>();
Set seen = new HashSet<>();
// Count each checkout exactly once, if it is on a branch
checkoutForPom.forEach((pom, checkout) ->
{
// Filter out any irrelevant or already examined checkouts
if (seen.contains(checkout) || !pom.groupId().is(groupId))
{
return;
}
// If we are on a branch, collect its name and add to the number
// of times it has been seen
branchFor(checkout)
.ifPresent(branch ->
{
seen.add(checkout);
branchNameCounts.compute(branch,
(b, old) ->
{
if (old == null)
{
return 1;
}
return old + 1;
});
});
});
// If we found nothing, we're done
if (branchNameCounts.isEmpty())
{
return Optional.empty();
}
// Reverse sort the map entries by the count
List> entries = new ArrayList<>(
branchNameCounts.entrySet());
Collections.sort(entries,
(a, b) ->
{
return b.getValue().compareTo(a.getValue());
});
// And take the greatest
return Optional.of(entries.get(0).getKey());
}
public Set projectsWithin(GitCheckout checkout)
{
Set infos = projectsByRepository.get(checkout);
return infos == null
? Collections.emptySet()
: infos;
}
public Branches branches(GitCheckout checkout)
{
return allBranches.computeIfAbsent(checkout,
GitCheckout::branches);
}
public Optional checkoutFor(Pom info)
{
return Optional.ofNullable(checkoutForPom.get(info));
}
public boolean isDirty(GitCheckout checkout)
{
return dirty.computeIfAbsent(checkout,
GitCheckout::isDirty);
}
public boolean isDirtyIgnoringSubmoduleCommits(GitCheckout checkout)
{
return dirtyIgnoring.computeIfAbsent(checkout,
GitCheckout::isDirtyIgnoringModifiedSubmodules);
}
public Set allCheckouts()
{
return Collections.unmodifiableSet(projectsByRepository.keySet());
}
public Optional branchFor(GitCheckout checkout)
{
return branches.computeIfAbsent(checkout,
GitCheckout::branch);
}
public Map projectFolders()
{
Map infos = new HashMap<>();
allPoms()
.forEach(pom -> infos.put(pom.path().getParent(), pom));
return infos;
}
public Set allPoms()
{
Set set = new HashSet<>();
projectsByRepository.forEach((repo, infos) -> set.addAll(infos));
return set;
}
Optional project(String groupId, String artifactId)
{
Map map = infoForGroupAndArtifact.get(groupId);
if (map == null)
{
return Optional.empty();
}
return Optional.ofNullable(map.get(artifactId));
}
void clear()
{
infoForGroupAndArtifact.clear();
projectsByRepository.clear();
checkoutForPom.clear();
dirtyIgnoring.clear();
branches.clear();
dirty.clear();
allBranches.clear();
branchByGroupId.clear();
nonMavenCheckouts.clear();
detachedHeads.clear();
checkoutsForProjectFamily.clear();
remoteHeads.clear();
families.clear();
rootIsRoot = null;
}
synchronized void populate()
{
try
{
outer.root.allPomFilesInSubtreeParallel(this::cacheOnePomFile);
outer.root.submodules().ifPresent(statii ->
{
for (SubmoduleStatus stat : statii)
{
stat.checkout()
.filter(GitCheckout::noPomInRoot)
.ifPresent(nonMavenCheckouts::add);
}
});
}
catch (IOException ex)
{
Exceptions.chuck(ex);
}
}
private final Map repoInternTable = new ConcurrentHashMap<>();
private GitCheckout intern(GitCheckout co)
{
GitCheckout result = repoInternTable.putIfAbsent(co, co);
if (result == null)
{
result = co;
}
return result;
}
private void cacheOnePomFile(Path path)
{
// System.out.println(
// "C1 " + Thread.currentThread().getName() + "\t" + path
// .getParent().getFileName());
Pom.from(path)
.ifPresent(info ->
{
Map subcache = infoForGroupAndArtifact
.computeIfAbsent(info.groupId()
.text(),
id -> new ConcurrentHashMap<>());
subcache.put(info.coordinates().artifactId.text(), info);
GitCheckout.checkout(info.path())
.ifPresent(co ->
{
co = intern(co);
Set poms = projectsByRepository
.computeIfAbsent(co,
c -> new HashSet<>());
poms.add(info);
checkoutForPom.put(info, co);
});
});
}
Void invalidateBranches(GitCheckout co)
{
this.allBranches.remove(co);
this.dirtyIgnoring.remove(co);
this.dirty.remove(co);
this.detachedHeads.remove(co);
this.branches.remove(co);
return null;
}
}