Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | 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.Utf,
00045                 mango.io.Buffer,
00046                 mango.io.Writer,
00047                 mango.io.Exception,
00048                 mango.io.FileConst,
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 ~ FileConst.RootSeparatorString ~ 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 FileConst.FileSeparatorChar:
00165                                  if (path < 0)
00166                                     {
00167                                     if (ext < 0)
00168                                        {
00169                                        // check for '..' sequence
00170                                        if (i > 1 && filepath[i-2] != FileConst.FileSeparatorChar)
00171                                            ext = i;
00172                                        }
00173                                     suffix = i;
00174                                     }
00175                                  break;
00176 
00177                             case FileConst.PathSeparatorChar:
00178                                  if (path < 0)
00179                                      path = i;
00180                                  break;
00181 
00182                             case FileConst.RootSeparatorChar:
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                 Convert path separators to the correct format. This mutates
00230                 the provided 'path' content, so .dup it as necessary.
00231 
00232         ***********************************************************************/
00233 
00234         static char[] normalize (inout char[] path)
00235         {
00236                 version (Win32)
00237                         {
00238                         foreach (int i, char c; path)
00239                                  if (c == '/')
00240                                      path[i] = '\\';
00241                         }
00242                      else
00243                         {
00244                         foreach (int i, char c; path)
00245                                  if (c == '\\')
00246                                      path[i] = '/';
00247                         }
00248                 return path;
00249         }
00250 
00251         /***********************************************************************
00252         
00253         ***********************************************************************/
00254 
00255         void reset ()
00256         {
00257                 fp = null;
00258                 fpWide = null;
00259         }
00260 
00261         /***********************************************************************
00262         
00263                 Returns true if this FilePath is *not* relative to the 
00264                 current working directory.
00265 
00266         ***********************************************************************/
00267 
00268         bool isAbsolute ()
00269         {
00270                 return (root.length || 
00271                        (path.length && path[0] == FileConst.PathSeparatorChar)
00272                        );
00273         }               
00274 
00275         /***********************************************************************
00276                 
00277                 Return the root of this path. Roots are constructs such as
00278                 "c:".
00279 
00280         ***********************************************************************/
00281 
00282         char[] getRoot ()
00283         {
00284                 return root;
00285         }               
00286 
00287         /***********************************************************************
00288         
00289                 Return the file path. Paths start with a '/' but do not
00290                 end with one. The root path is empty. Directory paths 
00291                 are split such that the directory name is placed into
00292                 the 'name' member.
00293 
00294         ***********************************************************************/
00295 
00296         char[] getPath ()
00297         {
00298                 return path;
00299         }             
00300 
00301         /***********************************************************************
00302         
00303                 Return the name of this file, or directory.
00304 
00305         ***********************************************************************/
00306 
00307         char[] getName ()
00308         {
00309                 return name;
00310         }               
00311 
00312         /***********************************************************************
00313         
00314                 Return the file-extension, sans seperator
00315 
00316         ***********************************************************************/
00317 
00318         char[] getExtension ()
00319         {
00320                 return ext;
00321         }              
00322 
00323         /***********************************************************************
00324         
00325                 Suffix is like extension, except it can include multiple
00326                 '.' sequences. For example, "wumpus1.foo.bar" has suffix
00327                 "foo.bar" and extension "bar".
00328 
00329         ***********************************************************************/
00330 
00331         char[] getSuffix ()
00332         {
00333                 return suffix;
00334         }              
00335 
00336         /***********************************************************************
00337 
00338                 Convert this FilePath to a char[]. This is expected to 
00339                 execute optimally in most cases.
00340 
00341         ***********************************************************************/
00342 
00343         char[] toString ()
00344         {
00345                 if (fp is null)
00346                    {  
00347                    // get the assembled path
00348                    char[] tmp = write (new Buffer(MaxFilePathSize)).toString;
00349 
00350                    // add a terminating '\0'
00351                    fp = new char[tmp.length + 1];
00352                    fp[tmp.length] = 0;
00353                    fp.length = fp.length - 1;
00354 
00355                    // copy new filepath content
00356                    fp[] = tmp;
00357                    }
00358                 return fp;
00359         }               
00360 
00361         /***********************************************************************
00362         
00363                 Write this FilePath to the given IWriter. This makes the
00364                 FilePath compatible with all Writers. Note that we could 
00365                 leverage the write(IBuffer) method here, but that would
00366                 bypass any special character converters attached to the
00367                 IWriter.
00368 
00369         ***********************************************************************/
00370 
00371         void write (IWriter write)
00372         {
00373                 if (root.length)
00374                     write (root) (FileConst.RootSeparatorChar);
00375 
00376                 if (path.length)
00377                     write (path);
00378                        
00379                 if (name.length)
00380                     write (name);
00381 
00382                 if (ext.length) 
00383                     write (FileConst.FileSeparatorChar) (ext);
00384         }               
00385 
00386         /***********************************************************************
00387 
00388                 Convert this FilePath to a char[] in the provided buffer
00389 
00390         ***********************************************************************/
00391 
00392         IBuffer write (IBuffer buf)
00393         {
00394                 if (root.length)
00395                     buf.append(root).append(FileConst.RootSeparatorString);
00396 
00397                 if (path.length)
00398                     buf.append(path);
00399 
00400                 if (name.length)
00401                     buf.append(name);
00402 
00403                 if (ext.length)
00404                     buf.append(FileConst.FileSeparatorString).append(ext);
00405 
00406                 return buf;
00407         }               
00408 
00409         /***********************************************************************
00410 
00411                 Convert this FilePath to a Uri. Note that a root (such as a
00412                 drive-letter, or device) is placed into the Uri authority
00413         
00414         ***********************************************************************/
00415 
00416         MutableUri toUri ()
00417         {
00418                 MutableUri uri = new MutableUri();
00419 
00420                 if (isAbsolute)
00421                     uri.setScheme ("file");
00422 
00423                 if (root.length)
00424                     uri.setHost (root);
00425 
00426                 char[] s = path~name;
00427                 if (ext.length)
00428                     s ~= FileConst.FileSeparatorString ~ ext;
00429 
00430                 version (Win32)
00431                          Text.replace (s, FileConst.PathSeparatorChar, '/');
00432                 uri.setPath (s);
00433                 return uri;
00434         }
00435 
00436         /***********************************************************************
00437         
00438                 Return a zero terminated version of this file path. Note
00439                 that the compiler places a zero at the end of each static 
00440                 string, as does the allocator for char[] requests.
00441 
00442                 In typical usage, this will not need to duplicate the path
00443 
00444         ***********************************************************************/
00445 
00446         char[] toUtf8 ()
00447         {
00448                 char* p = fp;
00449 
00450                 // reconstruct if not terminated
00451                 if (p && *(cast(char *) p + fp.length))
00452                     reset();
00453 
00454                 return toString;
00455         }
00456 
00457         /***********************************************************************
00458         
00459         ***********************************************************************/
00460 
00461         wchar[] toUtf16 ()
00462         {
00463                 if (! fpWide)
00464                    {
00465                    fpWide = Utf.toUtf16 (toString);
00466                    *(cast(wchar*) fpWide + fpWide.length) = 0;
00467                    }
00468                 return fpWide;
00469         }
00470 
00471         /***********************************************************************
00472         
00473                 Splice this FilePath onto the end of the provided base path.
00474                 Output is return as a char[].
00475 
00476         ***********************************************************************/
00477 
00478         char[] splice (FilePath base)
00479         {      
00480                 return splice (base, new Buffer(MaxFilePathSize)).toString();
00481         }               
00482 
00483         /***********************************************************************
00484         
00485                 Splice this FilePath onto the end of the provided base path.
00486                 Output is placed into the provided IBuffer.
00487 
00488         ***********************************************************************/
00489 
00490         IBuffer splice (FilePath base, IBuffer buf)
00491         {      
00492                 int pos = buf.getLimit;
00493                 base.write (buf);
00494                 if (buf.getLimit != pos)
00495                     buf.append (FileConst.PathSeparatorString);
00496 
00497                 if (path.length)
00498                     buf.append(path);
00499                        
00500                 if (name.length)
00501                     buf.append(name);
00502 
00503                 if (ext.length)
00504                     buf.append(FileConst.FileSeparatorString).append(ext);
00505 
00506                 return buf;
00507         }               
00508 
00509         /***********************************************************************
00510         
00511                 Find the next parent of the FilePath. Returns a valid index
00512                 to the seperator when present, -1 otherwise.
00513 
00514         ***********************************************************************/
00515 
00516         private int locateParent ()
00517         {
00518                 int i = path.length;
00519 
00520                 // set new path to rightmost PathSeparator
00521                 if (--i > 0)
00522                     while (--i >= 0)
00523                            if (path[i] == FileConst.PathSeparatorChar)
00524                                return i;
00525                 return -1;
00526         }               
00527 
00528         /***********************************************************************
00529         
00530                 Returns a FilePath representing the parent of this one. An
00531                 exception is thrown if there is not parent (at the root).
00532 
00533         ***********************************************************************/
00534 
00535         FilePath toParent ()
00536         {
00537                 // set new path to rightmost PathSeparator
00538                 int i = locateParent();
00539 
00540                 if (i >= 0)
00541                    {
00542                    FilePath parent = new FilePath (this);
00543 
00544                    // slice path subsection
00545                    parent.path = path [0..i+1];
00546                    parent.reset ();
00547                    return parent;
00548                    }
00549 
00550                 // return null? throw exception? return null? Hmmmm ...
00551                 throw new IOException ("Cannot create parent path for an orphan file");
00552         }               
00553 
00554         /***********************************************************************
00555                 
00556                 Returns true if this FilePath has a parent.
00557 
00558         ***********************************************************************/
00559 
00560         bool isChild ()
00561         {
00562                 return locateParent() >= 0;
00563         }               
00564 
00565         /***********************************************************************
00566         
00567                 Return a cloned FilePath with a different name.
00568 
00569         ***********************************************************************/
00570 
00571         FilePath toSibling (char[] name)
00572         {
00573                 return toSibling (name, ext, suffix);
00574         }
00575 
00576         /***********************************************************************
00577         
00578                 Return a cloned FilePath with a different name and extension.
00579                 Note that the suffix is destroyed.
00580 
00581         ***********************************************************************/
00582 
00583         FilePath toSibling (char[] name, char[] ext)
00584         {
00585                 return toSibling (name, ext, suffix);
00586         }
00587 
00588         /***********************************************************************
00589         
00590                 Return a cloned FilePath with a different name, extension,
00591                 and suffix.
00592 
00593         ***********************************************************************/
00594 
00595         FilePath toSibling (char[] name, char[] ext, char[] suffix) 
00596         {
00597                 FilePath sibling = new FilePath (this);
00598 
00599                 sibling.suffix = suffix;
00600                 sibling.name = name;
00601                 sibling.ext = ext;
00602                 sibling.reset ();
00603 
00604                 return sibling;
00605         }
00606 }
00607 
00608 
00609 /*******************************************************************************
00610 
00611         Mutable version of FilePath, which allows one to change individual
00612         attributes. A change to any attribute will cause method toString() 
00613         to rebuild the output.
00614 
00615 *******************************************************************************/
00616 
00617 class MutableFilePath : FilePath
00618 {       
00619         /***********************************************************************
00620         
00621                 Create an empty MutableFilePath
00622 
00623         ***********************************************************************/
00624 
00625         this ()
00626         {
00627         }
00628 
00629         /***********************************************************************
00630         
00631                 Create a MutableFilePath through reference to another.
00632 
00633         ***********************************************************************/
00634 
00635         this (FilePath other)
00636         {
00637                 super (other);
00638         }
00639 
00640         /***********************************************************************
00641         
00642                 Create a MutableFilePath via a filename.
00643 
00644         ***********************************************************************/
00645 
00646         this (char[] name)
00647         {
00648                 super (name);
00649         }
00650 
00651         /***********************************************************************
00652         
00653                 Set the extension of this FilePath.
00654 
00655         ***********************************************************************/
00656 
00657         private final MutableFilePath set (char[]* x, char[]* v)
00658         {       
00659                 *x = *v;
00660                 reset ();
00661                 return this;
00662         }
00663 
00664         /***********************************************************************
00665         
00666                 Set the extension of this FilePath.
00667 
00668         ***********************************************************************/
00669 
00670         MutableFilePath setExt (char[] ext)
00671         {
00672                 return set (&this.ext, &ext);
00673         }
00674 
00675         /***********************************************************************
00676         
00677                 Set the name of this FilePath.
00678 
00679         ***********************************************************************/
00680 
00681         MutableFilePath setName (char[] name)
00682         {
00683                 return set (&this.name, &name);
00684         }
00685 
00686         /***********************************************************************
00687         
00688                 Set the path of this FilePath.
00689 
00690         ***********************************************************************/
00691 
00692         MutableFilePath setPath (char[] path)
00693         {
00694                 return set (&this.path, &path);
00695         }
00696 
00697         /***********************************************************************
00698         
00699                 Set the root of this FilePath (such as "c:")
00700 
00701         ***********************************************************************/
00702 
00703         MutableFilePath setRoot (char[] root)
00704         {
00705                 return set (&this.root, &root);
00706         }
00707 
00708         /***********************************************************************
00709         
00710                 Set the suffix of this FilePath.
00711 
00712         ***********************************************************************/
00713 
00714         MutableFilePath setSuffix (char[] suffix)
00715         {
00716                 return set (&this.suffix, &suffix);
00717         }
00718 }

Generated on Fri May 27 18:11:55 2005 for Mango by  doxygen 1.4.0