/* vim: set ts=2 et sw=2 cindent fo=qroca: */
package com.globant.katari.jsmodule.domain;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang.Validate;
/** Fetches the dependencies for a list of file's paths.
*
* The dependencies are returned in a list that contains their paths. The
* returned list of dependencies doesn't contain repeated dependencies and are
* ordered accordingly.
*
* @author ivan.bedecarats@globant.com
*/
public class DependenciesResolver {
/**
* The Dependencies Finder, never null.
*/
private DependenciesFinder dependenciesFinder;
/** Makes a new instance for the {@link DependenciesResolver} with the given
* parameter.
* @param theDependenciesFinder The {@link DependenciesFinder} used to look
* for the dependencies. It's never null.
*/
public DependenciesResolver(
final DependenciesFinder theDependenciesFinder) {
Validate.notNull(theDependenciesFinder,
"The immediate dependencies resolver cannot be null.");
dependenciesFinder = theDependenciesFinder;
}
/** Fetches all the dependencies for a given list of files.
*
* @param filesToBeResolved The list of files whose dependencies are going to
* be fetched. It's never null.
*
* @return A List containing all the dependencies for the given list of files.
* Never returns null.
*/
public List<String> resolve(final List<String> filesToBeResolved) {
Validate.notNull(filesToBeResolved,
"The filesToBeResolved cannot be null.");
return resolve(filesToBeResolved, new HashSet<String>());
}
/** It looks for the dependencies for a given list of files.
*
* It stores the dependency ancestors to see if it finds a circular
* dependency. If that happens a {@link RuntimeException} is thrown.
*
* @param filesToBeResolved The list of files whose dependencies are going to
* be fetched. It's never null.
*
* @param ancestors A {@link Set} containing the ancestors for a dependency.
* It's never null.
*
* @return A List containing all the dependencies for the given list of
* files. Never returns null.
*/
private List<String> resolve(final List<String> filesToBeResolved,
final Set<String> ancestors) {
Validate.notNull(filesToBeResolved,
"The filesToBeResolved cannot be null.");
Validate.notNull(ancestors, "The ancestors cannot be null.");
List<String> fetchedDependencies = new ArrayList<String>();
for (String file : filesToBeResolved) {
if (ancestors.contains(file)) {
throw new RuntimeException("Circular dependency found: " + file
+ " is in the list of its ancestors. Ancestors are " + ancestors);
} else {
HashSet<String> newAncestors = new HashSet<String>();
newAncestors.addAll(ancestors);
newAncestors.add(file);
List<String> dependenciesFound = dependenciesFinder.find(file);
addIgnoreDuplicates(fetchedDependencies,
resolve(dependenciesFound, newAncestors));
}
addIgnoreDuplicate(fetchedDependencies, file);
}
return fetchedDependencies;
}
/** Adds a list of files to another list of source files. If any of the files
* already exists in the source list, then it won't be added.
* @param sourceFiles The list where the new files will be added if they are
* not duplicated. It's never null.
* @param filesToBeAdded The list of files to be added. If any of them exists
* in the source list, then it won't be added. It's never null.
*/
private void addIgnoreDuplicates(final List<String> sourceFiles,
final List<String> filesToBeAdded) {
Validate.notNull(sourceFiles, "The list of source files cannot be null.");
Validate.notNull(filesToBeAdded,
"The list of files to be added cannot be null.");
for (String fileToBeAdded : filesToBeAdded) {
addIgnoreDuplicate(sourceFiles, fileToBeAdded);
}
}
/** Adds a file to a list of files if that file doesn't already exist in the
* list.
* @param sourceFiles The list where the new file will be added if it doesn't
* already exist in the list. It's never null.
* @param fileToBeAdded The file to be added in the list. If it already exist
* in the list, then it won't be added. It's never null.
*/
private void addIgnoreDuplicate(final List<String> sourceFiles,
final String fileToBeAdded) {
Validate.notNull(sourceFiles, "The list of source files cannot be null.");
Validate.notNull(fileToBeAdded, "The files to be added cannot be null.");
if (!sourceFiles.contains(fileToBeAdded)) {
sourceFiles.add(fileToBeAdded);
}
}
}