org.automagic.deps.doctor.spy.PomSpyImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.automagic.deps.doctor.spy.PomSpyImpl.java

Source

/*
 * Copyright 2015 Jose Carrizo
 * Licensed under the Apache License, Version 2.0 
 * See accompanying file LICENSE or get a copy at http://www.apache.org/licenses/LICENSE-2.0
 */
package org.automagic.deps.doctor.spy;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactCollector;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.dependency.tree.DependencyNode;
import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
import org.automagic.deps.doctor.TransitiveDependency;
import org.automagic.deps.doctor.TransitiveDepsGroup;
import org.automagic.deps.doctor.Utils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import com.google.common.base.Optional;

public class PomSpyImpl implements PomSpy {

    private final MavenProject project;
    private final ArtifactRepository repository;
    private final ArtifactFactory factory;
    private final ArtifactMetadataSource metadataSource;
    private final ArtifactCollector collector;
    private final DependencyTreeBuilder treeBuilder;
    private final Document document;
    private final boolean useParent;

    public PomSpyImpl(MavenProject project, ArtifactRepository repository, ArtifactFactory factory,
            ArtifactMetadataSource metadataSource, ArtifactCollector collector, DependencyTreeBuilder treeBuilder,
            boolean useParent) {
        this.project = project;
        this.repository = repository;
        this.factory = factory;
        this.metadataSource = metadataSource;
        this.collector = collector;
        this.treeBuilder = treeBuilder;
        this.useParent = useParent;
        this.document = Utils.getDocument(project.getFile());
    }

