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

Generated on Sun Oct 24 22:31:14 2004 for Mango by doxygen 1.3.6