All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.yal10n.svn.SVNUtil Maven / Gradle / Ivy

The newest version!
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 changeTypes = new HashSet();
            List 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 );
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy