View Javadoc

1   package org.apache.maven.scm.provider.cvslib;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.scm.CommandParameters;
23  import org.apache.maven.scm.ScmException;
24  import org.apache.maven.scm.ScmFileSet;
25  import org.apache.maven.scm.ScmResult;
26  import org.apache.maven.scm.command.Command;
27  import org.apache.maven.scm.command.add.AddScmResult;
28  import org.apache.maven.scm.command.branch.BranchScmResult;
29  import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
30  import org.apache.maven.scm.command.checkin.CheckInScmResult;
31  import org.apache.maven.scm.command.checkout.CheckOutScmResult;
32  import org.apache.maven.scm.command.diff.DiffScmResult;
33  import org.apache.maven.scm.command.export.ExportScmResult;
34  import org.apache.maven.scm.command.list.ListScmResult;
35  import org.apache.maven.scm.command.login.LoginScmResult;
36  import org.apache.maven.scm.command.remove.RemoveScmResult;
37  import org.apache.maven.scm.command.status.StatusScmResult;
38  import org.apache.maven.scm.command.tag.TagScmResult;
39  import org.apache.maven.scm.command.update.UpdateScmResult;
40  import org.apache.maven.scm.provider.AbstractScmProvider;
41  import org.apache.maven.scm.provider.ScmProviderRepository;
42  import org.apache.maven.scm.provider.cvslib.repository.CvsScmProviderRepository;
43  import org.apache.maven.scm.repository.ScmRepositoryException;
44  import org.apache.maven.scm.repository.UnknownRepositoryStructure;
45  import org.codehaus.plexus.util.FileUtils;
46  import org.codehaus.plexus.util.StringUtils;
47  
48  import java.io.File;
49  import java.io.IOException;
50  import java.util.ArrayList;
51  import java.util.List;
52  
53  /**
54   * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse </a>
55   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
56   * @version $Id: AbstractCvsScmProvider.java 527150 2007-04-10 15:03:27Z evenisse $
57   */
58  public abstract class AbstractCvsScmProvider
59      extends AbstractScmProvider
60  {
61      /** */
62      public final static String TRANSPORT_LOCAL = "local";
63  
64      /** */
65      public final static String TRANSPORT_PSERVER = "pserver";
66  
67      /** */
68      public final static String TRANSPORT_LSERVER = "lserver";
69  
70      /** */
71      public final static String TRANSPORT_EXT = "ext";
72  
73      /** */
74      public final static String TRANSPORT_SSPI = "sspi";
75  
76      // ----------------------------------------------------------------------
77      //
78      // ----------------------------------------------------------------------
79  
80      private static class ScmUrlParserResult
81      {
82          List messages = new ArrayList();
83  
84          ScmProviderRepository repository;
85      }
86  
87      // ----------------------------------------------------------------------
88      // ScmProvider Implementation
89      // ----------------------------------------------------------------------
90  
91      public String getScmSpecificFilename()
92      {
93          return "CVS";
94      }
95  
96      /* From the Cederqvist:
97      *
98      * "Tag names must start with an uppercase or lowercase letter and can
99      * contain uppercase and lowercase letters, digits, `-', and `_'. The
100     * two tag names BASE and HEAD are reserved for use by CVS. It is expected
101     * that future names which are special to CVS will be specially named,
102     * for example by starting with `.', rather than being named analogously
103     * to BASE and HEAD, to avoid conflicts with actual tag names."
104     */
105 
106     /* (non-Javadoc)
107     * @see org.apache.maven.scm.provider.AbstractScmProvider#sanitizeTagName(java.lang.String)
108     */
109 
110     public String sanitizeTagName( String arg0 )
111     {
112         if ( validateTagName( arg0 ) )
113         {
114             return arg0;
115         }
116 
117         if ( arg0.equals( "HEAD" ) || arg0.equals( "BASE" ) || !arg0.matches( "[A-Za-z].*" ) )
118             /* we don't even bother to sanitize these, they're just silly */
119         {
120             throw new RuntimeException(
121                 "Unable to sanitize tag " + arg0 + ": must begin with a letter" + "and not be HEAD or BASE" );
122         }
123 
124         /* swap all illegal characters for a _ */
125         return arg0.replaceAll( "[^A-Za-z0-9_-]", "_" );
126     }
127 
128     /* (non-Javadoc)
129     * @see org.apache.maven.scm.provider.AbstractScmProvider#validateTagName(java.lang.String)
130     */
131     public boolean validateTagName( String arg0 )
132     {
133         return ( arg0.matches( "[A-Za-z][A-Za-z0-9_-]*" ) && !arg0.equals( "HEAD" ) && !arg0.equals( "BASE" ) );
134     }
135 
136     public ScmProviderRepository makeProviderScmRepository( String scmSpecificUrl, char delimiter )
137         throws ScmRepositoryException
138     {
139         ScmUrlParserResult result = parseScmUrl( scmSpecificUrl, delimiter );
140 
141         if ( result.messages.size() > 0 )
142         {
143             throw new ScmRepositoryException( "The scm url is invalid.", result.messages );
144         }
145 
146         return result.repository;
147     }
148 
149     /**
150      * @see org.apache.maven.scm.provider.AbstractScmProvider#makeProviderScmRepository(java.io.File)
151      */
152     public ScmProviderRepository makeProviderScmRepository( File path )
153         throws ScmRepositoryException, UnknownRepositoryStructure
154     {
155         if ( path == null || !path.isDirectory() )
156         {
157             throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a valid directory." );
158         }
159 
160         File cvsDirectory = new File( path, "CVS" );
161 
162         if ( !cvsDirectory.exists() )
163         {
164             throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a cvs checkout directory." );
165         }
166 
167         File cvsRootFile = new File( cvsDirectory, "Root" );
168 
169         File moduleFile = new File( cvsDirectory, "Repository" );
170 
171         String cvsRoot;
172 
173         String module;
174 
175         try
176         {
177             cvsRoot = FileUtils.fileRead( cvsRootFile ).trim().substring( 1 );
178         }
179         catch ( IOException e )
180         {
181             throw new ScmRepositoryException( "Can't read " + cvsRootFile.getAbsolutePath() );
182         }
183         try
184         {
185             module = FileUtils.fileRead( moduleFile ).trim();
186         }
187         catch ( IOException e )
188         {
189             throw new ScmRepositoryException( "Can't read " + moduleFile.getAbsolutePath() );
190         }
191 
192         return makeProviderScmRepository( cvsRoot + ":" + module, ':' );
193     }
194 
195     public List validateScmUrl( String scmSpecificUrl, char delimiter )
196     {
197         ScmUrlParserResult result = parseScmUrl( scmSpecificUrl, delimiter );
198 
199         return result.messages;
200     }
201 
202     public String getScmType()
203     {
204         return "cvs";
205     }
206 
207     // ----------------------------------------------------------------------
208     //
209     // ----------------------------------------------------------------------
210 
211     private ScmUrlParserResult parseScmUrl( String scmSpecificUrl, char delimiter )
212     {
213         ScmUrlParserResult result = new ScmUrlParserResult();
214 
215         String[] tokens = StringUtils.split( scmSpecificUrl, Character.toString( delimiter ) );
216 
217         if ( tokens.length < 3 )
218         {
219             result.messages.add( "The connection string contains too few tokens." );
220 
221             return result;
222         }
223 
224         String cvsroot;
225 
226         String transport = tokens[0];
227 
228         if ( transport.equalsIgnoreCase( TRANSPORT_LOCAL ) )
229         {
230             // use the local repository directory eg. '/home/cvspublic'
231             cvsroot = tokens[1];
232         }
233         else if ( transport.equalsIgnoreCase( TRANSPORT_PSERVER ) || transport.equalsIgnoreCase( TRANSPORT_LSERVER ) ||
234             transport.equalsIgnoreCase( TRANSPORT_EXT ) || transport.equalsIgnoreCase( TRANSPORT_SSPI ) )
235         {
236             if ( tokens.length != 4 && transport.equalsIgnoreCase( TRANSPORT_EXT ) )
237             {
238                 result.messages.add( "The connection string contains too few tokens." );
239 
240                 return result;
241             }
242             else if ( ( tokens.length < 4 || tokens.length > 6 ) && transport.equalsIgnoreCase( TRANSPORT_PSERVER ) )
243             {
244                 result.messages.add( "The connection string contains too few tokens." );
245 
246                 return result;
247             }
248             else if ( tokens.length < 4 || tokens.length > 5 && !transport.equalsIgnoreCase( TRANSPORT_PSERVER ) )
249             {
250                 result.messages.add( "The connection string contains too few tokens." );
251 
252                 return result;
253             }
254             else if ( tokens.length != 4 && transport.equalsIgnoreCase( TRANSPORT_SSPI ) )
255             {
256                 result.messages.add( "The connection string contains an incorrect number of tokens (should be four)." );
257 
258                 return result;
259             }
260 
261             if ( transport.equalsIgnoreCase( TRANSPORT_LSERVER ) )
262             {
263                 //create the cvsroot as the local socket cvsroot
264                 cvsroot = tokens[1] + ":" + tokens[2];
265             }
266             else
267             {
268                 //create the cvsroot as the remote cvsroot
269                 if ( tokens.length == 4 )
270                 {
271                     cvsroot = ":" + transport + ":" + tokens[1] + ":" + tokens[2];
272                 }
273                 else
274                 {
275                     cvsroot = ":" + transport + ":" + tokens[1] + ":" + tokens[2] + ":" + tokens[3];
276                 }
277             }
278         }
279         else
280         {
281             result.messages.add( "Unknown transport: " + transport );
282 
283             return result;
284         }
285 
286         String user = null;
287 
288         String password = null;
289 
290         String host = null;
291 
292         String path = null;
293 
294         String module = null;
295 
296         int port = -1;
297 
298         if ( transport.equalsIgnoreCase( TRANSPORT_PSERVER ) )
299         {
300             // set default port, it's necessary for checking entries in .cvspass
301             port = 2401;
302 
303             if ( tokens.length == 4 )
304             {
305                 //pserver:[username@]host:path:module
306                 String userhost = tokens[1];
307 
308                 int index = userhost.indexOf( "@" );
309 
310                 if ( index == -1 )
311                 {
312                     host = userhost;
313                 }
314                 else
315                 {
316                     user = userhost.substring( 0, index );
317 
318                     host = userhost.substring( index + 1 );
319                 }
320 
321                 path = tokens[2];
322 
323                 module = tokens[3];
324             }
325             else if ( tokens.length == 6 )
326             {
327                 //pserver:username:password@host:port:path:module
328                 user = tokens[1];
329 
330                 String passhost = tokens[2];
331 
332                 int index = passhost.indexOf( "@" );
333 
334                 if ( index == -1 )
335                 {
336                     result.messages
337                         .add( "The user_password_host part must be on the form: <username>:<password>@<hostname>." );
338 
339                     return result;
340                 }
341 
342                 password = passhost.substring( 0, index );
343 
344                 host = passhost.substring( index + 1 );
345 
346                 port = new Integer( tokens[3] ).intValue();
347 
348                 path = tokens[4];
349 
350                 module = tokens[5];
351             }
352             else
353             {
354                 //tokens.length == 5
355                 if ( tokens[1].indexOf( "@" ) > 0 )
356                 {
357                     //pserver:username@host:port:path:module
358                     String userhost = tokens[1];
359 
360                     int index = userhost.indexOf( "@" );
361 
362                     user = userhost.substring( 0, index );
363 
364                     host = userhost.substring( index + 1 );
365 
366                     port = new Integer( tokens[2] ).intValue();
367                 }
368                 else if ( tokens[2].indexOf( "@" ) >= 0 )
369                 {
370                     //pserver:username:password@host:path:module
371                     //<username>:<password>@<hostname>
372                     user = tokens[1];
373 
374                     String passhost = tokens[2];
375 
376                     int index = passhost.indexOf( "@" );
377 
378                     password = passhost.substring( 0, index );
379 
380                     host = passhost.substring( index + 1 );
381                 }
382                 else
383                 {
384                     //pserver:host:port:path:module
385                     try
386                     {
387                         port = new Integer( tokens[2] ).intValue();
388                     }
389                     catch ( Exception e )
390                     {
391                         //incorrect
392                         result.messages.add( "Your scm url is invalid." );
393 
394                         return result;
395                     }
396 
397                     host = tokens[1];
398                 }
399 
400                 path = tokens[3];
401 
402                 module = tokens[4];
403             }
404 
405             String userHost = host;
406 
407             if ( user != null )
408             {
409                 userHost = user + "@" + host;
410             }
411 
412             // cvsroot format is :pserver:[user@]host:[port]path
413             cvsroot = ":" + transport + ":" + userHost + ":";
414 
415             if ( port != -1 )
416             {
417                 cvsroot += port;
418             }
419 
420             cvsroot += path;
421         }
422         else if ( transport.equalsIgnoreCase( TRANSPORT_SSPI ) )
423         {
424             //sspi:[username@]host:path:module
425             String userhost = tokens[1];
426 
427             int index = userhost.indexOf( "@" );
428 
429             if ( index == -1 )
430             {
431                 user = "";
432 
433                 host = userhost;
434             }
435             else
436             {
437                 user = userhost.substring( 0, index );
438 
439                 host = userhost.substring( index + 1 );
440             }
441 
442             path = tokens[2];
443 
444             module = tokens[3];
445 
446             // cvsroot format is :sspi:host:path
447             cvsroot = ":" + transport + ":" + host + ":" + path;
448         }
449         else
450         {
451             if ( !transport.equalsIgnoreCase( TRANSPORT_LOCAL ) )
452             {
453                 String userhost = tokens[1];
454 
455                 int index = userhost.indexOf( "@" );
456 
457                 if ( index == -1 )
458                 {
459                     host = userhost;
460                 }
461                 else
462                 {
463                     user = userhost.substring( 0, index );
464 
465                     host = userhost.substring( index + 1 );
466                 }
467             }
468 
469             if ( transport.equals( TRANSPORT_LOCAL ) )
470             {
471                 path = tokens[1];
472 
473                 module = tokens[2];
474 
475                 if ( module != null && module.startsWith( "/" ) )
476                 {
477                     module = module.substring( 1 );
478                 }
479 
480             }
481             else
482             {
483                 if ( tokens.length == 4 )
484                 {
485                     path = tokens[2];
486 
487                     module = tokens[3];
488                 }
489                 else
490                 {
491                     port = new Integer( tokens[2] ).intValue();
492 
493                     path = tokens[3];
494 
495                     module = tokens[4];
496                 }
497             }
498         }
499 
500         if ( port == -1 )
501         {
502             result.repository = new CvsScmProviderRepository( cvsroot, transport, user, password, host, path, module );
503         }
504         else
505         {
506             result.repository =
507                 new CvsScmProviderRepository( cvsroot, transport, user, password, host, port, path, module );
508         }
509 
510         return result;
511     }
512 
513     private ScmResult executeCommand( Command command, ScmProviderRepository repository, ScmFileSet fileSet,
514                                       CommandParameters parameters )
515         throws ScmException
516     {
517         fileSet = fixUpScmFileSetAbsoluteFilePath( fileSet );
518 
519         command.setLogger( getLogger() );
520 
521         return command.execute( repository, fileSet, parameters );
522     }
523 
524     protected abstract Command getAddCommand();
525 
526     protected abstract Command getBranchCommand();
527 
528     protected abstract Command getChangeLogCommand();
529 
530     protected abstract Command getCheckInCommand();
531 
532     protected abstract Command getCheckOutCommand();
533 
534     protected abstract Command getDiffCommand();
535 
536     protected abstract Command getExportCommand();
537 
538     protected abstract Command getListCommand();
539 
540     protected abstract Command getLoginCommand();
541 
542     protected abstract Command getRemoveCommand();
543 
544     protected abstract Command getStatusCommand();
545 
546     protected abstract Command getTagCommand();
547 
548     protected abstract Command getUpdateCommand();
549 
550     /**
551      * @see org.apache.maven.scm.provider.AbstractScmProvider#add(org.apache.maven.scm.provider.ScmProviderRepository,org.apache.maven.scm.ScmFileSet,org.apache.maven.scm.CommandParameters)
552      */
553     public AddScmResult add( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
554         throws ScmException
555     {
556         return (AddScmResult) executeCommand( getAddCommand(), repository, fileSet, parameters );
557     }
558 
559     /**
560      * @see org.apache.maven.scm.provider.AbstractScmProvider#tag(org.apache.maven.scm.provider.ScmProviderRepository,org.apache.maven.scm.ScmFileSet,org.apache.maven.scm.CommandParameters)
561      */
562     public BranchScmResult branch( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
563         throws ScmException
564     {
565         return (BranchScmResult) executeCommand( getBranchCommand(), repository, fileSet, parameters );
566     }
567 
568     /**
569      * @see org.apache.maven.scm.provider.AbstractScmProvider#changelog(org.apache.maven.scm.provider.ScmProviderRepository,org.apache.maven.scm.ScmFileSet,org.apache.maven.scm.CommandParameters)
570      */
571     public ChangeLogScmResult changelog( ScmProviderRepository repository, ScmFileSet fileSet,
572                                          CommandParameters parameters )
573         throws ScmException
574     {
575         return (ChangeLogScmResult) executeCommand( getChangeLogCommand(), repository, fileSet, parameters );
576     }
577 
578     /**
579      * @see org.apache.maven.scm.provider.AbstractScmProvider#checkin(org.apache.maven.scm.provider.ScmProviderRepository,org.apache.maven.scm.ScmFileSet,org.apache.maven.scm.CommandParameters)
580      */
581     public CheckInScmResult checkin( ScmProviderRepository repository, ScmFileSet fileSet,
582                                      CommandParameters parameters )
583         throws ScmException
584     {
585         return (CheckInScmResult) executeCommand( getCheckInCommand(), repository, fileSet, parameters );
586     }
587 
588     /**
589      * @see org.apache.maven.scm.provider.AbstractScmProvider#checkout(org.apache.maven.scm.provider.ScmProviderRepository,org.apache.maven.scm.ScmFileSet,org.apache.maven.scm.CommandParameters)
590      */
591     public CheckOutScmResult checkout( ScmProviderRepository repository, ScmFileSet fileSet,
592                                        CommandParameters parameters )
593         throws ScmException
594     {
595         return (CheckOutScmResult) executeCommand( getCheckOutCommand(), repository, fileSet, parameters );
596     }
597 
598     /**
599      * @see org.apache.maven.scm.provider.AbstractScmProvider#diff(org.apache.maven.scm.provider.ScmProviderRepository,org.apache.maven.scm.ScmFileSet,org.apache.maven.scm.CommandParameters)
600      */
601     public DiffScmResult diff( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
602         throws ScmException
603     {
604         return (DiffScmResult) executeCommand( getDiffCommand(), repository, fileSet, parameters );
605     }
606 
607     /**
608      * @see org.apache.maven.scm.provider.AbstractScmProvider#export(org.apache.maven.scm.provider.ScmProviderRepository,org.apache.maven.scm.ScmFileSet,org.apache.maven.scm.CommandParameters)
609      */
610     protected ExportScmResult export( ScmProviderRepository repository, ScmFileSet fileSet,
611                                       CommandParameters parameters )
612         throws ScmException
613     {
614         return (ExportScmResult) executeCommand( getExportCommand(), repository, fileSet, parameters );
615     }
616 
617     /**
618      * @see org.apache.maven.scm.provider.AbstractScmProvider#login(org.apache.maven.scm.provider.ScmProviderRepository,org.apache.maven.scm.ScmFileSet,org.apache.maven.scm.CommandParameters)
619      */
620     public LoginScmResult login( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
621         throws ScmException
622     {
623         return (LoginScmResult) executeCommand( getLoginCommand(), repository, fileSet, parameters );
624     }
625 
626     /**
627      * @see org.apache.maven.scm.provider.AbstractScmProvider#remove(org.apache.maven.scm.provider.ScmProviderRepository,org.apache.maven.scm.ScmFileSet,org.apache.maven.scm.CommandParameters)
628      */
629     public RemoveScmResult remove( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
630         throws ScmException
631     {
632         return (RemoveScmResult) executeCommand( getRemoveCommand(), repository, fileSet, parameters );
633     }
634 
635     /**
636      * @see org.apache.maven.scm.provider.AbstractScmProvider#status(org.apache.maven.scm.provider.ScmProviderRepository,org.apache.maven.scm.ScmFileSet,org.apache.maven.scm.CommandParameters)
637      */
638     public StatusScmResult status( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
639         throws ScmException
640     {
641         return (StatusScmResult) executeCommand( getStatusCommand(), repository, fileSet, parameters );
642     }
643 
644     /**
645      * @see org.apache.maven.scm.provider.AbstractScmProvider#tag(org.apache.maven.scm.provider.ScmProviderRepository,org.apache.maven.scm.ScmFileSet,org.apache.maven.scm.CommandParameters)
646      */
647     public TagScmResult tag( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
648         throws ScmException
649     {
650         return (TagScmResult) executeCommand( getTagCommand(), repository, fileSet, parameters );
651     }
652 
653     /**
654      * @see org.apache.maven.scm.provider.AbstractScmProvider#update(org.apache.maven.scm.provider.ScmProviderRepository,org.apache.maven.scm.ScmFileSet,org.apache.maven.scm.CommandParameters)
655      */
656     public UpdateScmResult update( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
657         throws ScmException
658     {
659         return (UpdateScmResult) executeCommand( getUpdateCommand(), repository, fileSet, parameters );
660     }
661 
662     /**
663      * @see org.apache.maven.scm.provider.AbstractScmProvider#list(org.apache.maven.scm.provider.ScmProviderRepository,org.apache.maven.scm.ScmFileSet,org.apache.maven.scm.CommandParameters)
664      */
665     protected ListScmResult list( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
666         throws ScmException
667     {
668         return (ListScmResult) executeCommand( getListCommand(), repository, fileSet, parameters );
669     }
670 
671     /**
672      * CVS provider requires that all files in ScmFileSet must be relative to basedir
673      * This function ensures and converts all absolute paths to relative paths
674      *
675      * @param currentFileSet
676      * @return
677      * @throws ScmException
678      */
679     private static ScmFileSet fixUpScmFileSetAbsoluteFilePath( ScmFileSet currentFileSet )
680         throws ScmException
681     {
682         ScmFileSet newFileSet = null;
683 
684         try
685         {
686             File basedir = getAbsoluteFilePath( currentFileSet.getBasedir() );
687 
688             File[] files = currentFileSet.getFiles();
689 
690             for ( int i = 0; i < files.length; ++i )
691             {
692                 if ( files[i].isAbsolute() )
693                 {
694                     files[i] = new File( getRelativePath( basedir, files[i] ) );
695                 }
696             }
697 
698             newFileSet = new ScmFileSet( basedir, files );
699         }
700         catch ( IOException e )
701         {
702             throw new ScmException( "Invalid file set.", e );
703         }
704 
705         return newFileSet;
706     }
707 
708     public static String getRelativePath( File basedir, File f )
709         throws ScmException, IOException
710     {
711         File fileOrDir = getAbsoluteFilePath( f );
712 
713         if ( !fileOrDir.getPath().startsWith( basedir.getPath() ) )
714         {
715             throw new ScmException( fileOrDir.getPath() + " was not contained in " + basedir.getPath() );
716         }
717 
718         return fileOrDir.getPath().substring( basedir.getPath().length() + 1, fileOrDir.getPath().length() );
719     }
720 
721     private static File getAbsoluteFilePath( File fileOrDir )
722         throws IOException
723     {
724         String javaPathString = fileOrDir.getCanonicalPath().replace( '\\', '/' );
725 
726         if ( javaPathString.endsWith( "/" ) )
727         {
728             javaPathString = javaPathString.substring( 0, javaPathString.length() - 1 );
729         }
730 
731         return new File( javaPathString );
732     }
733 }