public class DependenciesSettings {

    protected static final Pattern NOTATION_PATTERN = Pattern.compile("(?<group>[a-zA-Z0-9.\\-_/]+)(?::(?<version>[a-zA-Z0-9.\\-_/]+))?");

    private final boolean _root;

    @Nonnull
    private final Project _project;

    private Boolean _forceUpdate;
    private Boolean _deleteUnknownDependencies;
    private Boolean _deleteAllCachedDependenciesOnClean;
    private Path _dependencyCache;

    private Collection<VcsRepositoryProvider> _vcsRepositoryProviders;

    @Inject
    public DependenciesSettings(boolean root, @Nonnull Project project) {
        _root = root;
        _project = project;
        if (root) {
            _dependencyCache = project.getProjectDir().toPath().resolve(VENDOR_DIRECTORY_NAME);
            _deleteUnknownDependencies = true;
        }
    }

    public Boolean getForceUpdate() {
        return _forceUpdate;
    }

    public void setForceUpdate(Boolean forceUpdate) {
        _forceUpdate = forceUpdate;
    }

    public Boolean getDeleteUnknownDependencies() {
        return _deleteUnknownDependencies;
    }

    public void setDeleteUnknownDependencies(Boolean deleteUnknownDependencies) {
        _deleteUnknownDependencies = deleteUnknownDependencies;
    }

    public Boolean getDeleteAllCachedDependenciesOnClean() {
        return _deleteAllCachedDependenciesOnClean;
    }

    public void setDeleteAllCachedDependenciesOnClean(Boolean deleteAllCachedDependenciesOnClean) {
        _deleteAllCachedDependenciesOnClean = deleteAllCachedDependenciesOnClean;
    }

    public Path getDependencyCache() {
        return _dependencyCache;
    }

    public void setDependencyCache(Path dependencyCache) {
        _dependencyCache = dependencyCache;
    }

    public Collection<VcsRepositoryProvider> getVcsRepositoryProviders() {
        return _vcsRepositoryProviders;
    }

    public void setVcsRepositoryProviders(Collection<VcsRepositoryProvider> vcsRepositoryProviders) {
        _vcsRepositoryProviders = vcsRepositoryProviders;
    }

    @Nonnull
    public VcsRepositoryProvider vcsRepositoryProvider(@Nonnull String prefix, @Nonnull String name, @Nonnull String dependencyPattern) {
        return vcsRepositoryProvider(prefix, name, Pattern.compile(dependencyPattern));
    }

    @Nonnull
    public VcsRepositoryProvider vcsRepositoryProvider(@Nonnull String prefix, @Nonnull String name, @Nonnull Pattern dependencyPattern) {
        return vcsRepositoryProvider(git, prefix, name, dependencyPattern);
    }

    @Nonnull
    public VcsRepositoryProvider vcsRepositoryProvider(@Nonnull String type, @Nonnull String prefix, @Nonnull String name, @Nonnull String dependencyPattern) {
        return vcsRepositoryProvider(VcsType.valueOf(type), prefix, name, Pattern.compile(dependencyPattern));
    }

    @Nonnull
    public VcsRepositoryProvider vcsRepositoryProvider(@Nonnull VcsType type, @Nonnull String prefix, @Nonnull String name, @Nonnull Pattern dependencyPattern) {
        final VcsRepositoryProvider result = new VcsRepositoryProvider(type, prefix, name, dependencyPattern);
        add(result);
        return result;
    }

    public void add(@Nonnull VcsRepositoryProvider vcsRepositoryProvider) {
        if (_vcsRepositoryProviders == null) {
            _vcsRepositoryProviders = new ArrayList<>();
        }
        _vcsRepositoryProviders.add(vcsRepositoryProvider);
    }

    @Nonnull
    public Dependency add(@Nonnull String configurationName, @Nonnull Object notation) {
        return add(configurationName, notation, null);
    }

    @Nonnull
    public Dependency add(@Nonnull String configurationName, @Nonnull Object notation, @Nullable Closure<?> configureWith) {
        return doAdd(configurationName, notation, configureWith);
    }

    @Nonnull
    public Dependency create(@Nonnull String configurationName, @Nonnull Object notation) {
        return create(configurationName, notation, null);
    }

