Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members | Related Pages

FilePath.d

Go to the documentation of this file.
00001 /*******************************************************************************
00002 
00003         @file FilePath.d
00004         
00005         Copyright (C) 2004 Kris Bell
00006         
00007         This software is provided 'as-is', without any express or implied
00008         warranty. In no event will the authors be held liable for damages
00009         of any kind arising from the use of this software.
00010         
00011         Permission is hereby granted to anyone to use this software for any 
00012         purpose, including commercial applications, and to alter it and/or 
00013         redistribute it freely, subject to the following restrictions:
00014         
00015         1. The origin of this software must not be misrepresented; you must 
00016            not claim that you wrote the original software. If you use this 
00017            software in a product, an acknowledgment within documentation of 
00018            said product would be appreciated but is not required.
00019 
00020         2. Altered source versions must be plainly marked as such, and must 
00021            not be misrepresented as being the original software.
00022 
00023         3. This notice may not be removed or altered from any distribution
00024            of the source.
00025 
00026 
00027                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00028 
00029 
00030         @version        Initial version, March 2004      
00031         @author         Kris
00032 
00033 
00034 *******************************************************************************/
00035 
00036 module mango.io.FilePath;
00037 
00038 private import  mango.utils.Text;
00039 
00040 private import  mango.io.Uri,
00041                 mango.io.Utf8,
00042                 mango.io.Buffer,
00043                 mango.io.Writer,
00044                 mango.io.Exception,
00045                 mango.io.FileSystem,
00046                 mango.io.DisplayWriter;
00047 
00048 private import  mango.io.model.IWriter;
00049 
00050 /*******************************************************************************
00051 
00052         Models a file name. These are expected to be used as the constructor 
00053         argument to File implementations. The intention is that they easily
00054         convert to other representations such as absolute, canonical, or Url.
00055         Note that this class is immutable. Use MutableFilePath if you wish
00056         to alter specific attributes.
00057 
00058         File paths containing non-ansi characters should be UTF-8 encoded. 
00059         Supporting Unicode in this manner was deemed to be more suitable 
00060         than providing a wchar version of FilePath, and is both consistent 
00061         & compatible with the approach taken with the Uri class.
00062 
00063 *******************************************************************************/
00064 
00065 class FilePath : IWritable
00066 {       
00067         private char[]  fp,
00068                         ext,
00069                         name,
00070                         path,
00071                         root,
00072                         suffix;
00073 
00074         private wchar[] fpWide;
00075 
00076         // version = MangoDebug;
00077         // version = MangoOptimize;
00078 
00079         private static const int MaxFilePathSize = 1024;
00080         
00081         /***********************************************************************
00082         
00083                 Create an empty FilePath. This is strictly for subclass
00084                 use.
00085 
00086         ***********************************************************************/
00087 
00088         protected this ()
00089         {
00090         }
00091 
00092         /***********************************************************************
00093         
00094                 Create a FilePath through reference to another.
00095 
00096         ***********************************************************************/
00097 
00098         this (FilePath other)
00099         in {
00100            assert (other);
00101            }
00102         body
00103         {
00104                 fp = other.fp;
00105                 ext = other.ext;
00106                 name = other.name;
00107                 path = other.path;
00108                 root = other.root;
00109                 suffix = other.suffix;
00110                 fpWide = other.fpWide;
00111         }
00112 
00113         /***********************************************************************
00114         
00115                 Create a FilePath from a Uri. Note that the Uri authority
00116                 is used to house an optional root (device, drive-letter ...)
00117 
00118         ***********************************************************************/
00119 
00120         this (Uri uri)
00121         {
00122                 char[] path = uri.getPath();
00123 
00124                 if (uri.getHost.length)
00125                     path = uri.getHost ~ FileSystem.RootSeperatorString ~ path;
00126                 
00127                 this (path);
00128         }
00129 
00130         /***********************************************************************
00131         
00132                 Create a FilePath from the given string. Note the path
00133                 is not duplicated here, so you are expected to provide 
00134                 an immutable copy for the lifetime of this object. 
00135 
00136                 If you're not certain, duplicate the path first.
00137 
00138         ***********************************************************************/
00139 
00140         this (char[] filepath)
00141         in {
00142            assert (filepath);
00143            assert(filepath.length > 0);
00144            }
00145         out {
00146             if (root)
00147                 assert (root.length > 0);
00148             }
00149         body
00150         {
00151                 int     ext = -1,
00152                         path = -1,
00153                         root = -1,
00154                         suffix = -1;
00155                 
00156                 //printf ("FilePath: '%.*s'\n", filepath);
00157 
00158                 for (int i=filepath.length; i > 0; --i)
00159                      switch (filepath[i-1])
00160                             {
00161                             case FileSystem.FileSeperatorChar:
00162                                  if (path < 0)
00163                                     {
00164                                     if (ext < 0)
00165                                        {
00166                                        // check for '..' sequence
00167                                        if (i > 1 && filepath[i-2] != FileSystem.FileSeperatorChar)
00168                                            ext = i;
00169                                        }
00170                                     suffix = i;
00171                                     }
00172                                  break;
00173 
00174                             case FileSystem.PathSeperatorChar:
00175                                  if (path < 0)
00176                                      path = i;
00177                                  break;
00178 
00179                             case FileSystem.RootSeperatorChar:
00180                                  root = i;
00181                             default:
00182                                  break;
00183                             }
00184 
00185                 int i = filepath.length;
00186 
00187                 version (MangoDebug)
00188                 {
00189                 printf ("\n>>'%d' root:'%d' path:'%d' ext:'%d' suffix:'%d'\n", 
00190                         i, root, path, ext, suffix);
00191                 }
00192 
00193                 if (ext >= 0)
00194                    {
00195                    this.ext = filepath [ext..i];
00196                    this.suffix = filepath [suffix..i];
00197                    --ext;
00198                    }
00199                 else
00200                    ext = i;
00201 
00202                 if (root >= 1)
00203                     this.root = filepath [0..root-1];
00204                 else
00205                    root = 0;
00206 
00207                 if (path >= root)
00208                     this.path = filepath [root..path];
00209                 else
00210                    path = root;
00211 
00212                 this.name = filepath [path..ext];
00213 
00214                 // save original
00215                 this.fp = filepath;
00216 
00217                 version (MangoDebug)
00218                 {
00219                 printf (">>'%.*s' root:'%.*s' path:'%.*s' name:'%.*s' ext:'%.*s' suffix:'%.*s'\n", 
00220                         filepath, this.root, this.path, this.name, this.ext, this.suffix);
00221                 }
00222         }
00223                              
00224         /***********************************************************************
00225         
00226         ***********************************************************************/
00227 
00228         void reset ()
00229         {
00230                 fp = null;
00231                 fpWide = null;
00232         }
00233 
00234         /***********************************************************************
00235         
00236                 Returns true if this FilePath is *not* relative to the 
00237                 current working directory.
00238 
00239         ***********************************************************************/
00240 
00241         bool isAbsolute ()
00242         {
00243                 return (root.length || 
00244                        (path.length && path[0] == FileSystem.PathSeperatorChar)
00245                        );
00246         }               
00247 
00248         /***********************************************************************
00249                 
00250                 Return the root of this path. Roots are constructs such as
00251                 "c:".
00252 
00253         ***********************************************************************/
00254 
00255         char[] getRoot ()
00256         {
00257                 return root;
00258         }               
00259 
00260         /***********************************************************************
00261         
00262                 Return the file path. Paths start with a '/' but do not
00263                 end with one. The root path is empty. Directory paths 
00264                 are split such that the directory name is placed into
00265                 the 'name' member.
00266 
00267         ***********************************************************************/
00268 
00269         char[] getPath ()
00270         {
00271                 return path;
00272         }             
00273 
00274         /***********************************************************************
00275         
00276                 Return the name of this file, or directory.
00277 
00278         ***********************************************************************/
00279 
00280         char[] getName ()
00281         {
00282                 return name;
00283         }               
00284 
00285         /***********************************************************************
00286         
00287                 Return the file-extension, sans seperator
00288 
00289         ***********************************************************************/
00290 
00291         char[] getExtension ()
00292         {
00293                 return ext;
00294         }              
00295 
00296         /***********************************************************************
00297         
00298                 Suffix is like extension, except it can include multiple
00299                 '.' sequences. For example, "wumpus1.foo.bar" has suffix
00300                 "foo.bar" and extension "bar".
00301 
00302         ***********************************************************************/
00303 
00304         char[] getSuffix ()
00305         {
00306                 return suffix;
00307         }              
00308 
00309         /***********************************************************************
00310 
00311                 Convert this FilePath to a char[]. This is expected to 
00312                 execute optimally in most cases.
00313 
00314         ***********************************************************************/
00315 
00316         char[] toString ()
00317         {
00318                 if (fp is null)
00319                    {  
00320                    Buffer buf = new Buffer (MaxFilePathSize);
00321                    
00322                    version (MangoOptimize)
00323                            {
00324                            if (root.length)
00325                                buf.append(root).append(FileSystem.RootSeperatorString);
00326 
00327                            if (path.length)
00328                                buf.append(path);
00329 
00330                            if (name.length)
00331                                buf.append(name);
00332 
00333                            if (ext.length)
00334                                buf.append(FileSystem.FileSeperatorString).append(ext);
00335                            }
00336                         else
00337                            write (new DisplayWriter (buf));
00338 
00339                    // get the assembled path
00340                    char[] tmp = buf.toString;
00341 
00342                    // this will add a terminating '\0'
00343                    fp = new char[tmp.length];
00344 
00345                    // copy new filepath content
00346                    fp[0..tmp.length] = tmp[0..tmp.length];
00347                    }
00348                 return fp;
00349         }               
00350 
00351         /***********************************************************************
00352 
00353                 Convert this FilePath to a Uri. Note that a root (such as a
00354                 drive-letter, or device) is placed into the Uri authority
00355         
00356         ***********************************************************************/
00357 
00358         MutableUri toUri ()
00359         {
00360                 MutableUri uri = new MutableUri();
00361 
00362                 if (isAbsolute)
00363                     uri.setScheme ("file");
00364 
00365                 if (root.length)
00366                     uri.setHost (root);
00367 
00368                 char[] s = path~name;
00369                 if (ext.length)
00370                     s ~= FileSystem.FileSeperatorString ~ ext;
00371 
00372                 version (Win32)
00373                          Text.replace (s, FileSystem.PathSeperatorChar, '/');
00374                 uri.setPath (s);
00375                 return uri;
00376         }
00377 
00378         /***********************************************************************
00379         
00380                 Write this FilePath to the given IWriter. This makes the
00381                 FilePath compatible with all Writers
00382 
00383         ***********************************************************************/
00384 
00385         void write (IWriter writer)
00386         {
00387                 if (root.length)
00388                     writer.put(root).put(FileSystem.RootSeperatorChar);
00389 
00390                 if (path.length)
00391                     writer.put(path);
00392                        
00393                 if (name.length)
00394                     writer.put(name);
00395 
00396                 if (ext.length)
00397                     writer.put(FileSystem.FileSeperatorChar).put(ext);
00398         }               
00399 
00400 
00401         /***********************************************************************
00402         
00403                 Return a zero terminated version of this file path. Note
00404                 that the compiler places a zero at the end of each static 
00405                 string, as does the allocator for char[] requests.
00406 
00407                 In typical usage, this will not need to duplicate the path
00408 
00409         ***********************************************************************/
00410 
00411         char[] toUtf8 ()
00412         {
00413                 char* p = fp;
00414 
00415                 // reconstruct if not terminated
00416                 if (p && *(cast(char *) p + fp.length))
00417                     reset();
00418 
00419                 return toString;
00420         }
00421 
00422         /***********************************************************************
00423         
00424         ***********************************************************************/
00425 
00426         wchar[] toUtf16 ()
00427         {
00428                 if (! fpWide)
00429                    {
00430                    fpWide = Utf8.decode (toString);
00431                    *(cast(wchar*) fpWide + fpWide.length) = 0;
00432                    }
00433                 return fpWide;
00434         }
00435 
00436         /***********************************************************************
00437         
00438                 Splice this FilePath onto the end of the provided base path.
00439                 Output is return as a char[].
00440 
00441         ***********************************************************************/
00442 
00443         char[] splice (FilePath base)
00444         {      
00445                 return splice (base, new Buffer(MaxFilePathSize)).toString();
00446         }               
00447 
00448         /***********************************************************************
00449         
00450                 Splice this FilePath onto the end of the provided base path.
00451                 Output is placed into the provided IBuffer.
00452 
00453         ***********************************************************************/
00454 
00455         IBuffer splice (FilePath base, IBuffer buf)
00456         {      
00457                 if (base.root.length)
00458                     buf.append(base.root).append(FileSystem.RootSeperatorString);
00459 
00460                 if (base.path.length)
00461                     buf.append(base.path);
00462 
00463                 if (base.name.length)
00464                     buf.append(base.name).append(FileSystem.PathSeperatorString);
00465 
00466                 if (path.length)
00467                     buf.append(path).append(FileSystem.PathSeperatorString);
00468                        
00469                 if (name.length)
00470                     buf.append(name);
00471 
00472                 if (ext.length)
00473                     buf.append(FileSystem.FileSeperatorString).append(ext);
00474 
00475                 return buf;
00476         }               
00477 
00478         /***********************************************************************
00479         
00480                 Find the next parent of the FilePath. Returns a valid index
00481                 to the seperator when present, -1 otherwise.
00482 
00483         ***********************************************************************/
00484 
00485         private int locateParent ()
00486         {
00487                 int i = path.length;
00488 
00489                 // set new path to rightmost PathSeperator
00490                 if (--i > 0)
00491                     while (--i >= 0)
00492                            if (path[i] == FileSystem.PathSeperatorChar)
00493                                return i;
00494                 return -1;
00495         }               
00496 
00497         /***********************************************************************
00498         
00499                 Returns a FilePath representing the parent of this one. An
00500                 exception is thrown if there is not parent (at the root).
00501 
00502         ***********************************************************************/
00503 
00504         FilePath toParent ()
00505         {
00506                 // set new path to rightmost PathSeperator
00507                 int i = locateParent();
00508 
00509                 if (i >= 0)
00510                    {
00511                    FilePath parent = new FilePath (this);
00512 
00513                    // slice path subsection
00514                    parent.path = path [0..i+1];
00515                    parent.reset ();
00516                    return parent;
00517                    }
00518 
00519                 // return null? throw exception? return null? Hmmmm ...
00520                 throw new IOException ("Cannot create parent path for an orphan file");
00521         }               
00522 
00523         /***********************************************************************
00524                 
00525                 Returns true if this FilePath has a parent.
00526 
00527         ***********************************************************************/
00528 
00529         bool isChild ()
00530         {
00531                 return locateParent() >= 0;
00532         }               
00533 
00534         /***********************************************************************
00535         
00536                 Return a cloned FilePath with a different name.
00537 
00538         ***********************************************************************/
00539 
00540         FilePath toSibling (char[] name)
00541         {
00542                 return toSibling (name, ext, suffix);
00543         }
00544 
00545         /***********************************************************************
00546         
00547                 Return a cloned FilePath with a different name and extension.
00548                 Note that the suffix is destroyed.
00549 
00550         ***********************************************************************/
00551 
00552         FilePath toSibling (char[] name, char[] ext)
00553         {
00554                 return toSibling (name, ext, suffix);
00555         }
00556 
00557         /***********************************************************************
00558         
00559                 Return a cloned FilePath with a different name, extension,
00560                 and suffix.
00561 
00562         ***********************************************************************/
00563 
00564         FilePath toSibling (char[] name, char[] ext, char[] suffix) 
00565         {
00566                 FilePath sibling = new FilePath (this);
00567 
00568                 sibling.suffix = suffix;
00569                 sibling.name = name;
00570                 sibling.ext = ext;
00571                 sibling.reset ();
00572 
00573                 return sibling;
00574         }
00575 }
00576 
00577 
00578 /*******************************************************************************
00579 
00580         Mutable version of FilePath, which allows one to change individual
00581         attributes. A change to any attribute will cause method toString() 
00582         to rebuild the output.
00583 
00584 *******************************************************************************/
00585 
00586 class MutableFilePath : FilePath
00587 {       
00588         /***********************************************************************
00589         
00590                 Create an empty MutableFilePath
00591 
00592         ***********************************************************************/
00593 
00594         this ()
00595         {
00596         }
00597 
00598         /***********************************************************************
00599         
00600                 Create a MutableFilePath through reference to another.
00601 
00602         ***********************************************************************/
00603 
00604         this (FilePath other)
00605         {
00606                 super (other);
00607         }
00608 
00609         /***********************************************************************
00610         
00611                 Set the extension of this FilePath.
00612 
00613         ***********************************************************************/
00614 
00615         private final MutableFilePath set (char[]* x, char[]* v)
00616         {       
00617                 *x = *v;
00618                 reset ();
00619                 return this;
00620         }
00621 
00622         /***********************************************************************
00623         
00624                 Set the extension of this FilePath.
00625 
00626         ***********************************************************************/
00627 
00628         MutableFilePath setExt (char[] ext)
00629         {
00630                 return set (&this.ext, &ext);
00631         }
00632 
00633         /***********************************************************************
00634         
00635                 Set the name of this FilePath.
00636 
00637         ***********************************************************************/
00638 
00639         MutableFilePath setName (char[] name)
00640         {
00641                 return set (&this.name, &name);
00642         }
00643 
00644         /***********************************************************************
00645         
00646                 Set the path of this FilePath.
00647 
00648         ***********************************************************************/
00649 
00650         MutableFilePath setPath (char[] path)
00651         {
00652                 return set (&this.path, &path);
00653         }
00654 
00655         /***********************************************************************
00656         
00657                 Set the root of this FilePath (such as "c:")
00658 
00659         ***********************************************************************/
00660 
00661         MutableFilePath setRoot (char[] root)
00662         {
00663                 return set (&this.root, &root);
00664         }
00665 
00666         /***********************************************************************
00667         
00668                 Set the suffix of this FilePath.
00669 
00670         ***********************************************************************/
00671 
00672         MutableFilePath setSuffix (char[] suffix)
00673         {
00674                 return set (&this.suffix, &suffix);
00675         }
00676 }

Generated on Sun Nov 7 19:06:50 2004 for Mango by doxygen 1.3.6