All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.telenav.cactus.maven.tree.ProjectTree Maven / Gradle / Ivy

There is a newer version: 1.5.49
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// © 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.function.optional.ThrowingOptional;
import com.telenav.cactus.scope.ProjectFamily;
import com.telenav.cactus.git.Branches;
import com.telenav.cactus.git.GitCheckout;
import com.telenav.cactus.git.Heads;
import com.telenav.cactus.maven.model.Pom;
import com.telenav.cactus.scope.Scope;
import java.nio.file.Files;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.maven.project.MavenProject;

/**
 *
 * @author Tim Boudreau
 */
public class ProjectTree
{

    final GitCheckout root;
    private final AtomicBoolean upToDate = new AtomicBoolean();
    private final ProjectTreeCache cache = new ProjectTreeCache(this);

    static
    {
        try
        {
            // Force this into the maven classloader as well
            Object o = ProjectTree.class.getClassLoader().loadClass(
                    "com.telenav.cactus.maven.tree.ProjectTree$1");
        }
        catch (ClassNotFoundException ex)
        {
            ex.printStackTrace(System.out);
        }
    }

    ProjectTree(GitCheckout root)
    {
        this.root = root;
    }
    
    public boolean isSubmoduleRoot(GitCheckout co) {
        return root.equals(co)
                && withCache(ProjectTreeCache::rootIsSubmoduleRoot);
    }

    public GitCheckout root()
    {
        return root;
    }

    public static ThrowingOptional from(MavenProject project)
    {
        return from(project.getBasedir().toPath());
    }

    public static ThrowingOptional from(Path fileOrFolder)
    {
        return ThrowingOptional.from(GitCheckout.checkout(fileOrFolder))
                .flatMapThrowing(GitCheckout::submoduleRoot)
                .map(ProjectTree::new);
    }

    public void invalidateCache()
    {
        if (upToDate.compareAndSet(true, false))
        {
            cache.clear();
        }
    }

    private synchronized  T withCache(Function func)
    {
        if (upToDate.compareAndSet(false, true))
        {
            cache.populate();
        }
        return func.apply(cache);
    }

    public Optional findProject(String groupId, String artifactId)
    {
        return withCache(c ->
        {
            return c.project(groupId, artifactId);
        });
    }

    public boolean areVersionsConsistent()
    {
        return allVersions().size() <= 1;
    }

    public Set allBranches(Predicate pred)
    {

        return allCheckouts().stream()
                // filter to the checkouts we want
                .filter(pred)
                // map to the branch, which may not be present
                .map(co -> co.branch().orElse(""))
                // prune those that are not on a branch
                .filter(s -> !s.isEmpty())
                .collect(Collectors.toCollection(HashSet::new));
    }

    public Set allBranches()
    {
        Set branches = new HashSet<>();
        this.allCheckouts().forEach(checkout ->
        {
            checkout.branch().ifPresent(branches::add);
        });
        return branches;
    }

    public Set allVersions()
    {
        Set result = new HashSet<>();
        allProjects().forEach(pom ->
        {
            result.add(pom.version().text());
        });
        return result;
    }

    public Set allVersions(Predicate test)
    {
        Set result = new HashSet<>();
        allProjects().forEach(pom ->
        {
            if (test.test(pom))
            {
                result.add(pom.version().text());
            }
        });
        return result;
    }

    public Set allProjects()
    {
        return withCache(ProjectTreeCache::allPoms);
    }

    public Set projectsForGroupId(String groupId)
    {
        Set result = new TreeSet<>();
        allProjects().forEach(project ->
        {
            if (groupId.equals(project.coordinates().groupId))
            {
                result.add(project);
            }
        });
        return result;
    }

    public Set projectsForFamily(ProjectFamily fam)
    {
        Set result = new TreeSet<>();
        allProjects().forEach(project ->
        {
            if (fam.equals(ProjectFamily.fromGroupId(project.groupId().text())))
            {
                result.add(project);
            }
        });
        return result;

    }

