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

Generated on Sun Mar 6 00:30:56 2005 for Mango by doxygen 1.3.6