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; } } }); } }