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.refactoring.PomCategorizer Maven / Gradle / Ivy
Go to download
API for manipulating versions consistently across large trees
of maven projects with an eye to ensuring the result is consistent
and buildable.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// © 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.refactoring;
import com.mastfrog.function.optional.ThrowingOptional;
import com.telenav.cactus.maven.model.MavenCoordinates;
import com.telenav.cactus.maven.model.ParentMavenCoordinates;
import com.telenav.cactus.maven.model.Pom;
import com.telenav.cactus.maven.model.resolver.Poms;
import com.telenav.cactus.scope.ProjectFamily;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.EnumSet;
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.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static com.telenav.cactus.maven.refactoring.PomRole.*;
import static com.telenav.cactus.scope.ProjectFamily.familyOf;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
/**
* Infers roles for POMs based on their type, parent, whether they contain
* modules and similar, in order to make update decisions based on their role in
* the meta-project.
*
* @author Tim Boudreau
*/
public final class PomCategorizer
{
private final Map> pomsForKind = new EnumMap<>(
PomRole.class);
private final Map> rolesForPom = new HashMap<>();
private final Map> pomsForFamily = new HashMap<>();
private final Map>> pomsForValueOfProperty = new HashMap<>();
private final Set allCoordinates = new HashSet<>();
private final Map parentForPom = new HashMap<>();
private final Map> childPomsByParent = new HashMap<>();
private final Poms poms;
public PomCategorizer(Poms poms)
{
this.poms = poms;
categorizePoms();
}
public Set families()
{
return pomsForFamily.keySet();
}
public boolean is(Pom pom, PomRole role)
{
Set forPom = rolesForPom.get(pom);
return forPom == null
? false
: forPom.contains(role);
}
public void eachPom(Consumer c)
{
allPoms().forEach(c);
}
public List allPoms()
{
return poms.poms();
}
public Map> pomsForValueOfProperty(String property)
{
return pomsForValueOfProperty.getOrDefault(property, emptyMap());
}
public Map>> pomsForValueOfProperty()
{
return pomsForValueOfProperty;
}
public Set allCoordinates()
{
return allCoordinates;
}
public Map> rolesForPom()
{
return rolesForPom;
}
public Map> pomsForFamily()
{
return pomsForFamily;
}
public void eachPomInFamily(ProjectFamily family, Consumer c)
{
Set result = pomsForFamily.get(family);
if (result != null)
{
result.forEach(c);
}
}
public Map> projectsInFamilyWithRole(PomRole role)
{
Map> result = new HashMap<>();
pomsForFamily.forEach((family, poms) ->
{
Set set = poms.stream().filter(pom -> is(pom, role)).collect(
Collectors.toCollection(HashSet::new));
result.put(family, set);
});
return result;
}
public Set pomsWithRoles(PomRole first, PomRole... more)
{
Set result = new HashSet<>();
eachPomWithRoleIn(result::add, first, more);
return result;
}
public void eachPomWithRoleIn(Consumer c, PomRole first,
PomRole... more)
{
Set all = PomRole.setOf(first, more);
for (Pom pom : poms)
{
Set roles = EnumSet.copyOf(rolesForPom.get(pom));
roles.retainAll(all);
if (!roles.isEmpty())
{
c.accept(pom);
}
}
}
public Set pomsForFamily(ProjectFamily family)
{
return pomsForFamily.getOrDefault(family, emptySet());
}
public Set rolesFor(Pom pom)
{
return rolesForPom.getOrDefault(pom, Collections.emptySet());
}
public Poms poms()
{
return poms;
}
public boolean hasParent(Pom pom)
{
return parentForPom.containsKey(pom);
}
public Optional parentOf(Pom pom)
{
return Optional.ofNullable(parentForPom.get(pom));
}
public void eachPomAndItsRoles(BiConsumer> c)
{
PomRole.visitMapEntriesSorted(rolesForPom, c);
}
public ThrowingOptional pomFor(MavenCoordinates coords)
{
return poms.get(coords);
}
public List parents(Pom what)
{
List result = new ArrayList<>();
while (what != null)
{
result.add(what);
what = parentForPom.get(what);
}
return result;
}
private void onPom(PomRole role, Pom pom)
{
Set pomSet = pomsForKind
.computeIfAbsent(role, r -> new HashSet<>());
Set roles = rolesForPom.computeIfAbsent(pom,
p -> new HashSet<>());
pomSet.add(pom);
roles.add(role);
}
public Set childrenOf(Pom pom)
{
return childPomsByParent.getOrDefault(pom, emptySet());
}
public Set descendantsOf(Pom pom)
{
Set seen = new HashSet<>();
Set result = new HashSet<>();
visitDescendantsOf(pom, seen, p ->
{
result.add(p);
return true;
});
return result;
}
/**
* Visit all descendants of a pom, aborting child trees if the passed
* predicate returns false.
*
* @param pom A pom
* @param addTo A predicate - if it returns false, descendants of the pom
* passed will not be followed any further
*/
public void visitDescendantsOf(Pom pom, Predicate addTo)
{
visitDescendantsOf(pom, new HashSet<>(), addTo);
}
private void visitDescendantsOf(Pom pom, Set seen, Predicate addTo)
{
// In a mangled tree with cycles, which it is possible to encounter,
// we could endlessly loop, so protect against that
if (seen.contains(pom))
{
return;
}
seen.add(pom);
childrenOf(pom).forEach(desc ->
{
if (addTo.test(desc))
{
visitDescendantsOf(desc, seen, addTo);
}
});
}
public Set roots()
{
Set result = new HashSet<>();
for (Pom pom : allPoms())
{
if (!parentForPom.containsKey(pom))
{
result.add(pom);
}
}
return result;
}
private void recordParent(Pom child, Pom parent)
{
parentForPom.put(child, parent);
Set all = childPomsByParent.computeIfAbsent(parent,
p -> new HashSet<>());
all.add(child);
}
private void categorizePoms()
{
// Collect the ways each pom is used within the project tree
Set parents = new HashSet<>();
for (Pom pom : poms.poms())
{
ThrowingOptional parent = pom.parent();
// Collect the actual parent POM
parent.ifPresent(par ->
{
// Make sure we use the type with the right equality contract
parents.add(par.toPlainMavenCoordinates());
poms.get(par).ifPresent(
parentPom -> recordParent(pom, parentPom));
});
// Make sure
allCoordinates.add(pom.coordinates().toPlainMavenCoordinates());
// Create an inverse index by property by property value to
// map all POMs defining a property in relation to the name
// and value
pom.properties().forEach((prop, value) ->
{
Map> pomsByValue
= pomsForValueOfProperty.computeIfAbsent(prop,
p -> new HashMap<>());
Set set = pomsByValue.computeIfAbsent(value,
v -> new HashSet<>());
set.add(pom);
});
// Collect the family
ProjectFamily fam = familyOf(pom);
Set forFamily = pomsForFamily.computeIfAbsent(fam,
f -> new HashSet<>());
forFamily.add(pom);
// If it is a pom project...
if (pom.isPomProject())
{
if (!pom.modules().isEmpty())
{
// If it has modules, it is a bill of materials.
// We'll catch up with whether it is also supplying
// configuration below
onPom(BILL_OF_MATERIALS, pom);
}
else
{
if (!parent.isPresent())
{
onPom(CONFIG_ROOT, pom);
}
else
{
onPom(CONFIG, pom);
}
}
}
else
{
onPom(JAVA, pom);
}
}
// Now mark all the poms referencecd by other poms as their parent
// as having that category
parents.forEach(parent ->
{
poms.get(parent).ifPresent(parentPom ->
{
onPom(PARENT, parentPom);
});
});
// Note any UFOs so they don't disappear from our univers
for (Pom pom : poms.poms())
{
Set roles = rolesForPom.get(pom);
if (roles == null || roles.isEmpty())
{
onPom(UNKNOWN, pom);
}
}
// If something is a parent AND a BILL_OF_MATERIALS (the typical maven
// bill-of-materials-is-also-parent layout) then by definition, that
// POM can also supply configuration to its children, so note that
rolesForPom.forEach((pom, roles) ->
{
if (roles.contains(PARENT) && roles.contains(BILL_OF_MATERIALS))
{
if (pom.parent().isPresent())
{
roles.add(CONFIG_ROOT);
}
else
{
roles.add(CONFIG);
}
}
});
}
}