    public Map> branchesByGroupId()
    {
        return withCache(c ->
        {
            Map> result = new TreeMap<>();
            c.allPoms().forEach(pom ->
            {
                GitCheckout.checkout(pom.path()).ifPresent(checkout ->
                {
                    Set branches = result.computeIfAbsent(
                            pom.groupId().text(), g -> new TreeSet<>());
                    checkout.branches().localBranches().forEach(br ->
                    {
                        branches.add(br.trackingName());
                    });
                    checkout.branches().remoteBranches().forEach(br ->
                    {
                        branches.add(br.trackingName());
                    });
                });
            });
            return result;
        });
    }

    public Map>> projectsByBranchByGroupId(
            Predicate filter)
    {
        return withCache(c ->
        {
            Map>> result = new TreeMap<>();
            for (Pom pom : c.allPoms())
            {
                if (!filter.test(pom))
                {
                    continue;
                }
                Map> infosByBranch = result.computeIfAbsent(
                        pom.groupId().text(), id -> new TreeMap<>());
                GitCheckout.checkout(pom.path()).ifPresent(checkout ->
                {
                    c.branchFor(checkout).ifPresent(branch ->
                    {
                        Set set = infosByBranch.computeIfAbsent(branch,
                                b -> new TreeSet<>());
                        set.add(pom);
                    });
                });
            }
            return result;
        });
    }

    public Map>> projectsByGroupIdAndVersion()
    {
        Map>> result = new TreeMap<>();
        projectsByGroupId().forEach((gid, poms) ->
        {
            Map> subMap = result.computeIfAbsent(gid,
                    g -> new TreeMap<>());
            for (Pom info : poms)
            {
                Set pomSet = subMap.computeIfAbsent(info.version()
                        .text(),
                        v -> new TreeSet<>());
                pomSet.add(info);
            }
        });
        return result;
    }

    public Set groupIdsIn(GitCheckout checkout)
    {
        return withCache(c ->
        {
            return c.projectsWithin(checkout).stream().map(
                    info -> info.groupId().toString())
                    .collect(Collectors.toCollection(HashSet::new));
        });
    }

    public Map> projectsByGroupId()
    {
        Map> result = new TreeMap<>();
        allProjects().forEach(pom ->
        {
            Set set = result.computeIfAbsent(pom.groupId().text(),
                    x -> new TreeSet<>());
            set.add(pom);
        });
        return result;
    }

    public Set allProjectFolders()
    {
        return withCache(c ->
        {
            Set result = new HashSet<>();
            c.allPoms().forEach(pom -> result.add(pom.projectFolder()));
            return result;
        });
    }

    public Map> projectsByVersion(Predicate filter)
    {
        return withCache(c ->
        {
            Map> result = new TreeMap<>();
            c.allPoms().forEach(pom ->
            {
                if (filter.test(pom))
                {
                    Set infos = result.computeIfAbsent(pom.version()
                            .text(),
                            v -> new TreeSet<>());
                    infos.add(pom);
                }
            });
            return result;
        });
    }

    public Optional projectOf(Path file)
    {
        withCache(c ->
        {
            Path realFile;
            if (Files.isDirectory(file) && Files.exists(file.resolve("pom.xml")))
            {
                realFile = file.resolve("pom.xml");
            }
            else
            {
                realFile = file;
            }
            if ("pom.xml".equals(realFile.getFileName()))
            {
                for (Pom pom : c.allPoms())
                {
                    if (pom.path().equals(realFile))
                    {
                        return Optional.of(pom);
                    }
                }
            }
            List paths = new ArrayList<>();
            Map candidateItems = new HashMap<>();
            c.projectFolders().forEach((dir, pomInfo) ->
            {
                if (realFile.startsWith(dir))
                {
                    candidateItems.put(dir, pomInfo);
                    paths.add(dir);
                }
            });
            if (paths.isEmpty())
            {
                return Optional.empty();
            }
            Collections.sort(paths, (a, b) ->
            {
                // reverse sort
                return Integer.compare(b.getNameCount(), a.getNameCount());
            });
            return Optional.of(candidateItems.get(paths.get(0)));
        });
        return Optional.empty();
    }

    public GitCheckout checkoutFor(Pom info)
    {
        return withCache(c -> c.checkoutForPom.get(info));
    }

    public Set allCheckouts()
    {
        return withCache(ProjectTreeCache::allCheckouts);
    }

    public Optional branchFor(GitCheckout checkout)
    {
        return withCache(c -> c.branchFor(checkout));
    }

    public boolean isDirty(GitCheckout checkout)
    {
        return withCache(c -> c.isDirty(checkout));
    }
    
