package org.jboss.windup.graph.traversal;
import java.util.HashSet;
import java.util.Set;
import com.google.common.base.Function;
import org.jboss.windup.graph.model.ArchiveModel;
import org.jboss.windup.graph.model.ProjectModel;
import org.jboss.windup.graph.model.resource.FileModel;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
/**
* <p>
* Provides a traversal that will limit the returned children to children that are not duplicated within this traversal.
* </p>
*
* <p>
* Put another way, if we have a ProjectTree that looks like this:
* <ul>
* <li>root.ear</li>
* <li>foo.war</li>
* <ul>
* <li>WEB-INF/lib/duplicated.jar</li>
* <li>WEB-INF/lib/other.jar</li>
* </ul>
* <li>duplicated.jar</li>
* <li>another.jar</li>
* </ul>
*
* Then this will only iterate root.ear, foo.war, duplicated.jar (one time only), WEB-INF/lib/other.jar, and another.jar.
* </p>
* <p>
* This is most useful in cases where you care about the contents of a project hierarchy but where representing them
* more than once would be redundant.
* </p>
*
* @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a>
* @author <a href="http://ondra.zizka.cz/">Ondrej Zizka, zizka@seznam.cz</a>
*/
public class OnlyOnceTraversalStrategy implements TraversalStrategy
{
private Set<String> alreadySeenHashes;
public OnlyOnceTraversalStrategy()
{
reset();
}
@Override
public ProjectModelTraversal.TraversalState getTraversalState(ProjectModelTraversal traversal)
{
return ProjectModelTraversal.TraversalState.ALL;
}
@Override
public void reset()
{
this.alreadySeenHashes = new HashSet<>();
}
@Override
public Iterable<ProjectModelTraversal> getChildren(final ProjectModelTraversal traversal)
{
ProjectModel canonicalProject = traversal.getCanonicalProject();
Iterable<ProjectModelTraversal> defaultChildren = Iterables.transform(canonicalProject.getChildProjects(), new Function<ProjectModel, ProjectModelTraversal>()
{
@Override
public ProjectModelTraversal apply(ProjectModel input)
{
return new ProjectModelTraversal(traversal, input, OnlyOnceTraversalStrategy.this);
}
});
return Iterables.filter(defaultChildren, new Predicate<ProjectModelTraversal>()
{
@Override
public boolean apply(ProjectModelTraversal input)
{
FileModel rootFile = input.getCurrent().getRootFileModel();
// This duplicate handling logic only applies to archives, so skip if it is not an archive
if (!(rootFile instanceof ArchiveModel))
return true;
ArchiveModel archive = (ArchiveModel) rootFile;
if (!alreadySeenHashes.contains(archive.getSHA1Hash()))
{
alreadySeenHashes.add(archive.getSHA1Hash());
return true;
}
else
{
return false;
}
}
});
}
}