1 package org.apache.maven.xdoc.util;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
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
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
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
529
530
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
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 }