net.sf.yal10n.svn.SVNUtil.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.yal10n.svn.SVNUtil.java

Source

package net.sf.yal10n.svn;

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.security.MessageDigest;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import net.sf.yal10n.settings.ScmType;

import org.apache.commons.lang.StringUtils;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.scm.ChangeFile;
import org.apache.maven.scm.ChangeSet;
import org.apache.maven.scm.CommandParameters;
import org.apache.maven.scm.ScmException;
import org.apache.maven.scm.ScmFileSet;
import org.apache.maven.scm.ScmFileStatus;
import org.apache.maven.scm.ScmResult;
import org.apache.maven.scm.ScmRevision;
import org.apache.maven.scm.command.changelog.ChangeLogScmRequest;
import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
import org.apache.maven.scm.command.checkout.CheckOutScmResult;
import org.apache.maven.scm.command.diff.DiffScmResult;
import org.apache.maven.scm.command.info.InfoItem;
import org.apache.maven.scm.command.info.InfoScmResult;
import org.apache.maven.scm.manager.BasicScmManager;
import org.apache.maven.scm.manager.ScmManager;
import org.apache.maven.scm.provider.ScmProvider;
import org.apache.maven.scm.provider.ScmProviderRepository;
import org.apache.maven.scm.provider.git.gitexe.GitExeScmProvider;
import org.apache.maven.scm.provider.svn.svnexe.SvnExeScmProvider;
import org.apache.maven.scm.repository.ScmRepository;
import org.codehaus.plexus.component.annotations.Component;

/**
 * Simple SVN utility for checking out files from subversion.
 */
@Component(role = SVNUtil.class, hint = "SVNUtil")
public class SVNUtil {
    private static final int BYTE_MASK = 0xff;

    private ScmManager scmManager;

    /**
     * Instantiates a new SVN util.
     */
    public SVNUtil() {
        scmManager = new BasicScmManager();
        scmManager.setScmProvider("svn", new SvnExeScmProvider());
        scmManager.setScmProvider("git", new GitExeScmProvider());
    }

    /**
     * Determines the correct url. The url can start with a dot or two dots,
     * which will be interpreted as a relative file url.
     * @param svnUrl the url
     * @return the SVN url
     */
    private String getUrl(String svnUrl) {
        if (svnUrl.startsWith("..") || svnUrl.startsWith(".")) {
            String completePath = new File(".", svnUrl).getAbsolutePath();
            return "file://" + completePath;
        }
        return svnUrl;
    }

    private String createScmSvnUrl(ScmType type, String svnUrl) {
        String result = getUrl(svnUrl);
        if (!result.startsWith("scm:")) {
            switch (type) {
            case SVN:
                result = "scm:svn:" + result;
                break;
            case GIT:
                result = "scm:git:" + result;
                break;
            default:
                throw new RuntimeException("Scm type " + type + " is not supported.");
            }
        }
        return result;
    }

    private void checkResult(ScmResult result) {
        if (!result.isSuccess()) {
            System.err.println("Provider message:");

            System.err.println(result.getProviderMessage() == null ? "" : result.getProviderMessage());

            System.err.println("Command output:");

            System.err.println(result.getCommandOutput() == null ? "" : result.getCommandOutput());

            throw new RuntimeException("Command failed." + StringUtils.defaultString(result.getProviderMessage()));
        }
    }

