DiffCommand.java

package org.docascode.api;

import org.docascode.api.core.DocAsCodeRepository;
import org.docascode.api.core.errors.DocAsCodeException;
import org.docascode.api.diff.Differencer;
import org.docascode.api.event.Event;
import org.docascode.api.retrieve.GitRetriever;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.version.Version;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.io.File;

public class DiffCommand extends DocAsCodeCommand<DiffCommand>{
    private String oldRevision;

    public DiffCommand setOldRevision(String oldRevision){
        this.oldRevision = oldRevision;
        return this;
    }

    private String newRevision;

    public DiffCommand setNewRevision(String newRevision){
        this.newRevision = newRevision;
        return this;
    }

    private String revision;

    public DiffCommand setRevision(String revision){
        this.revision = revision;
        return this;
    }

    private File toFile;

    public DiffCommand toFile(File toFile){
        this.toFile = toFile;
        return this;
    }

    private File file;

    public DiffCommand setFile(File file){
        this.file = file;
        return this;
    }

    DiffCommand(DocAsCodeRepository repo) {
        super(repo);
    }

    private boolean openAtEnd = false;

    public DiffCommand openAtEnd(boolean openAtEnd){
        this.openAtEnd = openAtEnd;
        return this;
    }

    private File resolve(String revision) throws DocAsCodeException {
        String path = getRepository().relativize(file);
        Map<String, org.eclipse.aether.artifact.Artifact> artifactMap =
                ((DocAsCodeRepository) getRepository().addListener(this)).chrono().toArtifactFileMap();
        if (artifactMap.keySet().contains(path)){
            if (revision.equals("WorkTree")){
                return file;
            }
            org.eclipse.aether.artifact.Artifact artifact = artifactMap.get(path);
            List<Version> listVersion = getRepository().mvn()
                    .addRemoteRepositories(null) //default repository
                    .getVersionRange(artifact);
            List<String> listVersionString = new ArrayList<>();
            for (Version v : listVersion){
                listVersionString.add(v.toString());
            }
            if (listVersionString.contains(revision) ||
                    (!listVersionString.isEmpty() && revision.equals("LATEST")) ){
                artifact = artifact.setVersion(revision);
                ArtifactResult artifactResult = getRepository().mvn().resolve(artifact);
                return artifactResult.getArtifact().getFile();
            }
        }
        try (RevWalk revWalk = new RevWalk(getRepository().git())){
            ObjectId revisionId = getRepository().git().resolve(revision);
            if (revisionId!= null){
                RevCommit commit = revWalk.parseCommit(revisionId);
                return new GitRetriever()
                        .setCommit(commit)
                        .setPath(path)
                        .setRepository(getRepository().git())
                        .toDir(new File(
                                getRepository().getTmpDir().getAbsolutePath(),
                                commit.getName()))
                        .call();
            } else {
                Event e = new Event(this);
                e.setMessage(String.format(
                        "File '%s' not found at revision '%s'...",
                        path,
                        revision));
                fireEvent(e);
                return null;
            }
        } catch (IOException e) {
            throw new DocAsCodeException(
                String.format(
                    "Revision '%s' not found in git repository",
                    revision),
                e
            );
        }
    }

    @Override
    public DiffCommand call() throws DocAsCodeException {
        File newFile;
        if (newRevision == null){
            newFile = file;
            newRevision = "WorkTree";
        } else {
            newFile = resolve(newRevision);
        }
        if (revision==null){
            revision = oldRevision+":"+newRevision;
        }
        File oldFile = resolve(oldRevision);
        try {
            Files.deleteIfExists(Paths.get(toFile.getAbsolutePath()));
            toFile.getParentFile().mkdirs();
            if (!toFile.createNewFile()){
                throw new DocAsCodeException(
                        String.format(
                                "Unable to create file '%s'",
                                toFile));
            }
        } catch (IOException e) {
            throw new DocAsCodeException(
                    String.format(
                            "Unable to create file '%s'",
                            toFile),e);
        }
        Differencer diff = (Differencer) new Differencer().addListener(this);
        diff.setBaseFile(oldFile)
                .setRevisedFile(newFile)
                .setRevision(this.revision)
                .setTargetFile(toFile)
                .openAtEnd(openAtEnd)
                .diff();
        return this;
    }
}