1212import  java .nio .file .InvalidPathException ;
1313import  java .nio .file .LinkOption ;
1414import  java .nio .file .Path ;
15- import  java .nio .file .Paths ;
1615import  java .nio .file .WatchEvent ;
1716import  java .nio .file .WatchKey ;
1817import  java .nio .file .WatchService ;
@@ -80,11 +79,11 @@ final class HttpPath implements Path {
8079     * @param query          query. May be {@code null}. 
8180     * @param reference      reference. May be {@code null}. 
8281     * @param normalizedPath normalized path (as a byte array). Shouldn't be {@code null}. 
83-      * 
8482     * @implNote does not perform any check for efficiency. 
8583     */ 
8684    private  HttpPath (final  HttpFileSystem  fs ,
87-             final  String  query , final  String  reference ,
85+             final  String  query ,
86+             final  String  reference ,
8887            final  boolean  absolute ,
8988            final  byte ... normalizedPath ) {
9089        this .fs  = fs ;
@@ -97,7 +96,7 @@ private HttpPath(final HttpFileSystem fs,
9796        this .absolute  = absolute ;
9897
9998        // normalized path bytes (shouldn't be null) 
100-         this .normalizedPath  = normalizedPath ;
99+         this .normalizedPath  = Utils . nonNull ( normalizedPath ,  () ->  "path may not be null" ) ;
101100    }
102101
103102    /** 
@@ -109,7 +108,7 @@ private HttpPath(final HttpFileSystem fs,
109108     * @param reference reference component for the URL (optional). 
110109     */ 
111110    HttpPath (final  HttpFileSystem  fs , final  String  path , final  String  query ,
112-             final  String  reference ) {
111+               final  String  reference ) {
113112        // always absolute and checking it when converting to byte[] 
114113        this (Utils .nonNull (fs , () -> "null fs" ), query , reference , true ,
115114                getNormalizedPathBytes (Utils .nonNull (path , () -> "null path" ), true ));
@@ -128,7 +127,7 @@ public boolean isAbsolute() {
128127    @ Override 
129128    public  Path  getRoot () {
130129        // root is a Path with only the byte array (always absolute) 
131-         return  new  HttpPath (fs , null , null , true );
130+         return  new  HttpPath (fs , null , null , true ,  new   byte [ 0 ] );
132131    }
133132
134133    /** 
@@ -352,80 +351,76 @@ public Path normalize() {
352351    /** 
353352     * {@inheritDoc} 
354353     * 
355-      * @implNote since HttpPaths are always absolute, it's really only useful to resolve the system file path 
356-      * type against them.  This will always return an HttpPath so if an absolute path of another type is given as other 
357-      * than this will throw {@link IllegalArgumentException} 
354+      * @implNote This implementation differs from the expected behavior of the method when other is absolute. Instead 
355+      * of returning other in that case it will throw {@link UnsupportedOperationException}. 
358356     */ 
359357    @ Override 
360358    public  HttpPath  resolve (final  Path  other ) {
361359        if (other  == null ){
362360            return  this ;
363-         } else  if (other .isAbsolute ()){
364-             if (other  instanceof  HttpPath  absolutePath ){
365-                 return  absolutePath ;
366-             } else  {
367-                 throw  new  IllegalArgumentException ("Cannot a resolve an absolute path which isn't an HttpPath against an"  +
368-                         " HttpPath." 
369-                         + "\n This path is: "  + this .toUriString ()
361+         }  else  if (other .isAbsolute ()){
362+             //Note: This violates the general contract of the method but shouldn't be important practically. 
363+                 throw  new  UnsupportedOperationException ("Cannot resolve an absolute path against an http(s) path." 
364+                         + "\n This path is: "  + this 
370365                        + "\n The problematic path is an instance of "  + other .getClass ().getName ()
371366                        + "\n Other path: "  + other );
372-             }
373367        } else  {
374-             final  URI  otherUri ;
375368            try  {
376-                 otherUri  = new  URI (other .toString ());
369+                 final  URI  otherUri  = new  URI (other .toString ()); // to string is used here instead of the expected toUri because 
370+                                                                 // toUri will produce normalized absolute paths in many filesystems which is 
371+                                                                 // what we don't want 
372+                 return  resolve (otherUri );
377373            } catch  (URISyntaxException  e ) {
378374                throw  new  IllegalArgumentException ("Can only resolve http(s) paths against fully encoded paths which are valid URIs." , e );
379375            }
380-             if (otherUri .getScheme () != null ){
381-                 throw  new  CantDealWithThisException ("Attempting to resolve this against a path which is relatve but looks " 
382-                         + "like it has a scheme." 
383-                         + "\n This: "  + this 
384-                         + "\n Other: "  + other 
385-                         + "\n Other interpretted as URI: "  + otherUri 
386-                         + "\n This is a limitatation of the current implementation of resolve." 
387-                         + "\n Please use choose a less horrible file name or get in touch with the developers to complain." );
388- 
389-             }
390-             return  new  HttpPath (fs , otherUri .getRawQuery (),
391-                     otherUri .getRawFragment (),
392-                     this .isAbsolute (),
393-                     concatPaths (this .normalizedPath , getNormalizedPathBytes (otherUri .getRawPath (), false )));
394376        }
395377    }
396378
397-     /** 
398-      * Thrown when an input could potentially be handled by this method but it's more 
399-      * complicated to deal with than the developers had time for 
400-      */ 
401-     public  static  class  CantDealWithThisException  extends  IllegalArgumentException {
402-         /** 
403-          * @param message explain why the developers can't deal wih this 
404-          */ 
405-         public  CantDealWithThisException (String  message ){
406-             super (message );
407-         }
379+     private  HttpPath  resolve (URI  other ){
380+         return  new  HttpPath (fs , other .getRawQuery (),
381+                 other .getRawFragment (),
382+                 this .isAbsolute (),
383+                 concatPaths (this .normalizedPath , getNormalizedPathBytes (other .getRawPath (), false )));
408384    }
409385
410- 
411386    @ Override 
412387    public  HttpPath  resolve (final  String  other ) {
413-         return  resolve (other  == null  ? null : Paths .get (other ));
388+         return  resolve (fromRelativeString (other ));  // Paths.get() and the filesystem equivalent can't be used here 
389+                                                     // because we don't allow them to create relative HttpPaths  
414390    }
415391
416392    @ Override 
417393    public  Path  resolveSibling (final  String  other ){
418-         return  resolveSibling (other  ==  null  ?  null  :  Paths . get (other ));
394+         return  resolveSibling (fromRelativeString (other ));
419395    }
396+ 
420397    @ Override 
421398    public  Path  relativize (final  Path  other ) {
422399        throw  new  UnsupportedOperationException ("Not implemented" );
423400    }
424401
402+     private  HttpPath  fromRelativeString (final  String  other ) {
403+         if  (other  == null ) {
404+             return  null ;
405+         } else  {
406+             try  {
407+                 final  URI  uri  = new  URI (other );
408+                 if  (uri .isAbsolute ()) {
409+                     throw  new  UnsupportedOperationException ("Resolving absolute URI strings against an HTTP path is not supported."  +
410+                             "\n URI: "  + uri );
411+                 }
412+                 return  new  HttpPath (getFileSystem (), uri .getRawFragment (), uri .getRawQuery (), false ,
413+                         getNormalizedPathBytes (uri .getRawPath (), false ));
414+             } catch  (URISyntaxException  e ) {
415+                 throw  new  IllegalArgumentException ("Cannot resolve against an invalid URI." , e );
416+             }
417+         }
418+     }
419+ 
425420    @ Override 
426421    public  URI  toUri () {
427422        try  {
428-             return  new  URI (toUriString ());
423+             return  new  URI (toUriString (true ));
429424        } catch  (final  URISyntaxException  e ) {
430425            throw  new  IOError (e );
431426        }
@@ -436,8 +431,8 @@ public Path toAbsolutePath() {
436431        if  (isAbsolute ()) {
437432            return  this ;
438433        }
439-         // just create  a new path with a different absolute status 
440-         return  new  HttpPath (fs , query , reference , true ,  normalizedPath );
434+         // just fromUri  a new path with a different absolute status 
435+         return  new  HttpPath (fs , query , reference , true ,normalizedPath );
441436    }
442437
443438    @ Override 
@@ -555,18 +550,25 @@ public int hashCode() {
555550
556551    @ Override 
557552    public  String  toString () {
558-         return  toUriString ();
553+         return  toUriString (isAbsolute () );
559554    }
560555
561-     private  String  toUriString () {
562- 
556+     private  String  toUriString (boolean  includeRoot ) {
563557        // TODO - maybe we should cache (https://github.com/magicDGS/jsr203-http/issues/18) 
564558        // adding scheme, authority and normalized path 
565-         final  StringBuilder  sb  = new  StringBuilder ()
566-                 .append (fs .provider ().getScheme ()) // scheme 
567-                 .append ("://" )
568-                 .append (fs .getAuthority ()) // authority 
569-                 .append (new  String (normalizedPath , HttpUtils .HTTP_PATH_CHARSET ));
559+         final  StringBuilder  sb  = new  StringBuilder ();
560+         if (includeRoot ) {
561+             sb .append (fs .provider ().getScheme ()) // scheme 
562+                     .append ("://" )
563+                     .append (fs .getAuthority ()) // authority 
564+                     .append (new  String (normalizedPath , HttpUtils .HTTP_PATH_CHARSET ));
565+         }  else  if ( normalizedPath .length  != 0 ){
566+             if (normalizedPath [0 ] == HttpUtils .HTTP_PATH_SEPARATOR_CHAR ) {
567+                 sb .append (new  String (normalizedPath , 1 , normalizedPath .length  - 1 , HttpUtils .HTTP_PATH_CHARSET ));
568+             } else  {
569+                  sb .append (new  String (normalizedPath , HttpUtils .HTTP_PATH_CHARSET ));
570+             }
571+         }
570572        if  (query  != null ) {
571573            sb .append ('?' ).append (query );
572574        }
@@ -719,4 +721,5 @@ private static byte[] concatPaths(byte[] array1, byte[] array2) {
719721        System .arraycopy (array2 , 0 , result , array1ModifiedLength  + 1 , array2 .length );
720722        return  result ;
721723    }
724+     
722725}
0 commit comments