View Javadoc

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