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