    @Nonnull
    public Dependency create(@Nonnull String configurationName, @Nonnull Object notation, @Nullable Closure<?> configureWith) {
        return doAdd(configurationName, notation, configureWith);
    }

    @Nonnull
    protected Dependency doAdd(@Nonnull String configurationName, @Nonnull Object notation, @Nullable Closure<?> configureWith) {
        if (notation instanceof String) {
            return doAdd(configurationName, (String) notation, configureWith);
        }
        if (notation instanceof Map) {
            //noinspection unchecked,rawtypes
            return doAdd(configurationName, (Map) notation, configureWith);
        }
        throw new IllegalArgumentException("Could not handle notation of type: " + notation.getClass().getName());
    }

    @Nonnull
    protected Dependency doAdd(@Nonnull String configurationName, @Nonnull String notation, @Nullable Closure<?> configureWith) {
        final Matcher matcher = NOTATION_PATTERN.matcher(notation);
        if (!matcher.matches()) {
            throw new IllegalArgumentException("Illegal dependency notation provided: " + notation);
        }
        final String group = matcher.group("group");
        final String version = matcher.group("version");
        return doAdd(configurationName, new GolangDependency()
                .setGroup(group)
                .setVersion(version)
            , configureWith);
    }

    @Nonnull
    protected Dependency doAdd(@Nonnull String configurationName, @Nonnull Map<String, Object> arguments, @Nullable Closure<?> configureWith) {
        final String group = requiredArgument(arguments, "group");
        final String version = argument(arguments, "version");
        final URI repositoryUri = uriArgument(arguments, "repositoryUri");
        final VcsType repositoryType = vcsTypeArgument(arguments, "repositoryType");
        final UpdatePolicy updatePolicy = updatePolicyArgument(arguments, "updatePolicy");
        return doAdd(configurationName, new GolangDependency()
                .setGroup(group)
                .setVersion(version)
                .setRepositoryUri(repositoryUri)
                .setRepositoryType(repositoryType)
                .setUpdatePolicy(updatePolicy)
            , configureWith);
    }

    @Nonnull
    protected Dependency doAdd(@Nonnull String configurationName, @Nonnull Dependency dependency, @Nullable Closure<?> configureWith) {
        if (!_root) {
            throw new IllegalStateException("Adding of dependencies for golang is currently not support at task level.");
        }
        final Configuration configuration = _project.getConfigurations().getByName(configurationName);
        configuration.getDependencies().add(dependency);
        if (configureWith != null) {
            return configure(configureWith, dependency);
        }
        return dependency;
    }

    @Nullable
    protected String argument(@Nonnull Map<String, Object> arguments, @Nonnull String name) {
        final Object plain = arguments.get(name);
        if (plain == null) {
            return null;
        }
        return plain.toString();
    }

    @Nullable
    protected URI uriArgument(@Nonnull Map<String, Object> arguments, @Nonnull String name) {
        final Object plain = arguments.get(name);
        if (plain == null) {
            return null;
        }
        if (plain instanceof URI) {
            return (URI) plain;
        }
        return URI.create(plain.toString());
    }

    @Nullable
    protected VcsType vcsTypeArgument(@Nonnull Map<String, Object> arguments, @Nonnull String name) {
        final Object plain = arguments.get(name);
        if (plain == null) {
            return null;
        }
        if (plain instanceof VcsType) {
            return (VcsType) plain;
        }
        return VcsType.valueOf(plain.toString());
    }

    @Nonnull
    protected UpdatePolicy updatePolicyArgument(@Nonnull Map<String, Object> arguments, @Nonnull String name) {
        final Object plain = arguments.get(name);
        if (plain instanceof UpdatePolicy) {
            return (UpdatePolicy) plain;
        }
        return UpdatePolicy.valueOf(plain.toString());
    }

    @Nonnull
    protected String requiredArgument(@Nonnull Map<String, Object> arguments, @Nonnull String name) {
        final String result = argument(arguments, name);
        if (result == null) {
            throw new IllegalArgumentException("Required argument '" + name + "' not provided. But got: " + arguments);
        }
        return result;
    }