    public boolean isDirtyIgnoringSubmoduleCommits(GitCheckout checkout)
    {
        return withCache(c -> c.isDirtyIgnoringSubmoduleCommits(checkout));
    }
    

    public Set checkoutsFor(Collection infos)
    {
        return withCache(c ->
        {
            Set result = new TreeSet<>();
            infos.forEach(pom -> c.checkoutFor(pom).ifPresent(result::add));
            return result;
        });
    }

    public Branches branches(GitCheckout checkout)
    {
        return withCache(c -> c.branches(checkout));
    }

    public Optional mostCommonBranchForGroupId(String groupId)
    {
        return withCache(c -> c.mostCommonBranchForGroupId(groupId));
    }

    public boolean isDetachedHead(GitCheckout checkout)
    {
        return withCache(c -> c.isDetachedHead(checkout));
    }

    public Set projectsWithin(GitCheckout checkout)
    {
        return withCache(c -> c.projectsWithin(checkout));
    }

    public Set nonMavenCheckouts()
    {
        return withCache(ProjectTreeCache::nonMavenCheckouts);
    }

    public Set checkoutsContainingGroupId(String groupId)
    {
        return withCache(c -> c.checkoutsContainingGroupId(groupId));
    }

    public Set checkoutsInProjectFamily(Set family)
    {
        return withCache(c -> c.checkoutsInProjectFamily(family));
    }

    public Set checkoutsInProjectFamilyOrChildProjectFamily(
            String gid, Set family)
    {
        return withCache(c -> c.checkoutsInProjectFamilyOrChildProjectFamily(
                gid));
    }

    public Set checkoutsInProjectFamily(ProjectFamily family)
    {
        return withCache(c -> c.checkoutsInProjectFamily(family));
    }

    public Heads remoteHeads(GitCheckout checkout)
    {
        return withCache(c -> c.remoteHeads(checkout));
    }
    
    public Set allProjectFamilies() {
        return withCache(ProjectTreeCache::allProjectFamilies);
    }
    
    public void invalidateBranches(GitCheckout co) {
        withCache(cache -> cache.invalidateBranches(co));
    }

    /**
     * Get a depth-first list of checkouts matching this scope, given the passed
     * contextual criteria.
     *
     * @param scope A scope
     * @param callingProjectsCheckout The checkout of the a mojo is currently
     * being run against.
     * @param includeRoot If true, include the root (submodule parent) checkout
     * in the returned list regardless of whether it directly contains a maven
     * project matching the other criteria (needed for operations that change
     * the head commit of a submodule, which will generate modifications in the
     * submodule parent project.
     * @param callingProjectsGroupId The group id of the project whose mojo is
     * being invoked
     * @return A list of checkouts
     */
    public List matchCheckouts(Scope scope,
            GitCheckout callingProjectsCheckout, boolean includeRoot,
            Set family, String callingProjectsGroupId)
    {
        Set checkouts;
        switch (scope)
        {
            case FAMILY:
                checkouts = checkoutsInProjectFamily(family);
                break;
            case FAMILY_OR_CHILD_FAMILY:
                checkouts = checkoutsInProjectFamilyOrChildProjectFamily(
                        callingProjectsGroupId,
                        family);
                break;
            case SAME_GROUP_ID:
                checkouts = checkoutsContainingGroupId(
                        callingProjectsGroupId);
                break;
            case JUST_THIS:
                checkouts = new HashSet<>(Arrays.asList(callingProjectsCheckout));
                break;
            case ALL_PROJECT_FAMILIES:
                checkouts = new HashSet<>(allCheckouts());
                break;
            case ALL:
                checkouts = new HashSet<>(allCheckouts());
                checkouts.addAll(nonMavenCheckouts());
                break;
            default:
                throw new AssertionError(this);
        }
        checkouts = new LinkedHashSet<>(checkouts);
        if (!includeRoot)
        {
            callingProjectsCheckout.submoduleRoot().ifPresent(checkouts::remove);
        }
        else
        {
            if (!checkouts.isEmpty()) // don't generate a push of _just_ the root checkout
            {
                callingProjectsCheckout.submoduleRoot()
                        .ifPresent(checkouts::add);
            }
        }
        return GitCheckout.depthFirstSort(checkouts);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy