View Javadoc

1   package org.apache.maven.xdoc.util;
2   
3   /* ====================================================================
4    *   Licensed to the Apache Software Foundation (ASF) under one or more
5    *   contributor license agreements.  See the NOTICE file distributed with
6    *   this work for additional information regarding copyright ownership.
7    *   The ASF licenses this file to You under the Apache License, Version 2.0
8    *   (the "License"); you may not use this file except in compliance with
9    *   the License.  You may obtain a copy of the License at
10   *
11   *       http://www.apache.org/licenses/LICENSE-2.0
12   *
13   *   Unless required by applicable law or agreed to in writing, software
14   *   distributed under the License is distributed on an "AS IS" BASIS,
15   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   *   See the License for the specific language governing permissions and
17   *   limitations under the License.
18   * ====================================================================
19   */
20  
21  import java.util.ArrayList;
22  import java.util.List;
23  
24  import org.apache.commons.lang.StringUtils;
25  import org.apache.maven.scm.provider.clearcase.ClearCaseScmProvider;
26  import org.apache.maven.scm.provider.clearcase.repository.ClearCaseScmProviderRepository;
27  import org.apache.maven.scm.provider.cvslib.CvsScmProvider;
28  import org.apache.maven.scm.provider.cvslib.repository.CvsScmProviderRepository;
29  import org.apache.maven.scm.provider.perforce.PerforceScmProvider;
30  import org.apache.maven.scm.provider.perforce.repository.PerforceScmProviderRepository;
31  import org.apache.maven.scm.provider.starteam.StarteamScmProvider;
32  import org.apache.maven.scm.provider.starteam.repository.StarteamScmProviderRepository;
33  import org.apache.maven.scm.provider.svn.SvnScmProvider;
34  import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
35  import org.apache.maven.scm.repository.ScmRepositoryException;
36  import org.apache.maven.util.EnhancedStringTokenizer;
37  
38  /**
39   * Utility class to manage SCM informations. NOTE: This is very CVS specific,
40   * but I would like to try additional SCM package like subversion ASAP.
41   *
42   * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
43   * @author <a href="mailto:aheritier@apache.org">Arnaud Heritier</a>
44   * @version $Id$
45   */
46  public final class ScmUtil
47  {
48      /**
49       * Get the SCM type.
50       *
51       * @param scmConnection
52       *            the scm connection to analyse.
53       * @return the scm type : cvs, svn , ...
54       */
55      public String getScmType( final String scmConnection )
56      {
57          if ( isValid( scmConnection ) )
58          {
59              return splitSCMConnection( scmConnection )[1];
60          }
61  
62          return null;
63      }
64  
65      /**
66       * Get cvs connection string. Used in
67       * xdocs/src/plugin-resources/templates/scm/cvs.xml. If username == "",
68       * assumes anonymous (pserver) connection. In this case, inserts a separator
69       * (':' or '|') between the username and '@' to indicate that there is a
70       * password and that it is empty. If username != "" it replaces username in
71       * conn.
72       *
73       * @param conn
74       *            six token connection string
75       * @param username
76       *            username override if non-empty.
77       * @return CVS root.
78       */
79      public String getCvsConnection( String conn, String username )
80      {
81          String[] tokens = splitSCMConnection( conn );
82  
83          if ( !tokens[1].equals( "cvs" ) )
84          {
85              return "";
86          }
87  
88          String separator = getSCMConnectionSeparator( conn );
89  
90          if ( tokens[3].indexOf( '@' ) >= 0 )
91          {
92              if ( username.length() == 0 )
93              {
94                  username =
95                      tokens[3].substring( 0, tokens[3].indexOf( '@' ) )
96                      + separator;
97              }
98  
99              tokens[3] =
100                 username + "@"
101                 + tokens[3].substring( tokens[3].indexOf( '@' ) + 1 );
102         }
103 
104         String result =
105             tokens[0] + ":" + tokens[1] + separator + tokens[2] + separator
106             + tokens[3] + separator + tokens[4] + separator + tokens[5];
107 
108         return result;
109     }
110 
111     /**
112      * Get cvs module. Used in xdocs/src/plugin-resources/templates/scm/cvs.xml.
113      *
114      * @param conn
115      *            six token connection string
116      * @return CVS module.
117      */
118     public String getCvsModule( String conn )
119     {
120         if ( isValid( conn ) )
121         {
122             String[] tokens = splitSCMConnection( conn );
123 
124             if ( !tokens[1].equals( "cvs" ) )
125             {
126                 return "";
127             }
128 
129             return tokens[5];
130         }
131 
132         return null;
133     }
134 
135     /**
136      * Get svn connection string. Used in
137      * xdocs/src/plugin-resources/templates/scm/svn.xml. It removes the first
138      * two elements (scm:svn:)
139      *
140      * @param conn
141      *            the repository connection
142      * @return CVS root.
143      */
144     public String getSvnConnection( String conn )
145     {
146         if ( ( conn != null ) && ( conn.length() > 8 )
147             && "svn".equals( conn.substring( 4, 7 ) ) )
148         {
149             return conn.substring( 8 );
150         }
151         else
152         {
153             System.err.println(
154                 "Your subversion connection does not seem to be valid: " + conn );
155 
156             return "";
157         }
158     }
159 
160     /**
161      * Create the documentation to provide an anonymous access with a <code>CVS</code> SCM.
162      * For example, generate the following command line:
163      * <p>cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic login</p>
164      * <p>cvs -z3 -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic co maven-plugins/dist</p>
165      *
166      * @param connection  The connection string.
167      * @see <a href="http://ximbiot.com/cvs/manual/cvs-1.12.12/cvs_16.html#SEC115">http://ximbiot.com/cvs/manual/cvs-1.12.12/cvs_16.html#SEC115</a>
168      */
169     public String anonymousAccessCVS( String connection )
170     {
171         char delim = getSCMConnectionSeparator( connection ).charAt( 0 );
172 
173         CvsScmProvider cvsProvider = new CvsScmProvider();
174         CvsScmProviderRepository cvsRepo;
175 
176         String scmSpecificUrl = connection.substring( 8 );
177 
178         try
179         {
180             cvsRepo =
181                 (CvsScmProviderRepository) cvsProvider
182                 .makeProviderScmRepository( scmSpecificUrl, delim );
183         }
184         catch ( ScmRepositoryException e )
185         {
186             System.err.println(
187                 "Your developerConnection does not seem to be valid: "
188                 + e.getMessage() );
189 
190             return "";
191         }
192 
193         StringBuffer command = new StringBuffer();
194 
195         command.append( "cvs -d " ).append( cvsRepo.getCvsRoot() ).append( " login" );
196         command.append( "\n" );
197         command.append( "cvs -z3 -d " ).append( cvsRepo.getCvsRoot() );
198         command.append( " co " ).append( cvsRepo.getModule() );
199 
200         return command.toString();
201     }
202 
203     /**
204      * Create the documentation to provide an developer access with a <code>CVS</code> SCM.
205      * For example, generate the following command line:
206      * <p>export CVS_RSH=ssh</p>
207      * <p>cvs -z3 -d :ext:username@cvs.apache.org:/home/cvs co maven-plugins/dist</p>
208      *
209      * @param devConnection The developer connection string.
210      * @see <a href="http://ximbiot.com/cvs/manual/cvs-1.12.12/cvs_16.html#SEC115">http://ximbiot.com/cvs/manual/cvs-1.12.12/cvs_16.html#SEC115</a>
211      */
212     public String developerAccessCVS( String devConnection )
213     {
214         char delim = getSCMConnectionSeparator( devConnection ).charAt( 0 );
215 
216         CvsScmProvider cvsProvider = new CvsScmProvider();
217         CvsScmProviderRepository cvsRepo;
218 
219         String scmSpecificUrl = devConnection.substring( 8 );
220 
221         try
222         {
223             cvsRepo =
224                 (CvsScmProviderRepository) cvsProvider
225                 .makeProviderScmRepository( scmSpecificUrl, delim );
226         }
227         catch ( ScmRepositoryException e )
228         {
229             System.err.println(
230                 "Your developerConnection does not seem to be valid: "
231                 + e.getMessage() );
232 
233             return "";
234         }
235 
236         // Safety: remove the username if present
237         String cvsRoot =
238             StringUtils.replace( cvsRepo.getCvsRoot(), cvsRepo.getUser(),
239                 "username" );
240 
241         StringBuffer command = new StringBuffer();
242 
243         command.append( "export CVS_RSH=ssh" );
244         command.append( "\n" );
245         command.append( "cvs -z3 -d " ).append( cvsRoot ).append( " co " )
246                .append( cvsRepo.getModule() );
247 
248         return command.toString();
249     }
250 
251     /**
252      * Create the documentation to provide an anonymous access with a <code>SVN</code> SCM.
253      * For example, generate the following command line:
254      * <p>svn checkout http://svn.apache.org/repos/asf/maven/components/trunk/ maven</p>
255      *
256      * @param connection The connection string.
257      * @param checkoutDirectoryName The checkout directory.
258      * @see <a href="http://svnbook.red-bean.com/">http://svnbook.red-bean.com/</a>
259      */
260     public String anonymousAccessSVN( String connection,
261         String checkoutDirectoryName )
262     {
263         char delim = getSCMConnectionSeparator( connection ).charAt( 0 );
264 
265         SvnScmProvider svnProvider = new SvnScmProvider();
266         SvnScmProviderRepository svnRepo;
267 
268         String scmSpecificUrl = connection.substring( 8 );
269 
270         try
271         {
272             svnRepo =
273                 (SvnScmProviderRepository) svnProvider
274                 .makeProviderScmRepository( scmSpecificUrl, delim );
275         }
276         catch ( ScmRepositoryException e )
277         {
278             System.err.println(
279                 "Your developerConnection does not seem to be valid: "
280                 + e.getMessage() );
281 
282             return "";
283         }
284 
285         StringBuffer command = new StringBuffer();
286 
287         command.append( "svn checkout " ).append( svnRepo.getUrl() )
288                .append( " " ).append( checkoutDirectoryName );
289 
290         return command.toString();
291     }
292 
293     /**
294      * Create the documentation to provide an developer access with a <code>SVN</code> SCM.
295      * For example, generate the following command line:
296      * <p>svn checkout https://svn.apache.org/repos/asf/maven/components/trunk maven</p>
297      * <p>svn commit --username your-username -m "A message"</p>
298      *
299      * @param devConnection The developer connection string.
300      * @param checkoutDirectoryName The checkout directory.
301      * @see <a href="http://svnbook.red-bean.com/">http://svnbook.red-bean.com/</a>
302      */
303     public String developerAccessSVN( String devConnection,
304         String checkoutDirectoryName )
305     {
306         char delim = getSCMConnectionSeparator( devConnection ).charAt( 0 );
307 
308         SvnScmProvider svnProvider = new SvnScmProvider();
309         SvnScmProviderRepository svnRepo;
310 
311         String scmSpecificUrl = devConnection.substring( 8 );
312 
313         try
314         {
315             svnRepo =
316                 (SvnScmProviderRepository) svnProvider
317                 .makeProviderScmRepository( scmSpecificUrl, delim );
318         }
319         catch ( ScmRepositoryException e )
320         {
321             System.err.println(
322                 "Your developerConnection does not seem to be valid: "
323                 + e.getMessage() );
324 
325             return "";
326         }
327 
328         StringBuffer command = new StringBuffer();
329 
330         command.append( "svn checkout " ).append( svnRepo.getUrl() )
331                .append( " " ).append( checkoutDirectoryName );
332 
333         return command.toString();
334     }
335 
336     /**
337      * Create the documentation to provide an developer access with a
338      * <code>Perforce</code> SCM. For example, generate the following command
339      * line:
340      * <p>
341      * p4 -H hostname -p port -u username -P password path
342      * </p>
343      * <p>
344      * p4 -H hostname -p port -u username -P password path submit -c changement
345      * </p>
346      *
347      * @param devConnection
348      * @see <a
349      *      href="http://www.perforce.com/perforce/doc.051/manuals/cmdref/index.html">http://www.perforce.com/perforce/doc.051/manuals/cmdref/index.html</>
350      */
351     public String developerAccessPerforce( String devConnection )
352     {
353         StringBuffer command = new StringBuffer();
354         char delim = getSCMConnectionSeparator( devConnection ).charAt( 0 );
355 
356         PerforceScmProvider perforceProvider = new PerforceScmProvider();
357         PerforceScmProviderRepository perforceRepo;
358 
359         String scmSpecificUrl = devConnection.substring( 13 );
360 
361         try
362         {
363             perforceRepo =
364                 (PerforceScmProviderRepository) perforceProvider
365                 .makeProviderScmRepository( scmSpecificUrl, delim );
366         }
367         catch ( ScmRepositoryException e )
368         {
369             System.err.println(
370                 "Your developerConnection does not seem to be valid: "
371                 + e.getMessage() );
372 
373             return "";
374         }
375 
376         command.append( "p4" );
377 
378         if ( !StringUtils.isEmpty( perforceRepo.getHost() ) )
379         {
380             command.append( " -H " ).append( perforceRepo.getHost() );
381         }
382 
383         if ( perforceRepo.getPort() > 0 )
384         {
385             command.append( " -p " + perforceRepo.getPort() );
386         }
387 
388         command.append( " -u username" );
389         command.append( " -P password" );
390         command.append( " " );
391         command.append( perforceRepo.getPath() );
392         command.append( "\n" );
393         command.append( "p4 submit -c \"A comment\"" );
394 
395         return command.toString();
396     }
397 
398     // Starteam
399 
400     /**
401      * Create the documentation to provide an developer access with a
402      * <code>Starteam</code> SCM. For example, generate the following command
403      * line:
404      * <p>
405      * stcmd co -x -nologo -stop -p myusername:mypassword@myhost:1234/projecturl
406      * -is
407      * </p>
408      * <p>
409      * stcmd ci -x -nologo -stop -p myusername:mypassword@myhost:1234/projecturl
410      * -f NCI -is
411      * </p>
412      *
413      * @param devConnection
414      */
415     public String developerAccessStarteam( String devConnection )
416     {
417         StringBuffer command = new StringBuffer();
418         char delim = getSCMConnectionSeparator( devConnection ).charAt( 0 );
419 
420         StarteamScmProvider starteamProvider = new StarteamScmProvider();
421         StarteamScmProviderRepository starteamRepo;
422 
423         String scmSpecificUrl = devConnection.substring( 13 );
424 
425         try
426         {
427             starteamRepo =
428                 (StarteamScmProviderRepository) starteamProvider
429                 .makeProviderScmRepository( scmSpecificUrl, delim );
430         }
431         catch ( ScmRepositoryException e )
432         {
433             System.err.println(
434                 "Your developerConnection does not seem to be valid: "
435                 + e.getMessage() );
436 
437             return "";
438         }
439 
440         // Safety: remove the username/password if present
441         String fullUrl =
442             StringUtils.replace( starteamRepo.getFullUrl(),
443                 starteamRepo.getUser(), "username" );
444 
445         fullUrl =
446             StringUtils.replace( fullUrl, starteamRepo.getPassword(),
447                 "password" );
448 
449         command.append( "stcmd co -x -nologo -stop -p " );
450         command.append( fullUrl );
451         command.append( " -is" );
452         command.append( "\n" );
453         command.append( "stcmd ci -x -nologo -stop -p " );
454         command.append( fullUrl );
455         command.append( " -f NCI -is" );
456 
457         return command.toString();
458     }
459 
460     /**
461      * Create the documentation to provide an developer access with a
462      * <code>Clearcase</code> SCM. For example, generate the following command
463      * line:
464      * <p>
465      * cleartool checkout module
466      * </p>
467      *
468      * @param devConnection
469      */
470     public String developerAccessClearCase( String devConnection )
471     {
472         StringBuffer command = new StringBuffer();
473         char delim = getSCMConnectionSeparator( devConnection ).charAt( 0 );
474 
475         ClearCaseScmProvider clearCaseProvider = new ClearCaseScmProvider();
476         ClearCaseScmProviderRepository clearCaseRepo;
477 
478         String scmSpecificUrl = devConnection.substring( 14 );
479 
480         try
481         {
482             clearCaseRepo =
483                 (ClearCaseScmProviderRepository) clearCaseProvider
484                 .makeProviderScmRepository( scmSpecificUrl, delim );
485         }
486         catch ( ScmRepositoryException e )
487         {
488             System.err.println(
489                 "Your developerConnection does not seem to be valid: "
490                 + e.getMessage() );
491 
492             return "";
493         }
494 
495         command.append( "cleartool checkout " ).append( clearCaseRepo
496             .getViewName( "id" ) );
497 
498         return command.toString();
499     }
500 
501     /**
502      * Splits an SCM string into parts.
503      *
504      * @param connection
505      * @return A string array of SCM parts
506      */
507     public String[] splitSCMConnection( String connection )
508     {
509         if ( connection == null )
510         {
511             throw new NullPointerException( "repository connection is null" );
512         }
513 
514         if ( connection.length() < 5 )
515         {
516             throw new IllegalArgumentException(
517                 "repository connection is too short" );
518         }
519 
520         if ( !connection.startsWith( "scm:" ) )
521         {
522             throw new IllegalArgumentException(
523                 "repository connection must start with scm:" );
524         }
525 
526         String delimiter = getSCMConnectionSeparator( connection );
527 
528         // If the tokenizer is going to work correctly then the character
529         // following "scm" must be the same as the delimiter, which is not
530         // always the case. Therefor we give it a modified connection.
531         String modifiedConnection =
532             "scm" + delimiter + connection.substring( 4 );
533 
534         EnhancedStringTokenizer tok =
535             new EnhancedStringTokenizer( modifiedConnection, delimiter );
536 
537         String[] tokens = tokenizerToArray( tok );
538 
539         // for a valid repository, it should be scm:<provider> at least
540         if ( ( tokens.length >= 1 ) && tokens[1].equals( "cvs" ) )
541         {
542             if ( ( tokens.length >= 2 ) && tokens[2].equals( "local" ) )
543             {
544                 if ( tokens.length == 6 )
545                 {
546                     if ( ( tokens[3].length() > 0 )
547                         && !tokens[3].equals( "local" ) )
548                     {
549                         throw new IllegalArgumentException(
550                             "cvs local repository connection string must specify 5 tokens, or an empty 3rd token if 6" );
551                     }
552                 }
553                 else if ( tokens.length == 5 )
554                 {
555                     String[] newTokens = new String[6];
556 
557                     newTokens[0] = tokens[0];
558                     newTokens[1] = tokens[1];
559                     newTokens[2] = tokens[2];
560                     newTokens[3] = "";
561                     newTokens[4] = tokens[3];
562                     newTokens[5] = tokens[4];
563                     tokens = newTokens;
564                 }
565                 else
566                 {
567                     throw new IllegalArgumentException(
568                         "cvs local repository connection string doesn't contain five tokens" );
569                 }
570             }else if ( ( tokens.length == 7 ) && tokens[2].equals( "pserver" ) && tokens[4].startsWith("@"))
571             {
572                 String[] newTokens = new String[6];
573 
574                 newTokens[0] = tokens[0];
575                 newTokens[1] = tokens[1];
576                 newTokens[2] = tokens[2];
577                 newTokens[3] = tokens[3] + tokens[4];
578                 newTokens[4] = tokens[5];
579                 newTokens[5] = tokens[6];
580                 tokens = newTokens;
581             }
582             else if ( tokens.length != 6 )
583             {
584                 throw new IllegalArgumentException(
585                     "cvs repository connection string doesn't contain six tokens" );
586             }
587         }
588 
589         return tokens;
590     }
591 
592     /**
593      * Get the separator used in an SCM string
594      *
595      * @param connection
596      * @return String that can be either ":" or "|"
597      */
598     public String getSCMConnectionSeparator( String connection )
599     {
600         if ( connection == null )
601         {
602             throw new NullPointerException( "repository connection is null" );
603         }
604 
605         if ( connection.indexOf( "|" ) != -1 )
606         {
607             return "|";
608         }
609         else
610         {
611             return ":";
612         }
613     }
614 
615     /**
616      * Converts a tokenizer to an array of strings FIXME: This should be in a
617      * string util class.
618      *
619      * @param tok
620      * @return String[]
621      */
622     public String[] tokenizerToArray( EnhancedStringTokenizer tok )
623     {
624         List l = new ArrayList();
625 
626         while ( tok.hasMoreTokens() )
627         {
628             l.add( tok.nextToken() );
629         }
630 
631         return (String[]) l.toArray( new String[l.size()] );
632     }
633 
634     /**
635      * Simple check for a value in the POM. Due to the Jelly swizzling fields
636      * that aren't set come out as empty strings. This will not be required when
637      * the new lazy evaluation mechanism is put in place.
638      *
639      * @param value
640      *            POM value to test.
641      * @return Is the value valid.
642      */
643     protected boolean isValid( String value )
644     {
645         if ( ( value != null ) && !value.trim().equals( "" ) )
646         {
647             return true;
648         }
649 
650         return false;
651     }
652 }