    @Nullable
    public Object methodMissing(@Nonnull String name, @Nullable Object args) {
        final Object[] argsArray = (Object[]) args;
        final Configuration configuration = _project.getConfigurations().findByName(name);
        if (configuration == null) {
            throw new MissingMethodException(name, this.getClass(), argsArray);
        }
        final List<?> normalizedArgs = CollectionUtils.flattenCollections(argsArray);
        if (normalizedArgs.size() == 2 && normalizedArgs.get(1) instanceof Closure) {
            //noinspection rawtypes
            return doAdd(name, normalizedArgs.get(0), (Closure) normalizedArgs.get(1));
        } else if (normalizedArgs.size() == 1) {
            return doAdd(name, normalizedArgs.get(0), null);
        } else {
            for (final Object arg : normalizedArgs) {
                doAdd(name, arg, null);
            }
            return null;
        }
    }
} public class GolangDependency implements Dependency, Comparable<GolangDependency> {

    @Nonnull
    private String _group = "<unknown>";
    @Nullable
    private String _version;
    @Nullable
    private URI _repositoryUri;
    @Nullable
    private VcsType _repositoryType;
    @Nonnull
    private UpdatePolicy _updatePolicy = defaultUpdatePolicy();
    @Nonnull
    private Type _type = explicit;

    @Nullable
    private GolangDependency _parent;
    @Nullable
    private Path _location;

    @Nonnull
    public static GolangDependency newDependency(@Nonnull String group) {
        return new GolangDependency()
            .setGroup(group);
    }

    public GolangDependency() {}

    public GolangDependency(@Nonnull Dependency raw) {
        if (raw instanceof GolangDependency) {
            final GolangDependency original = (GolangDependency) raw;
            setGroup(original.getGroup());
            setVersion(original.getVersion());
            setRepositoryUri(original.getRepositoryUri());
            setRepositoryType(original.getRepositoryType());
            setUpdatePolicy(original.getUpdatePolicy());
        } else {
            final String name = raw.getName();
            if (StringUtils.isEmpty(name)) {
                setGroup(raw.getGroup());
            } else {
                setGroup(raw.getGroup() + "/" + name);
            }
            final String version = raw.getVersion();
            if (StringUtils.isNotEmpty(version) && !"default".equalsIgnoreCase(version)) {
                setVersion(version);
            }
        }
    }

    @Override
    @Nonnull
    public String getGroup() {
        return _group;
    }

    @Override
    @Nonnull
    public String getName() {
        return FilenameUtils.getName(getGroup());
    }

    @Override
    @Nullable
    public String getVersion() {
        return _version;
    }

    @Nullable
    public URI getRepositoryUri() {
        return _repositoryUri;
    }

    @Nullable
    public VcsType getRepositoryType() {
        return _repositoryType;
    }

    @Nonnull
    public UpdatePolicy getUpdatePolicy() {
        return _updatePolicy;
    }

    @Nonnull
    public Type getType() {
        return _type;
    }

    @Nullable
    public GolangDependency getParent() {
        return _parent;
    }

    @Nullable
    public Path getLocation() {
        return _location;
    }

    @Nonnull
    public GolangDependency setGroup(@Nonnull String group) {
        _group = group;
        return this;
    }

    @Nonnull
    public GolangDependency setVersion(@Nullable String version) {
        _version = version;
        return this;
    }

    @Nonnull
    public GolangDependency setRepositoryUri(@Nullable URI repositoryUri) {
        _repositoryUri = repositoryUri;
        return this;
    }

    @Nonnull
    public GolangDependency setRepositoryType(@Nullable VcsType repositoryType) {
        _repositoryType = repositoryType;
        return this;
    }

    @Nonnull
    public GolangDependency setUpdatePolicy(@Nonnull UpdatePolicy updatePolicy) {
        //noinspection ConstantConditions
        if (updatePolicy == null) {
            throw new NullPointerException("There is no updatePolicy provided.");
        }
        _updatePolicy = updatePolicy;
        return this;
    }

    @Nonnull
    public GolangDependency setType(@Nonnull Type type) {
        //noinspection ConstantConditions
        if (type == null) {
            throw new NullPointerException("There is no type provided.");
        }
        _type = type;
        return this;
    }

    @Nonnull
    public GolangDependency setParent(@Nullable GolangDependency parent) {
        _parent = parent;
        return this;
    }

    @Nonnull
    public GolangDependency setLocation(@Nullable Path location) {
        _location = location;
        return this;
    }

    @Override
    public boolean contentEquals(Dependency dependency) {
        return dependency instanceof GolangDependency
            && equals(dependency)
            ;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        final String group = getGroup();
        sb.append(getType()).append(' ').append(isEmpty(group) ? "<unknown>" : group);
        final String version = getVersion();
        final URI repositoryUri = getRepositoryUri();
        final VcsType repositoryType = getRepositoryType();
        final UpdatePolicy updatePolicy = getUpdatePolicy();
        final GolangDependency parent = getParent();
        final Path location = getLocation();
        if (!isEmpty(version) || repositoryUri != null || repositoryType != null || !defaultUpdatePolicy().equals(updatePolicy) || parent != null) {
            sb.append(" (");
            boolean first = true;
            if (!isEmpty(version)) {
                sb.append("version=").append(version);
                first = false;
            }
            if (repositoryUri != null) {
                if (!first) {
                    sb.append(", ");
                }
                sb.append("repositoryUri=").append(repositoryUri);
                first = false;
            }
            if (repositoryType != null) {
                if (!first) {
                    sb.append(", ");
                }
                sb.append("repositoryType=").append(repositoryType);
                first = false;
            }
            if (!defaultUpdatePolicy().equals(updatePolicy)) {
                if (!first) {
                    sb.append(", ");
                }
                sb.append("updatePolicy=").append(updatePolicy);
            }
            if (parent != null) {
                if (!first) {
                    sb.append(", ");
                }
                sb.append("parent=").append(parent.getGroup());
            }
            sb.append(')');
        }
        return sb.toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) { return true; }
        if (o == null || getClass() != o.getClass()) { return false; }
        final GolangDependency that = (GolangDependency) o;
        return Objects.equals(getGroup(), that.getGroup()) &&
            Objects.equals(getVersion(), that.getVersion()) &&
            Objects.equals(getRepositoryUri(), that.getRepositoryUri()) &&
            Objects.equals(getRepositoryType(), that.getRepositoryType()) &&
            Objects.equals(getUpdatePolicy(), that.getUpdatePolicy()) // TODO! Parent?
            ;
    }

    @Override
    public int hashCode() {
        return Objects.hash(getGroup(), getVersion(), getRepositoryUri(), getRepositoryType(), getUpdatePolicy()); // TODO! Parent?
    }

    @Override
    public int compareTo(@Nonnull GolangDependency that) {
        if (!Objects.equals(getGroup(), that.getGroup())) {
            return compare(getGroup(), that.getGroup());
        }
        if (!Objects.equals(getVersion(), that.getVersion())) {
            return compare(getVersion(), that.getVersion());
        }
        if (!Objects.equals(getRepositoryUri(), that.getRepositoryUri())) {
            return compare(getRepositoryUri(), that.getRepositoryUri());
        }
        if (getRepositoryType() != that.getRepositoryType()) {
            return compare(getRepositoryType(), that.getRepositoryType());
        }
        if (!Objects.equals(getUpdatePolicy(), that.getUpdatePolicy())) {
            return compare(getUpdatePolicy(), that.getUpdatePolicy());
        }
        if (!Objects.equals(getLocation(), that.getLocation())) {
            return compare(getLocation(), that.getLocation());
        }
        return 0;
    }

    protected <T extends Comparable<T>> int compare(@Nullable T a, @Nullable T b) {
        if (a == null && b == null) {
            return 0;
        }
        if (a == null) {
            return 1;
        }
        if (b == null) {
            return -1;
        }
        return a.compareTo(b);
    }

    @Override
    public GolangDependency copy() {
        return new GolangDependency()
            .setGroup(getGroup())
            .setVersion(getVersion())
            .setRepositoryUri(getRepositoryUri())
            .setRepositoryType(getRepositoryType())
            .setUpdatePolicy(getUpdatePolicy())
            .setLocation(getLocation())
            .setParent(getParent())
            .setType(getType())
            ;
    }

    @Nonnull
    public RawVcsReference toRawVcsReference() throws VcsValidationException {
        final String name = getGroup();
        if (isEmpty(name)) {
            throw new VcsValidationException("Name of a dependency must be set.");
        }
        final String ref = getVersion();
        final URI repositoryUri = getRepositoryUri();
        final VcsType repositoryType = getRepositoryType();
        final UpdatePolicy updatePolicy = getUpdatePolicy();
        return new RawVcsReference(repositoryType, name, repositoryUri, ref, updatePolicy);
    }

    public enum Type {
        explicit,
        implicit,
        source,
        system
    }

} public class Settings {

    @Nonnull
    private final Project _project;
    @Nonnull
    private final GolangSettings _golang;
    @Nonnull
    private final BuildSettings _build;
    @Nonnull
    private final ToolchainSettings _toolchain;
    @Nonnull
    private final DependenciesSettings _dependencies;
    @Nonnull
    private final TestingSettings _testing;

    public Settings(@Nonnull Project project, @Nonnull ExtensionContainer container) {
        _project = project;
        _golang = container.getByType(GolangSettings.class);
        if (!(_golang instanceof ExtensionAware)) {
            throw new IllegalStateException("golang instance (" + _golang + ") of provided extension container (" + container + ") is not an instance of " + ExtensionAware.class.getName() + ".");
        }
        final ExtensionContainer globalExtensions = ((ExtensionAware) _golang).getExtensions();
        _build = globalExtensions.getByType(BuildSettings.class);
        _toolchain = globalExtensions.getByType(ToolchainSettings.class);
        _dependencies = globalExtensions.getByType(DependenciesSettings.class);
        _testing = globalExtensions.getByType(TestingSettings.class);
    }

    public Settings(@Nonnull Project project, @Nonnull ExtensionContainer container, boolean init) {
        _project = project;
        _golang = container.create("golang", GolangSettings.class, init, project);
        final ExtensionContainer taskExtensions = ((ExtensionAware) _golang).getExtensions();
        _build = taskExtensions.create("build", BuildSettings.class, init, project);
        _toolchain = taskExtensions.create("toolchain", ToolchainSettings.class, init, project);
        _dependencies = taskExtensions.create("dependencies", DependenciesSettings.class, init, project);
        _testing = taskExtensions.create("testing", TestingSettings.class, init, project);
    }

    public Settings(@Nonnull Project project, @Nonnull GolangSettings golang, @Nonnull BuildSettings build, @Nonnull ToolchainSettings toolchain, @Nonnull DependenciesSettings dependencies, @Nonnull TestingSettings testing) {
        _project = project;
        _golang = golang;
        _build = build;
        _toolchain = toolchain;
        _dependencies = dependencies;
        _testing = testing;
    }

    @Nonnull
    public GolangSettings getGolang() {
        return _golang;
    }

    @Nonnull
    public BuildSettings getBuild() {
        return _build;
    }

    @Nonnull
    public ToolchainSettings getToolchain() {
        return _toolchain;
    }

    @Nonnull
    public DependenciesSettings getDependencies() {
        return _dependencies;
    }

    @Nonnull
    public TestingSettings getTesting() {
        return _testing;
    }

    @Nonnull
    public Project getProject() {
        return _project;
    }

    @Nonnull
    public Settings merge(@Nonnull Settings... with) {
        final GolangSettings golang = new GolangSettings(false, _project);
        final BuildSettings build = new BuildSettings(false, _project);
        final ToolchainSettings toolchain = new ToolchainSettings(false, _project);
        final DependenciesSettings dependencies = new DependenciesSettings(false, _project);
        final TestingSettings testing = new TestingSettings(false, _project);

        copyNonNulls(GolangSettings.class, _golang, golang);
        copyNonNulls(BuildSettings.class, _build, build);
        copyNonNulls(ToolchainSettings.class, _toolchain, toolchain);
        copyNonNulls(DependenciesSettings.class, _dependencies, dependencies);
        copyNonNulls(TestingSettings.class, _testing, testing);

        for (final Settings source : with) {
            copyNonNulls(GolangSettings.class, source.getGolang(), golang);
            copyNonNulls(BuildSettings.class, source.getBuild(), build);
            copyNonNulls(ToolchainSettings.class, source.getToolchain(), toolchain);
            copyNonNulls(DependenciesSettings.class, source.getDependencies(), dependencies);
            copyNonNulls(TestingSettings.class, source.getTesting(), testing);
        }

        return new Settings(_project, golang, build, toolchain, dependencies, testing);
    }
} public class FileUtils {

    private static final Logger LOG = LoggerFactory.getLogger(FileUtils.class);

    @Nullable
    public static Path toPath(@Nullable String plain) {
        return plain != null ? Paths.get(plain) : null;
    }

    public static void ensureParentOf(@Nonnull Path path) throws IOException {
        createDirectoriesIfRequired(path.getParent());
    }

    public static void createDirectoriesIfRequired(@Nonnull Path path) throws IOException {
        if (!exists(path)) {
            createDirectories(path);
        }
    }

    public static void delete(@Nullable Path path) throws IOException {
        if (path == null || !exists(path)) {
            return;
        }
        if (isRegularFile(path)) {
            doDelete(path);
            return;
        }
        walkFileTree(path, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                doDelete(file);
                return CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                doDelete(dir);
                return CONTINUE;
            }
        });
    }

    public static void deleteQuietly(@Nullable Path path) {
        if (path == null || !exists(path)) {
            return;
        }
        if (isRegularFile(path)) {
            try {
                doDelete(path);
            } catch (final IOException ignored) {}
            return;
        }
        try {
            walkFileTree(path, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                    try {
                        doDelete(file);
                    } catch (final IOException ignored) {}
                    return CONTINUE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
                    try {
                        doDelete(dir);
                    } catch (final IOException ignored) {}
                    return CONTINUE;
                }
            });
        } catch (final IOException ignored) {}
    }

    private static void doDelete(@Nonnull Path path) throws IOException {
        final FileStore fileStore = Files.getFileStore(path);
        if (fileStore.supportsFileAttributeView("dos")) {
            setAttribute(path, "dos:readonly", false);
            setAttribute(path, "dos:system", false);
            setAttribute(path, "dos:hidden", false);
        }
        Files.delete(path);

    }

    public static Set<Path> deleteWithLogging(@Nullable Path path) {
        final Set<Path> result = new TreeSet<>();
        if (path == null || !exists(path)) {
            return result;
        }
        if (isRegularFile(path)) {
            try {
                doDelete(path);
            } catch (final IOException e) {
                LOG.warn("Could not delete '{}' Got: {} - {}", path, e.getClass().getName(), e.getMessage());
            }
            result.add(path);
            return result;
        }
        try {
            walkFileTree(path, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                    result.add(file);
                    try {
                        doDelete(file);
                    } catch (final IOException e) {
                        LOG.warn("Could not delete '{}' Got: {} - {}", file, e.getClass().getName(), e.getMessage());
                    }
                    return CONTINUE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
                    result.add(dir);
                    try {
                        doDelete(dir);
                    } catch (final IOException e) {
                        LOG.warn("Could not delete '{}' Got: {} - {}", dir, e.getClass().getName(), e.getMessage());
                    }
                    return CONTINUE;
                }
            });
        } catch (final IOException ignored) {}
        return result;
    }

} protected enum DependencyDirType {
    unknown,
    containsInfoFile,
    hasContainsInfoFileParent,
    parentOfContainsInfoFile
} public enum Type {
    explicit,
    implicit,
    source,
    system
} @Nonnull
public static GolangDependency newDependency(@Nonnull String group) {
    return new GolangDependency()
        .setGroup(group);
} @Nonnull
public static List<org.echocat.gradle.plugins.golang.vcs.VcsRepositoryProvider> toConcrete(@Nullable Iterable<VcsRepositoryProvider> models) {
    final List<org.echocat.gradle.plugins.golang.vcs.VcsRepositoryProvider> result = new ArrayList<>();
    if (models != null) {
        for (final VcsRepositoryProvider model : models) {
            result.add(model.toConcrete());
        }
    }
    return unmodifiableList(result);
} @Nonnull
public static Executor executor(@Nonnull String executable) {
    return executor(Paths.get(executable).toAbsolutePath());
} @Nonnull
public static Iterable<VcsRepositoryProvider> delegatesWithDefaults(@Nullable Iterable<VcsRepositoryProvider> delegates) {
    final List<VcsRepositoryProvider> result = new ArrayList<>(defaultConcretes());

    result.add(new GolangOrgVcsRepositoryProvider());
    result.add(new GoogleGolangOrgVcsRepositoryProvider());
    result.add(new BitbucketVcsRepositoryProvider());

    if (delegates != null) {
        for (final VcsRepositoryProvider delegate : delegates) {
            result.add(delegate);
        }
    }

    result.add(new SuffixDetectingVcsRepositoryProvider());

    return unmodifiableList(result);
} @Nonnull
public static ProgressMonitor progressMonitorFor(@Nonnull final String messagePattern, @Nonnull final ProgressLogger progressLogger) {
    final MessageFormat messageFormat = new MessageFormat(messagePattern, Locale.US);
    return new ProgressMonitor() {
        @Override
        public void started() {}

        @Override
        public void update(@Nonnegative double progress) {
            progressLogger.progress(messageFormat.format(new Object[]{progress}));
        }

        @Override
        public void finished() {
            update(1d);
        }
    };
}
import org.apache.commons.lang3.StringUtils;
import org.echocat.gradle.plugins.golang.model.DependenciesSettings;
import org.echocat.gradle.plugins.golang.model.GolangDependency;
import org.echocat.gradle.plugins.golang.model.Settings;
import org.echocat.gradle.plugins.golang.utils.FileUtils;
import org.echocat.gradle.plugins.golang.vcs.*;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.artifacts.Dependency;
import org.gradle.internal.logging.progress.ProgressLogger;
import org.gradle.internal.logging.progress.ProgressLoggerFactory;
import org.gradle.internal.service.ServiceRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import static java.io.File.separatorChar;
import static java.lang.Boolean.TRUE;
import static java.nio.file.FileVisitResult.CONTINUE;
import static java.nio.file.Files.*;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableSet;
import static org.apache.commons.lang3.StringUtils.capitalize;
import static org.echocat.gradle.plugins.golang.DependencyHandler.DependencyDirType.*;
import static org.echocat.gradle.plugins.golang.DependencyHandler.GetResult.alreadyExists;
import static org.echocat.gradle.plugins.golang.DependencyHandler.GetResult.downloaded;
import static org.echocat.gradle.plugins.golang.model.GolangDependency.Type.*;
import static org.echocat.gradle.plugins.golang.model.GolangDependency.newDependency;
import static org.echocat.gradle.plugins.golang.model.VcsRepositoryProvider.toConcrete;
import static org.echocat.gradle.plugins.golang.utils.Executor.executor;
import static org.echocat.gradle.plugins.golang.vcs.CombinedVcsRepositoryProvider.delegatesWithDefaults;
import static org.echocat.gradle.plugins.golang.vcs.VcsRepository.Utils.progressMonitorFor;
        return result;
    }

    protected void appendFilesFor(@Nonnull Iterable<Path> roots, @Nonnull GolangDependency dependency, @Nonnull Set<Path> to) throws Exception {
        for (final Path root : roots) {
            appendFilesFor(root, dependency, to);
        }
    }

    protected void appendFilesFor(@Nonnull Path root, @Nonnull GolangDependency dependency, @Nonnull Set<Path> to) throws Exception {
        final Path directory = root.resolve(dependency.getGroup());
        if (isDirectory(directory)) {
            for (final Path path : newDirectoryStream(directory, GO_FILENAME_FILTER)) {
                to.add(path.toAbsolutePath());
            }
        }
    }

    @Nonnull
    public Collection<Path> deleteUnknownDependenciesIfRequired() throws Exception {
        final ProgressLogger progress = _progressLoggerFactory.newOperation(DependencyHandler.class);
        progress.setDescription("Delete unknown dependencies");
        progress.started("Delete unknown dependencies if required...");
        final DependenciesSettings dependencies = _settings.getDependencies();
        final Path dependencyCacheDirectory = dependencies.getDependencyCache();
        final Set<String> knownDependencyIds = new HashSet<>();
        for (final GolangDependency dependency : allProjectDependencies()) {
            knownDependencyIds.add(dependency.getGroup());
        }
        final Collection<Path> result = doDeleteUnknownDependenciesIfRequired(dependencyCacheDirectory, knownDependencyIds);
        progress.completed();
        return result;
    }

    @Nonnull
    public Collection<Path> deleteAllCachedDependenciesIfRequired() throws Exception {
        final ProgressLogger progress = _progressLoggerFactory.newOperation(DependencyHandler.class);
        progress.setDescription("Delete all cached dependencies");
        progress.started("Delete all cached dependencies if required...");
        final DependenciesSettings dependencies = _settings.getDependencies();
        final Path dependencyCacheDirectory = dependencies.getDependencyCache();
        final Collection<Path> result = doDeleteAllCachedDependenciesIfRequired(dependencyCacheDirectory);
        progress.completed();
        return result;
    }

    @Nonnull
    protected Collection<GolangDependency> dependencies(@Nonnull GetTask task) {
        final Set<GolangDependency> result = new LinkedHashSet<>();
        appendDependenciesOf(task.getConfiguration(), result);
        for (final String additionalConfiguration : task.getAdditionalConfigurations()) {
            appendDependenciesOf(additionalConfiguration, result);
        }
        return result;
    }

    protected void appendDependenciesOf(@Nonnull String configurationName, @Nonnull Collection<GolangDependency> to) {
        final Project project = _settings.getProject();
        final ConfigurationContainer configurations = project.getConfigurations();
        final Configuration configuration = configurations.getByName(configurationName);
        appendDependenciesOf(configuration, to);
    }

    @Nonnull
    protected Collection<GolangDependency> allProjectDependencies() {
        final Collection<GolangDependency> result = new LinkedHashSet<>();
        final Project project = _settings.getProject();
        for (final Configuration configuration : project.getConfigurations()) {
            appendDependenciesOf(configuration, result);
        }
        return result;
    }

    protected void appendDependenciesOf(@Nonnull Configuration configuration, @Nonnull Collection<GolangDependency> to) {
        for (final Dependency dependency : configuration.getDependencies()) {
            final GolangDependency toAdd;
            if (dependency instanceof GolangDependency) {
                toAdd = (GolangDependency) dependency;
            } else {
                toAdd = new GolangDependency(dependency);
            }
            if (!to.contains(toAdd)) {
                to.add(toAdd);
            }
        }
    }

    @Nonnull
    protected Collection<Path> doDeleteUnknownDependenciesIfRequired(@Nonnull Path root, @Nonnull Set<String> knownDependencyIds) throws IOException {
        if (!TRUE.equals(_settings.getDependencies().getDeleteUnknownDependencies())) {
            return emptyList();
        }
        return doDeleteUnknownDependencies(root, knownDependencyIds);
    }

    @Nonnull
    protected Collection<Path> doDeleteAllCachedDependenciesIfRequired(@Nonnull Path root) throws IOException {
        if (!TRUE.equals(_settings.getDependencies().getDeleteAllCachedDependenciesOnClean())) {
            return emptyList();
        }
        LOGGER.debug("Deleting cached dependency in {}...", root);
        final Set<Path> deleted = FileUtils.deleteWithLogging(root);
        LOGGER.info("Unknown cached in {} deleted.", root);
        return deleted;
    }

    @Nonnull
    protected Collection<Path> doDeleteUnknownDependencies(@Nonnull Path root, @Nonnull Set<String> knownDependencyIds) throws IOException {
        final Collection<Path> paths = collectUnknownDependencyDirectories(root, knownDependencyIds);
        for (final Path path : paths) {
            LOGGER.debug("Deleting unknown dependency in {}...", path);
            FileUtils.deleteWithLogging(path);
            LOGGER.info("Unknown dependency in {} deleted.", path);
        }
        return paths;
    }

    @Nonnull
    protected Collection<Path> collectUnknownDependencyDirectories(@Nonnull Path root, @Nonnull Set<String> knownDependencyIds) {
        final Set<Path> result = new TreeSet<>(Collections.<Path>reverseOrder());
        final Map<Path, DependencyDirType> candidates = collectDirectoriesToKnownDependencyOf(root, knownDependencyIds);