    @Override
    public List<TransitiveDepsGroup> getTransitiveDepsGroups() {
        try {
            ArtifactFilter filter = null; // evaluate all scopes
            DependencyNode rootNode = treeBuilder.buildDependencyTree(project, repository, factory, metadataSource,
                    filter, collector);
            ForceUpperBoundsDepsVisitor visitor = new ForceUpperBoundsDepsVisitor();
            rootNode.accept(visitor);

            List<List<DependencyNodeHopCountPair>> dependenciesList = visitor.getAllDependencies();

            if (dependenciesList.isEmpty()) {
                return Collections.emptyList();
            }

            List<TransitiveDepsGroup> groupsList = new ArrayList<>();

            for (List<DependencyNodeHopCountPair> pairsList : dependenciesList) {

                TransitiveDepsGroup group = new TransitiveDepsGroup();
                groupsList.add(group);

                for (DependencyNodeHopCountPair pair : pairsList) {
                    Artifact artifact = pair.getNode().getArtifact();
                    ArtifactVersion version = pair.extractArtifactVersion(true);
                    ArtifactVersion managedVersion = pair.extractArtifactVersion(false);
                    int hopCount = pair.getHopCount();
                    boolean wasAddedByPlugin = Utils.getPluginComment("/project", artifact, document).isPresent();
                    TransitiveDependency dependency = new TransitiveDependency(artifact, version, managedVersion,
                            hopCount, pair.getNode(), wasAddedByPlugin);
                    group.add(dependency);
                }
            }

            return groupsList;

        } catch (DependencyTreeBuilderException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean isBrandNew(TransitiveDependency dependency) {
        return !Utils.getDependencyNode(dependency.getArtifact(), document, "/project/dependencies").isPresent();
    }

    @Override
    public Optional<String> getVersion(TransitiveDependency dependency) {

        Optional<Node> dependencyNode = Utils.getDependencyNode(dependency.getArtifact(), document,
                "/project/dependencies");
        if (!dependencyNode.isPresent()) {
            return Optional.absent();
        }
        Optional<Node> versionNode = Utils.getNode("./version", dependencyNode.get());

        if (versionNode.isPresent()) {
            return Optional.of(versionNode.get().getTextContent().trim());
        }

        return Optional.absent();
    }

    @Override
    public Optional<String> getVersionPropertyName(String version) {
        Matcher matcher = Pattern.compile("\\s*\\$\\{([^}]+)\\}\\s*").matcher(version);
        if (matcher.matches()) {
            String name = matcher.group(1);
            return Optional.of(name);
        }
        return Optional.absent();
    }

    @Override
    public boolean hasXmlDeclaration() {
        try (InputStream fileInput = new FileInputStream(project.getFile());
                Reader streamReader = new InputStreamReader(fileInput);
                BufferedReader reader = new BufferedReader(streamReader)) {

            for (int i = 0; i < 3; i++) {
                String line = reader.readLine();
                if (line == null) {
                    break;
                }
                if (line.trim().startsWith("<?xml")) {
                    return true;
                }
            }

            return false;

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean isParentAvailable() {

        if (!useParent) {
            return false;
        }

        boolean parentPomIsAvailable = project.getParent() != null && project.getParent().getFile() != null
                && project.getParent().getFile().exists();

        return parentPomIsAvailable;
    }

    @Override
    public PomSpy getParentSpy() {
        return new PomSpyImpl(project.getParent(), repository, factory, metadataSource, collector, treeBuilder,
                useParent);
    }

    @Override
    public boolean hasDepsManagement() {
        return Utils.getNode("/project/dependencyManagement", document).isPresent();
    }

    @Override
    public boolean isManaged(TransitiveDependency dependency) {

        if (project.getDependencyManagement() == null
                || project.getDependencyManagement().getDependencies() == null) {
            return false;
        }

        return Utils
                .getDependencyNode(dependency.getArtifact(), document, "/project/dependencyManagement/dependencies")
                .isPresent();
    }

    @Override
    public String getManagedVersion(TransitiveDependency dependency) {

        Optional<Node> dependencyNode = Utils.getDependencyNode(dependency.getArtifact(), document,
                "/project/dependencyManagement/dependencies");
        Optional<Node> versionNode = Utils.getNode("./version", dependencyNode.get());
        return versionNode.get().getTextContent().trim();
    }

    @Override
    public boolean isLowerThanCurrent(TransitiveDependency dependency) {

        try {
            Optional<Node> dependencyNode = Utils.getDependencyNode(dependency.getArtifact(), document,
                    "/project/dependencies");
            if (dependencyNode.isPresent()) {
                Optional<Node> currentVersionNode = Utils.getNode("./version", dependencyNode.get());
                if (currentVersionNode.isPresent()) {
                    return isLowerThanCurrent(dependency, currentVersionNode);
                }
            }

            Optional<Node> managedDependencyNode = Utils.getDependencyNode(dependency.getArtifact(), document,
                    "/project/dependencyManagement/dependencies");
            if (managedDependencyNode.isPresent()) {
                Optional<Node> currentVersionNode = Utils.getNode("./version", managedDependencyNode.get());
                if (currentVersionNode.isPresent()) {
                    return isLowerThanCurrent(dependency, currentVersionNode);
                }
            }

            return false;
        } catch (InvalidVersionSpecificationException | OverConstrainedVersionException e) {
            throw new RuntimeException(e);
        }
    }

    @SuppressWarnings("unchecked")
    private boolean isLowerThanCurrent(TransitiveDependency newDependency, Optional<Node> currentVersionNode)
            throws InvalidVersionSpecificationException, OverConstrainedVersionException {
        String version = currentVersionNode.get().getTextContent().trim();
        Optional<String> propertyName = getVersionPropertyName(version);
        if (propertyName.isPresent()) {
            Optional<Node> propertyNode = Utils.getNode("/project/properties/" + propertyName.get(), document);
            if (!propertyNode.isPresent()) {
                return false;
            }
            version = propertyNode.get().getTextContent().trim();
        }
        VersionRange versionRange = VersionRange.createFromVersionSpec(version);
        ArtifactVersion currentVersion = versionRange.getSelectedVersion(newDependency.getArtifact());
        int result = newDependency.getVersion().compareTo(currentVersion);
        return result < 0;
    }

    MavenProject getProject() {
        return project;
    }
}