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

Generated on Sat Dec 24 17:28:32 2005 for Mango by  doxygen 1.4.0