001package org.apache.maven.scm.provider.starteam;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.File;
023import java.io.IOException;
024import java.util.ArrayList;
025import java.util.List;
026
027import org.apache.maven.scm.CommandParameters;
028import org.apache.maven.scm.ScmException;
029import org.apache.maven.scm.ScmFileSet;
030import org.apache.maven.scm.command.add.AddScmResult;
031import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
032import org.apache.maven.scm.command.checkin.CheckInScmResult;
033import org.apache.maven.scm.command.checkout.CheckOutScmResult;
034import org.apache.maven.scm.command.diff.DiffScmResult;
035import org.apache.maven.scm.command.edit.EditScmResult;
036import org.apache.maven.scm.command.remove.RemoveScmResult;
037import org.apache.maven.scm.command.status.StatusScmResult;
038import org.apache.maven.scm.command.tag.TagScmResult;
039import org.apache.maven.scm.command.unedit.UnEditScmResult;
040import org.apache.maven.scm.command.update.UpdateScmResult;
041import org.apache.maven.scm.provider.AbstractScmProvider;
042import org.apache.maven.scm.provider.ScmProviderRepository;
043import org.apache.maven.scm.provider.starteam.command.add.StarteamAddCommand;
044import org.apache.maven.scm.provider.starteam.command.changelog.StarteamChangeLogCommand;
045import org.apache.maven.scm.provider.starteam.command.checkin.StarteamCheckInCommand;
046import org.apache.maven.scm.provider.starteam.command.checkout.StarteamCheckOutCommand;
047import org.apache.maven.scm.provider.starteam.command.diff.StarteamDiffCommand;
048import org.apache.maven.scm.provider.starteam.command.edit.StarteamEditCommand;
049import org.apache.maven.scm.provider.starteam.command.remove.StarteamRemoveCommand;
050import org.apache.maven.scm.provider.starteam.command.status.StarteamStatusCommand;
051import org.apache.maven.scm.provider.starteam.command.tag.StarteamTagCommand;
052import org.apache.maven.scm.provider.starteam.command.unedit.StarteamUnEditCommand;
053import org.apache.maven.scm.provider.starteam.command.update.StarteamUpdateCommand;
054import org.apache.maven.scm.provider.starteam.repository.StarteamScmProviderRepository;
055import org.apache.maven.scm.repository.ScmRepositoryException;
056import org.codehaus.plexus.util.StringUtils;
057
058/**
059 * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
060 *
061 * @plexus.component role="org.apache.maven.scm.provider.ScmProvider" role-hint="starteam"
062 */
063public class StarteamScmProvider
064    extends AbstractScmProvider
065{
066    public static final String STARTEAM_URL_FORMAT =
067        "[username[:password]@]hostname:port:/projectName/[viewName/][folderHiearchy/]";
068
069    // ----------------------------------------------------------------------
070    // ScmProvider Implementation
071    // ----------------------------------------------------------------------
072
073    /** {@inheritDoc} */
074    public ScmProviderRepository makeProviderScmRepository( String scmSpecificUrl, char delimiter )
075        throws ScmRepositoryException
076    {
077        String user = null;
078
079        String password = null;
080
081        int index = scmSpecificUrl.indexOf( '@' );
082
083        String rest = scmSpecificUrl;
084
085        if ( index != -1 )
086        {
087            String userAndPassword = scmSpecificUrl.substring( 0, index );
088
089            rest = scmSpecificUrl.substring( index + 1 );
090
091            index = userAndPassword.indexOf( ':' );
092
093            if ( index != -1 )
094            {
095                user = userAndPassword.substring( 0, index );
096
097                password = userAndPassword.substring( index + 1 );
098            }
099            else
100            {
101                user = userAndPassword;
102            }
103        }
104
105        String[] tokens = StringUtils.split( rest, Character.toString( delimiter ) );
106
107        String host;
108
109        int port;
110
111        String path;
112
113        if ( tokens.length == 3 )
114        {
115            host = tokens[0];
116
117            port = new Integer( tokens[1] ).intValue();
118
119            path = tokens[2];
120        }
121        else if ( tokens.length == 2 )
122        {
123            if ( getLogger().isWarnEnabled() )
124            {
125                getLogger().warn(
126                                  "Your scm URL use a deprecated format. The new format is :"
127                                      + STARTEAM_URL_FORMAT );
128            }
129
130            host = tokens[0];
131
132            if ( tokens[1].indexOf( '/' ) == -1 )
133            {
134                throw new ScmRepositoryException(
135                    "Invalid SCM URL: The url has to be on the form: " + STARTEAM_URL_FORMAT );
136            }
137
138            int at = tokens[1].indexOf( '/' );
139
140            port = new Integer( tokens[1].substring( 0, at ) ).intValue();
141
142            path = tokens[1].substring( at );
143        }
144        else
145        {
146            throw new ScmRepositoryException(
147                "Invalid SCM URL: The url has to be on the form: " + STARTEAM_URL_FORMAT );
148        }
149
150        try
151        {
152            return new StarteamScmProviderRepository( user, password, host, port, path );
153        }
154        catch ( Exception e )
155        {
156            throw new ScmRepositoryException(
157                "Invalid SCM URL: The url has to be on the form: " + STARTEAM_URL_FORMAT );
158        }
159    }
160
161    public String getScmType()
162    {
163        return "starteam";
164    }
165
166    /** {@inheritDoc} */
167    public AddScmResult add( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
168        throws ScmException
169    {
170        fileSet = fixUpScmFileSetAbsoluteFilePath( fileSet );
171
172        StarteamAddCommand command = new StarteamAddCommand();
173
174        command.setLogger( getLogger() );
175
176        return (AddScmResult) command.execute( repository, fileSet, parameters );
177    }
178
179    /** {@inheritDoc} */
180    public ChangeLogScmResult changelog( ScmProviderRepository repository, ScmFileSet fileSet,
181                                         CommandParameters parameters )
182        throws ScmException
183    {
184        fileSet = fixUpScmFileSetAbsoluteFilePath( fileSet );
185
186        StarteamChangeLogCommand command = new StarteamChangeLogCommand();
187
188        command.setLogger( getLogger() );
189
190        return (ChangeLogScmResult) command.execute( repository, fileSet, parameters );
191    }
192
193    /** {@inheritDoc} */
194    public CheckInScmResult checkin( ScmProviderRepository repository, ScmFileSet fileSet,
195                                     CommandParameters parameters )
196        throws ScmException
197    {
198        fileSet = fixUpScmFileSetAbsoluteFilePath( fileSet );
199
200        StarteamCheckInCommand command = new StarteamCheckInCommand();
201
202        command.setLogger( getLogger() );
203
204        return (CheckInScmResult) command.execute( repository, fileSet, parameters );
205    }
206
207    /** {@inheritDoc} */
208    public CheckOutScmResult checkout( ScmProviderRepository repository, ScmFileSet fileSet,
209                                       CommandParameters parameters )
210        throws ScmException
211    {
212        fileSet = fixUpScmFileSetAbsoluteFilePath( fileSet );
213
214        StarteamCheckOutCommand command = new StarteamCheckOutCommand();
215
216        command.setLogger( getLogger() );
217
218        return (CheckOutScmResult) command.execute( repository, fileSet, parameters );
219    }
220
221    /** {@inheritDoc} */
222    public DiffScmResult diff( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
223        throws ScmException
224    {
225        fileSet = fixUpScmFileSetAbsoluteFilePath( fileSet );
226
227        StarteamDiffCommand command = new StarteamDiffCommand();
228
229        command.setLogger( getLogger() );
230
231        return (DiffScmResult) command.execute( repository, fileSet, parameters );
232    }
233
234    /** {@inheritDoc} */
235    public StatusScmResult status( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
236        throws ScmException
237    {
238        fileSet = fixUpScmFileSetAbsoluteFilePath( fileSet );
239
240        StarteamStatusCommand command = new StarteamStatusCommand();
241
242        command.setLogger( getLogger() );
243
244        return (StatusScmResult) command.execute( repository, fileSet, parameters );
245    }
246
247    /** {@inheritDoc} */
248    public TagScmResult tag( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
249        throws ScmException
250    {
251        fileSet = fixUpScmFileSetAbsoluteFilePath( fileSet );
252
253        StarteamTagCommand command = new StarteamTagCommand();
254
255        command.setLogger( getLogger() );
256
257        return (TagScmResult) command.execute( repository, fileSet, parameters );
258    }
259
260    /** {@inheritDoc} */
261    public UpdateScmResult update( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
262        throws ScmException
263    {
264        fileSet = fixUpScmFileSetAbsoluteFilePath( fileSet );
265
266        StarteamUpdateCommand command = new StarteamUpdateCommand();
267
268        command.setLogger( getLogger() );
269
270        return (UpdateScmResult) command.execute( repository, fileSet, parameters );
271    }
272
273    /** {@inheritDoc} */
274    protected EditScmResult edit( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
275        throws ScmException
276    {
277        fileSet = fixUpScmFileSetAbsoluteFilePath( fileSet );
278
279        StarteamEditCommand command = new StarteamEditCommand();
280
281        command.setLogger( getLogger() );
282
283        return (EditScmResult) command.execute( repository, fileSet, parameters );
284    }
285
286    /** {@inheritDoc} */
287    protected UnEditScmResult unedit( ScmProviderRepository repository, ScmFileSet fileSet,
288                                      CommandParameters parameters )
289        throws ScmException
290    {
291        fileSet = fixUpScmFileSetAbsoluteFilePath( fileSet );
292
293        StarteamUnEditCommand command = new StarteamUnEditCommand();
294
295        command.setLogger( getLogger() );
296
297        return (UnEditScmResult) command.execute( repository, fileSet, parameters );
298    }
299
300    /** {@inheritDoc} */
301    public RemoveScmResult remove( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
302        throws ScmException
303    {
304        fileSet = fixUpScmFileSetAbsoluteFilePath( fileSet );
305
306        StarteamRemoveCommand command = new StarteamRemoveCommand();
307
308        command.setLogger( getLogger() );
309
310        return (RemoveScmResult) command.execute( repository, fileSet, parameters );
311    }
312
313    /**
314     * Starteam provider requires that all files in ScmFileSet must be relative to basedir
315     * This function ensures and converts all absolute paths to relative paths
316     *
317     * @param currentFileSet
318     * @return
319     * @throws ScmException
320     */
321    private static ScmFileSet fixUpScmFileSetAbsoluteFilePath( ScmFileSet currentFileSet )
322        throws ScmException
323    {
324        ScmFileSet newFileSet = null;
325        
326        try
327        {
328            File basedir = getAbsoluteFilePath( currentFileSet.getBasedir() );
329
330            List<File> files = currentFileSet.getFileList();
331
332            List<File> relPathFiles = new ArrayList<File>(files.size());
333            
334            for ( File file : files )
335            {
336                if ( file.isAbsolute() )
337                {
338                    relPathFiles.add( new File( getRelativePath( basedir, file ) ));
339                } else {
340                    relPathFiles.add( file );
341                }
342                
343            }
344
345            newFileSet = new ScmFileSet( basedir, relPathFiles );
346        }
347        catch ( IOException e )
348        {
349            throw new ScmException( "Invalid file set.", e );
350        }
351
352        return newFileSet;
353    }
354
355    public static String getRelativePath( File basedir, File f )
356        throws ScmException, IOException
357    {
358        File fileOrDir = getAbsoluteFilePath( f );
359
360        if ( !fileOrDir.getCanonicalPath().startsWith( basedir.getCanonicalPath() ) )
361        {
362            throw new ScmException( fileOrDir.getPath() + " was not contained in " + basedir.getPath() );
363        }
364
365        if ( basedir.getCanonicalFile().equals(basedir.getAbsoluteFile()) )
366        {
367            return fileOrDir.getPath().substring( basedir.getPath().length() + 1, fileOrDir.getPath().length() );
368        }
369        return fileOrDir.getPath().substring( basedir.getCanonicalPath().length() + 1, fileOrDir.getPath().length() );
370    }
371
372    private static File getAbsoluteFilePath( File fileOrDir )
373        throws IOException
374    {
375        String javaPathString = fileOrDir.getCanonicalPath().replace( '\\', '/' );
376
377        if ( javaPathString.endsWith( "/" ) )
378        {
379            javaPathString = javaPathString.substring( 0, javaPathString.length() - 1 );
380        }
381
382        return new File( javaPathString );
383    }
384}