    /**
     * Checkout from the given svn url to the destination directory.
     *
     * @param log the log
     * @param type the scm type
     * @param svnUrl the svn url
     * @param destination the destination
     * @return the current checked out version
     */
    public String checkout(Log log, ScmType type, String svnUrl, String destination) {
        try {
            log.info("Updating " + svnUrl);

            String scmUrl = createScmSvnUrl(type, svnUrl);
            log.debug("Converted Url: " + scmUrl);

            ScmProvider scm = scmManager.getProviderByUrl(scmUrl);
            ScmRepository repository = scmManager.makeScmRepository(scmUrl);
            ScmProviderRepository providerRepository = repository.getProviderRepository();

            File dstPath = new File(destination);
            if (dstPath.exists() && !dstPath.isDirectory()) {
                throw new RuntimeException("Path is not a directory: " + dstPath);
            } else if (!dstPath.exists() && !dstPath.mkdirs()) {
                throw new RuntimeException("Couldn't create directory " + dstPath);
            }
            CheckOutScmResult checkOutResult = scm.checkOut(repository, new ScmFileSet(dstPath));
            checkResult(checkOutResult);
            String revision = checkOutResult.getRevision();
            if (revision == null) {
                InfoScmResult info = scm.info(providerRepository, new ScmFileSet(dstPath), null);
                checkResult(info);
                revision = info.getInfoItems().get(0).getRevision();
            }
            log.info("At revision " + revision);
            return revision;
        } catch (ScmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Gets the information about a file in a local working directory.
     *
     * @param log the log
     * @param type the scm type
     * @param svnUrl the checked out repository url
     * @param baseDir the directory where the repository has been checked out
     * @param relativeFilePath the file to check relative to baseDir
     * @return the svn information like revision.
     */
    public SVNInfo checkFile(Log log, ScmType type, String svnUrl, String baseDir, String relativeFilePath) {
        try {
            String scmUrl = createScmSvnUrl(type, svnUrl);
            ScmProvider scm = scmManager.getProviderByUrl(scmUrl);
            ScmRepository repository = scmManager.makeScmRepository(scmUrl);
            ScmProviderRepository providerScmRepository = repository.getProviderRepository();
            ScmFileSet fileSet = new ScmFileSet(new File(baseDir), new File(relativeFilePath));
            InfoScmResult info = scm.info(providerScmRepository, fileSet, (CommandParameters) null);
            checkResult(info);
            InfoItem item = info.getInfoItems().get(0);
            return new SVNInfo(item.getLastChangedRevision(), item.getLastChangedDate());
        } catch (ScmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Determines whether a given file has been modified between two revisions.
     *
     * @param log the log
     * @param type the scm type
     * @param svnUrl the repository url
     * @param checkoutDir the checkout directory
     * @param relativeFilePath the file to check, relative to the checkout directory
     * @param baseRevision the old revision (exclusive)
     * @param newRevision the new revision (inclusive)
     * @return the change type, e.g. ADD, MODIFICATION or NONE
     */
    public SVNLogChange log(Log log, ScmType type, String svnUrl, String checkoutDir, String relativeFilePath,
            String baseRevision, String newRevision) {
        try {
            String scmUrl = createScmSvnUrl(type, svnUrl);
            ScmProvider scm = scmManager.getProviderByUrl(scmUrl);
            ScmRepository repository = scmManager.makeScmRepository(scmUrl);
            ChangeLogScmRequest scmRequest = new ChangeLogScmRequest(repository,
                    new ScmFileSet(new File(checkoutDir), new File(relativeFilePath)));
            scmRequest.setStartRevision(new ScmRevision(baseRevision));
            scmRequest.setEndRevision(new ScmRevision(newRevision));
            ChangeLogScmResult changeLog = scm.changeLog(scmRequest);
            checkResult(changeLog);

            Set<String> changeTypes = new HashSet<String>();
            List<ChangeSet> changeSets = changeLog.getChangeLog().getChangeSets();
            for (ChangeSet cs : changeSets) {
                if (cs.getRevision().equals(baseRevision)) {
                    continue;
                }
                for (ChangeFile f : cs.getFiles()) {
                    if (f.getName().endsWith(relativeFilePath)) {
                        changeTypes.add(String.valueOf(f.getAction()));
                    }
                }
            }
            SVNLogChange result;
            if (changeTypes.isEmpty()) {
                result = SVNLogChange.NONE;
            } else if (changeTypes.contains(ScmFileStatus.ADDED.toString())) {
                result = SVNLogChange.ADD;
            } else {
                result = SVNLogChange.MODIFICATION;
            }
            return result;
        } catch (ScmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Retrieves a unified diff for a given file and revision.
     *
     * @param log the log
     * @param type the scm type
     * @param svnUrl the repository url
     * @param checkoutDir the checkout directory
     * @param relativeFilePath the file to diff, relative to the checkout directory
     * @param baseRevision the old revision
     * @param newRevision the new revision
     * @return the diff as a string
     */
    public String diff(Log log, ScmType type, String svnUrl, String checkoutDir, String relativeFilePath,
            String baseRevision, String newRevision) {
        try {
            String scmUrl = createScmSvnUrl(type, svnUrl);
            ScmRepository repository = scmManager.makeScmRepository(scmUrl);

            ScmFileSet scmFileSet = new ScmFileSet(new File(checkoutDir), new File(relativeFilePath));
            DiffScmResult diffResult = scmManager.diff(repository, scmFileSet, new ScmRevision(baseRevision),
                    new ScmRevision(newRevision));
            checkResult(diffResult);
            return filterPatch(diffResult.getPatch(), relativeFilePath);
        } catch (ScmException e) {
            throw new RuntimeException(e);
        }
    }

    private static String filterPatch(String patch, String relativeFilePath) {
        BufferedReader r = new BufferedReader(new StringReader(patch));
        String line;
        StringBuilder newPatch = new StringBuilder();
        boolean fileStarted = false;
        try {
            while ((line = r.readLine()) != null) {
                if (!fileStarted && line.startsWith("Index: " + relativeFilePath)) {
                    newPatch.append(line).append('\n');
                    fileStarted = true;
                } else if (fileStarted && line.startsWith("Index: ")) {
                    fileStarted = false;
                } else if (fileStarted) {
                    newPatch.append(line).append('\n');
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return newPatch.toString();
    }

    /**
     * Utility method to combine a prefix and url to get the complete repository URL.
     * Duplicated slashed will be removed.
     * @param repoPrefix the prefix
     * @param repoUrl the url
     * @return the complete repo url
     */
    public static String toCompleteUrl(String repoPrefix, String repoUrl) {
        String prefix = StringUtils.trimToEmpty(repoPrefix);
        String suffix = StringUtils.trimToEmpty(repoUrl);
        if (!prefix.isEmpty() && !prefix.endsWith("/")) {
            prefix = prefix + "/";
        }
        if (suffix.startsWith("/")) {
            suffix = suffix.substring(1);
        }
        if (suffix.endsWith("/")) {
            suffix = suffix.substring(0, suffix.lastIndexOf("/"));
        }
        return prefix + suffix;
    }

    /**
     * Calculates a unique id for the given repo url.
     * @param repoPrefix the prefix
     * @param repoUrl the url
     * @return the id
     */
    public static String toRepoId(String repoPrefix, String repoUrl) {
        String completeUrl = toCompleteUrl(repoPrefix, repoUrl);
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] md5 = digest.digest(completeUrl.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder(32);
            for (int i = 0; i < md5.length; i++) {
                int part = md5[i] & BYTE_MASK;
                String h = Integer.toHexString(part);
                if (part < 0x10) {
                    sb.append("0");
                }
                sb.append(h);
            }
            return sb.toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}