com.door43.translationstudio.tasks.PullTargetTranslationTask.java Source code

Java tutorial

Introduction

Here is the source code for com.door43.translationstudio.tasks.PullTargetTranslationTask.java

Source

package com.door43.translationstudio.tasks;

import android.os.Process;

import org.unfoldingword.tools.logger.Logger;

import com.door43.translationstudio.App;
import com.door43.translationstudio.R;
import com.door43.translationstudio.ui.SettingsActivity;
import com.door43.translationstudio.core.Profile;
import com.door43.translationstudio.core.TargetTranslation;
import com.door43.translationstudio.git.Repo;
import com.door43.translationstudio.git.TransportCallback;
import com.door43.util.Manifest;
import org.unfoldingword.tools.taskmanager.ManagedTask;

import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.CreateBranchCommand;
import org.eclipse.jgit.api.DeleteBranchCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.MergeResult;
import org.eclipse.jgit.api.PullCommand;
import org.eclipse.jgit.api.PullResult;
import org.eclipse.jgit.api.errors.CheckoutConflictException;
import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.errors.NoRemoteRepositoryException;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.merge.MergeStrategy;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * Pulls down changes from a remote target translation repository
 */
public class PullTargetTranslationTask extends ManagedTask {

    public static final String TASK_ID = "pull_target_translation_task";
    public final TargetTranslation targetTranslation;
    private final MergeStrategy mergeStrategy;
    private String message = "";
    private Status status = Status.UNKNOWN;
    private Map<String, int[][]> conflicts = new HashMap<>();
    private String sourceURL = null;

    /**
     * do a pull from a specific URL
     * @param targetTranslation
     * @param mergeStrategy
     * @param sourceURL
     */
    public PullTargetTranslationTask(TargetTranslation targetTranslation, MergeStrategy mergeStrategy,
            String sourceURL) {
        setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
        this.targetTranslation = targetTranslation;
        this.mergeStrategy = mergeStrategy;
        this.sourceURL = sourceURL;
    }

    @Override
    public void start() {
        if (App.isNetworkAvailable()) {
            // submit new language requests
            delegate(new SubmitNewLanguageRequestsTask());

            Profile profile = App.getProfile();
            if (targetTranslation != null && App.isNetworkAvailable() && profile != null
                    && profile.gogsUser != null) {
                publishProgress(-1, "Downloading updates");

                if (sourceURL == null) {
                    String server = App.context().getUserPreferences().getString(
                            SettingsActivity.KEY_PREF_GIT_SERVER,
                            App.context().getResources().getString(R.string.pref_default_git_server));
                    sourceURL = server + ":" + profile.gogsUser.getUsername() + "/" + this.targetTranslation.getId()
                            + ".git";
                }

                try {
                    this.targetTranslation.commitSync();
                } catch (Exception e) {
                    Logger.w(this.getClass().getName(),
                            "Failed to commit the target translation " + targetTranslation.getId(), e);
                }
                Repo repo = this.targetTranslation.getRepo();
                createBackupBranch(repo);
                this.message = pull(repo, sourceURL);
            }
        }
    }

    private void createBackupBranch(Repo repo) {
        try {
            Git git = repo.getGit();
            DeleteBranchCommand deleteBranchCommand = git.branchDelete();
            deleteBranchCommand.setBranchNames("backup-master").setForce(true).call();
            CreateBranchCommand createBranchCommand = git.branchCreate();
            createBranchCommand.setName("backup-master").setForce(true).call();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String pull(Repo repo, String remote) {
        Git git;
        try {
            repo.deleteRemote("origin");
            repo.setRemote("origin", remote);
            git = repo.getGit();
        } catch (IOException e) {
            return null;
        }

        Manifest localManifest = Manifest.generate(this.targetTranslation.getPath());

        // TODO: we might want to get some progress feedback for the user
        PullCommand pullCommand = git.pull().setTransportConfigCallback(new TransportCallback()).setRemote("origin")
                .setStrategy(mergeStrategy).setRemoteBranchName("master").setProgressMonitor(new ProgressMonitor() {
                    @Override
                    public void start(int totalTasks) {

                    }

                    @Override
                    public void beginTask(String title, int totalWork) {

                    }

                    @Override
                    public void update(int completed) {

                    }

                    @Override
                    public void endTask() {

                    }

                    @Override
                    public boolean isCancelled() {
                        return false;
                    }
                });
        try {
            PullResult result = pullCommand.call();
            MergeResult mergeResult = result.getMergeResult();
            if (mergeResult != null && mergeResult.getConflicts() != null
                    && mergeResult.getConflicts().size() > 0) {
                this.status = Status.MERGE_CONFLICTS;
                this.conflicts = mergeResult.getConflicts();

                // revert manifest merge conflict to avoid corruption
                if (this.conflicts.containsKey("manifest.json")) {
                    Logger.i("PullTargetTranslationTask", "Reverting to server manifest");
                    try {
                        git.checkout().setStage(CheckoutCommand.Stage.THEIRS).addPath("manifest.json").call();
                        Manifest remoteManifest = Manifest.generate(this.targetTranslation.getPath());
                        localManifest = TargetTranslation.mergeManifests(localManifest, remoteManifest);
                    } catch (CheckoutConflictException e) {
                        // failed to reset manifest.json
                        Logger.e(this.getClass().getName(), "Failed to reset manifest: " + e.getMessage(), e);
                    } finally {
                        localManifest.save();
                    }
                }

                // keep our license
                if (this.conflicts.containsKey("LICENSE.md")) {
                    Logger.i("PullTargetTranslationTask", "Reverting to local license");
                    try {
                        git.checkout().setStage(CheckoutCommand.Stage.OURS).addPath("LICENSE.md").call();
                    } catch (CheckoutConflictException e) {
                        Logger.e(this.getClass().getName(), "Failed to reset license: " + e.getMessage(), e);
                    }
                }
            } else {
                this.status = Status.UP_TO_DATE;
            }

            return "message";
        } catch (TransportException e) {
            Logger.e(this.getClass().getName(), e.getMessage(), e);
            Throwable cause = e.getCause();
            if (cause != null) {
                Throwable subException = cause.getCause();
                if (subException != null) {
                    String detail = subException.getMessage();
                    if ("Auth fail".equals(detail)) {
                        this.status = Status.AUTH_FAILURE; // we do special handling for auth failure
                    }
                } else if (cause instanceof NoRemoteRepositoryException) {
                    this.status = Status.NO_REMOTE_REPO;
                }
            }
            return null;
        } catch (Exception e) {
            Throwable cause = e.getCause();
            if (cause instanceof NoRemoteRepositoryException) {
                this.status = Status.NO_REMOTE_REPO;
            }
            Logger.e(this.getClass().getName(), e.getMessage(), e);
            return null;
        } catch (OutOfMemoryError e) {
            Logger.e(this.getClass().getName(), e.getMessage(), e);
            this.status = Status.OUT_OF_MEMORY;
            return null;
        } catch (Throwable e) {
            Logger.e(this.getClass().getName(), e.getMessage(), e);
            return null;
        }
    }

    public String getMessage() {
        return message;
    }

    public Status getStatus() {
        return status;
    }

    public enum Status {
        UP_TO_DATE, MERGE_CONFLICTS, OUT_OF_MEMORY, AUTH_FAILURE, NO_REMOTE_REPO, UNKNOWN
    }
}