View Javadoc

1   package org.apache.maven.plugins.site.wagon;
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.wagon.WagonConstants;
23  
24  import java.io.File;
25  import java.util.StringTokenizer;
26  
27  /**
28   * Various path (URL) manipulation routines.
29   *
30   * <strong>Note: </strong> This is a copy of a file from Wagon. It was copied here to be able to work around WAGON-307.
31   * This class can be removed when the prerequisite Maven version uses wagon-provider-api:1.0-beta-7.
32   *
33   * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
34   * @version $Id: PathUtils.html 816554 2012-05-08 11:56:34Z hboutemy $
35   */
36  public final class PathUtils
37  {
38      private PathUtils()
39      {
40      }
41      
42      /**
43       * Returns the directory path portion of a file specification string.
44       * Matches the equally named unix command.
45       *
46       * @return The directory portion excluding the ending file separator.
47       */
48      public static String dirname( final String path )
49      {
50          final int i = path.lastIndexOf( "/" );
51  
52          return ( ( i >= 0 ) ? path.substring( 0, i ) : "" );
53      }
54  
55      /**
56       * Returns the filename portion of a file specification string.
57       *
58       * @return The filename string with extension.
59       */
60      public static String filename( final String path )
61      {
62          final int i = path.lastIndexOf( "/" );
63          return ( ( i >= 0 ) ? path.substring( i + 1 ) : path );
64      }
65  
66      public static String[] dirnames( final String path )
67      {
68          final String dirname = PathUtils.dirname( path );
69          return split( dirname, "/", -1 );
70  
71      }
72  
73      private static String[] split( final String str, final String separator, final int max )
74      {
75          final StringTokenizer tok;
76  
77          if ( separator == null )
78          {
79              // Null separator means we're using StringTokenizer's default
80              // delimiter, which comprises all whitespace characters.
81              tok = new StringTokenizer( str );
82          }
83          else
84          {
85              tok = new StringTokenizer( str, separator );
86          }
87  
88          int listSize = tok.countTokens();
89  
90          if ( max > 0 && listSize > max )
91          {
92              listSize = max;
93          }
94  
95          final String[] list = new String[listSize];
96  
97          int i = 0;
98  
99          int lastTokenBegin;
100         int lastTokenEnd = 0;
101 
102         while ( tok.hasMoreTokens() )
103         {
104             if ( max > 0 && i == listSize - 1 )
105             {
106                 // In the situation where we hit the max yet have
107                 // tokens left over in our input, the last list
108                 // element gets all remaining text.
109                 final String endToken = tok.nextToken();
110 
111                 lastTokenBegin = str.indexOf( endToken, lastTokenEnd );
112 
113                 list[i] = str.substring( lastTokenBegin );
114 
115                 break;
116 
117             }
118             else
119             {
120                 list[i] = tok.nextToken();
121 
122                 lastTokenBegin = str.indexOf( list[i], lastTokenEnd );
123 
124                 lastTokenEnd = lastTokenBegin + list[i].length();
125             }
126 
127             i++;
128         }
129         return list;
130     }
131 
132     /**
133      * Return the host name (Removes protocol and path from the URL) E.g: for input
134      * <code>http://www.codehause.org</code> this method will return <code>www.apache.org</code>
135      *
136      * @param url the url
137      * @return the host name
138      */
139     public static String host( final String url )
140     {
141         String authorization = authorization( url );
142         int index = authorization.indexOf( '@' );
143         if ( index >= 0 )
144         {
145             return authorization.substring( index + 1 );
146         }
147         else
148         {
149             return authorization;
150         }
151     }
152 
153     /**
154      * This was changed from private to package local so that it can be unit tested.
155      */
156     static String authorization( final String url )
157     {
158         if ( url == null )
159         {
160             return "localhost";
161         }
162 
163         final String protocol = PathUtils.protocol( url );
164 
165         if ( protocol == null || protocol.equalsIgnoreCase( "file" ) )
166         {
167             return "localhost";
168         }
169 
170         String host = url;
171         if ( protocol.equalsIgnoreCase( "scm" ) )
172         {
173             // skip over type
174             host = host.substring( host.indexOf( ":", 4 ) + 1 ).trim();
175         }
176 
177         // skip over protocol
178         host = host.substring( host.indexOf( ":" ) + 1 ).trim();
179         if ( host.startsWith( "//" ) )
180         {
181             host = host.substring( 2 );
182         }
183 
184         int pos = host.indexOf( "/" );
185 
186         if ( pos > 0 )
187         {
188             host = host.substring( 0, pos );
189         }
190 
191         pos = host.indexOf( '@' );
192 
193         if ( pos > 0 )
194         {
195             pos = host.indexOf( ':', pos );
196         }
197         else
198         {
199             pos = host.indexOf( ":" );
200         }
201 
202         if ( pos > 0 )
203         {
204             host = host.substring( 0, pos );
205         }
206         return host;
207     }
208 
209     /**
210      * /**
211      * Return the protocol name.
212      * <br/>
213      * E.g: for input
214      * <code>http://www.codehause.org</code> this method will return <code>http</code>
215      *
216      * @param url the url
217      * @return the host name
218      */
219     public static String protocol( final String url )
220     {
221         final int pos = url.indexOf( ":" );
222 
223         if ( pos == -1 )
224         {
225             return "";
226         }
227         return url.substring( 0, pos ).trim();
228     }
229 
230     /**
231      * @param url
232      * @return the port or {@link WagonConstants#UNKNOWN_PORT} if not existent
233      */
234     public static int port( String url )
235     {
236 
237         final String protocol = PathUtils.protocol( url );
238 
239         if ( protocol == null || protocol.equalsIgnoreCase( "file" ) )
240         {
241             return WagonConstants.UNKNOWN_PORT;
242         }
243 
244         final String authorization = PathUtils.authorization( url );
245 
246         if ( authorization == null )
247         {
248             return WagonConstants.UNKNOWN_PORT;
249         }
250 
251         if ( protocol.equalsIgnoreCase( "scm" ) )
252         {
253             // skip over type
254             url = url.substring( url.indexOf( ":", 4 ) + 1 ).trim();
255         }
256 
257         if ( url.regionMatches( true, 0, "file:", 0, 5 ) || url.regionMatches( true, 0, "local:", 0, 6 ) )
258         {
259             return WagonConstants.UNKNOWN_PORT;
260         }
261 
262         // skip over protocol
263         url = url.substring( url.indexOf( ":" ) + 1 ).trim();
264         if ( url.startsWith( "//" ) )
265         {
266             url = url.substring( 2 );
267         }
268 
269         int start = authorization.length();
270 
271         if ( url.length() > start && url.charAt( start ) == ':' )
272         {
273             int end = url.indexOf( '/', start );
274 
275             if ( end == start + 1 )
276             {
277                 // it is :/
278                 return WagonConstants.UNKNOWN_PORT;
279             }
280 
281             if ( end == -1 )
282             {
283                 end = url.length();
284             }
285 
286             return Integer.parseInt( url.substring( start + 1, end ) );
287         }
288         else
289         {
290             return WagonConstants.UNKNOWN_PORT;
291         }
292 
293     }
294 
295     /**
296      * Derive the path portion of the given URL.
297      * 
298      * @param url the repository URL
299      * @return the basedir of the repository
300      * @todo need to URL decode for spaces?
301      */
302     public static String basedir( String url )
303     {
304         String protocol = PathUtils.protocol( url );
305 
306         String retValue = null;
307 
308         if ( protocol.equalsIgnoreCase( "scm" ) )
309         {
310             // skip over SCM bits
311             if ( url.regionMatches( true, 0, "scm:svn:", 0, 8 ) )
312             {
313                 url = url.substring( url.indexOf( ":", 4 ) + 1 );
314                 protocol = PathUtils.protocol( url );
315             }
316         }
317 
318         if ( protocol.equalsIgnoreCase( "file" ) )
319         {
320             retValue = url.substring( protocol.length() + 1 );
321             retValue = decode( retValue );
322             // special case: if omitted // on protocol, keep path as is
323             if ( retValue.startsWith( "//" ) )
324             {
325                 retValue = retValue.substring( 2 );
326 
327                 if ( retValue.length() >= 2 && ( retValue.charAt( 1 ) == '|' || retValue.charAt( 1 ) == ':' ) )
328                 {
329                     // special case: if there is a windows drive letter, then keep the original return value
330                     retValue = retValue.charAt( 0 ) + ":" + retValue.substring( 2 );
331                 }
332                 else
333                 {
334                     // Now we expect the host
335                     int index = retValue.indexOf( "/" );
336                     if ( index >= 0 )
337                     {
338                         retValue = retValue.substring( index + 1 );
339                     }
340 
341                     // special case: if there is a windows drive letter, then keep the original return value
342                     if ( retValue.length() >= 2 && ( retValue.charAt( 1 ) == '|' || retValue.charAt( 1 ) == ':' ) )
343                     {
344                         retValue = retValue.charAt( 0 ) + ":" + retValue.substring( 2 );
345                     }
346                     else if ( index >= 0 )
347                     {
348                         // leading / was previously stripped
349                         retValue = "/" + retValue;
350                     }
351                 }
352             }
353 
354             // special case: if there is a windows drive letter using |, switch to :
355             if ( retValue.length() >= 2 && retValue.charAt( 1 ) == '|' )
356             {
357                 retValue = retValue.charAt( 0 ) + ":" + retValue.substring( 2 );
358             }
359         }
360         else
361         {
362             final String authorization = PathUtils.authorization( url );
363 
364             final int port = PathUtils.port( url );
365 
366             int pos = 0;
367 
368             if ( protocol.equalsIgnoreCase( "scm" ) )
369             {
370                 pos = url.indexOf( ":", 4 ) + 1;
371                 pos = url.indexOf( ":", pos ) + 1;
372             }
373             else
374             {
375                 int index = url.indexOf( "://" );
376                 if( index != -1 ) {
377                     pos = index + 3;
378                 }
379             }
380 
381             pos += authorization.length();
382 
383             if ( port != WagonConstants.UNKNOWN_PORT )
384             {
385                 pos = pos + Integer.toString( port ).length() + 1;
386             }
387 
388             if ( url.length() > pos )
389             {
390                 retValue = url.substring( pos );
391                 if ( retValue.startsWith( ":" ) )
392                 {
393                     // this is for :/ after the host
394                     retValue = retValue.substring( 1 );
395                 }
396 
397                 // one module may be allowed in the path in CVS
398                 retValue = retValue.replace( ':', '/' );
399             }
400         }
401 
402         if ( retValue == null )
403         {
404             retValue = "/";
405         }
406         return retValue.trim();
407     }
408 
409     /**
410      * Decodes the specified (portion of a) URL. <strong>Note:</strong> This decoder assumes that ISO-8859-1 is used to
411      * convert URL-encoded octets to characters.
412      * 
413      * @param url The URL to decode, may be <code>null</code>.
414      * @return The decoded URL or <code>null</code> if the input was <code>null</code>.
415      */
416     private static String decode( String url )
417     {
418         String decoded = url;
419         if ( url != null )
420         {
421             int pos = -1;
422             while ( ( pos = decoded.indexOf( '%', pos + 1 ) ) >= 0 )
423             {
424                 if ( pos + 2 < decoded.length() )
425                 {
426                     String hexStr = decoded.substring( pos + 1, pos + 3 );
427                     char ch = (char) Integer.parseInt( hexStr, 16 );
428                     decoded = decoded.substring( 0, pos ) + ch + decoded.substring( pos + 3 );
429                 }
430             }
431         }
432         return decoded;
433     }
434 
435     public static String user( String url )
436     {
437         String host = authorization( url );
438         int index = host.indexOf( '@' );
439         if ( index > 0 )
440         {
441             String userInfo = host.substring( 0, index );
442             index = userInfo.indexOf( ':' );
443             if ( index > 0 )
444             {
445                 return userInfo.substring( 0, index );
446             }
447             else if ( index < 0 )
448             {
449                 return userInfo;
450             }
451         }
452         return null;
453     }
454 
455     public static String password( String url )
456     {
457         String host = authorization( url );
458         int index = host.indexOf( '@' );
459         if ( index > 0 )
460         {
461             String userInfo = host.substring( 0, index );
462             index = userInfo.indexOf( ':' );
463             if ( index >= 0 )
464             {
465                 return userInfo.substring( index + 1 );
466             }
467         }
468         return null;
469     }
470 
471     // TODO: move to plexus-utils or use something appropriate from there
472     public static String toRelative( File basedir, String absolutePath )
473     {
474         String relative;
475 
476         absolutePath = absolutePath.replace( '\\', '/' );
477         String basedirPath = basedir.getAbsolutePath().replace( '\\', '/' );
478 
479         if ( absolutePath.startsWith( basedirPath ) )
480         {
481             relative = absolutePath.substring( basedirPath.length() );
482             if ( relative.startsWith( "/" ) )
483             {
484                 relative = relative.substring( 1 );
485             }
486             if ( relative.length() <= 0 )
487             {
488                 relative = ".";
489             }
490         }
491         else
492         {
493             relative = absolutePath;
494         }
495 
496         return relative;
497     }
498 }