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.Exception,
00047                 mango.io.FileConst;
00048 
00049 private import  mango.io.model.IWriter;
00050  
00051 /*******************************************************************************
00052 
00053         Models a file name. These are expected to be used as the constructor 
00054         argument to File implementations. The intention is that they easily
00055         convert to other representations such as absolute, canonical, or Url.
00056         Note that this class is immutable. Use MutableFilePath if you wish
00057         to alter specific attributes.
00058 
00059         File paths containing non-ansi characters should be UTF-8 encoded. 
00060         Supporting Unicode in this manner was deemed to be more suitable 
00061         than providing a wchar version of FilePath, and is both consistent 
00062         & compatible with the approach taken with the Uri class.
00063 
00064 *******************************************************************************/
00065 
00066 class FilePath : IWritable
00067 {       
00068         private char[]  fp,
00069                         ext,
00070                         name,
00071                         path,
00072                         root,
00073                         suffix;
00074 
00075         private wchar[] fpWide;
00076 
00077         // version = MangoDebug;
00078         // version = MangoOptimize;
00079 
00080         private static const int MaxFilePathSize = 1024;
00081         
00082         /***********************************************************************
00083         
00084                 Create an empty FilePath. This is strictly for subclass
00085                 use.
00086 
00087         ***********************************************************************/
00088 
00089         protected this ()
00090         {
00091         }
00092 
00093         /***********************************************************************
00094         
00095                 Create a FilePath through reference to another.
00096 
00097         ***********************************************************************/
00098 
00099         this (FilePath other)
00100         in {
00101            assert (other);
00102            }
00103         body
00104         {
00105                 fp = other.fp;
00106                 ext = other.ext;
00107                 name = other.name;
00108                 path = other.path;
00109                 root = other.root;
00110                 suffix = other.suffix;
00111                 fpWide = other.fpWide;
00112         }
00113 /+
00114         /***********************************************************************
00115         
00116                 Create a FilePath from a Uri. Note that the Uri authority
00117                 is used to house an optional root (device, drive-letter ...)
00118 
00119         ***********************************************************************/
00120 
00121         this (Uri uri)
00122         {
00123                 char[] path = uri.getPath();
00124 
00125                 if (uri.getHost.length)
00126                     path = uri.getHost ~ FileConst.RootSeparatorString ~ path;
00127                 
00128                 this (path);
00129         }
00130 +/
00131         /***********************************************************************
00132         
00133                 Create a FilePath from the given string. Note the path
00134                 is not duplicated here, so you are expected to provide 
00135                 an immutable copy for the lifetime of this object. 
00136 
00137                 If you're not certain, duplicate the path first.
00138 
00139         ***********************************************************************/
00140 
00141         this (char[] filepath)
00142         in {
00143            assert (filepath);
00144            assert(filepath.length > 0);
00145            }
00146         out {
00147             if (root)
00148                 assert (root.length > 0);
00149             }
00150         body
00151         {
00152                 int     ext = -1,
00153                         path = -1,
00154                         root = -1,
00155                         suffix = -1;
00156                 
00157                 //printf ("FilePath: '%.*s'\n", filepath);
00158 
00159                 for (int i=filepath.length; i > 0; --i)
00160                      switch (filepath[i-1])
00161                             {
00162                             case FileConst.FileSeparatorChar:
00163                                  if (path < 0)
00164                                     {
00165                                     if (ext < 0)
00166                                        {
00167                                        // check for '..' sequence
00168                                        if (i > 1 && filepath[i-2] != FileConst.FileSeparatorChar)
00169                                            ext = i;
00170                                        }
00171                                     suffix = i;
00172                                     }
00173                                  break;
00174 
00175                             case FileConst.PathSeparatorChar:
00176                                  if (path < 0)
00177                                      path = i;
00178                                  break;
00179 
00180                             case FileConst.RootSeparatorChar:
00181                                  root = i;
00182                             default:
00183                                  break;
00184                             }
00185 
00186                 int i = filepath.length;
00187 
00188                 version (MangoDebug)
00189                 {
00190                 printf ("\n>>'%d' root:'%d' path:'%d' ext:'%d' suffix:'%d'\n", 
00191                         i, root, path, ext, suffix);
00192                 }
00193 
00194                 if (ext >= 0)
00195                    {
00196                    this.ext = filepath [ext..i];
00197                    this.suffix = filepath [suffix..i];
00198                    --ext;
00199                    }
00200                 else
00201                    ext = i;
00202 
00203                 if (root >= 1)
00204                     this.root = filepath [0..root-1];
00205                 else
00206                    root = 0;
00207 
00208                 if (path >= root)
00209                     this.path = filepath [root..path];
00210                 else
00211                    path = root;
00212 
00213                 this.name = filepath [path..ext];
00214 
00215                 // save original
00216                 this.fp = filepath;
00217 
00218                 version (MangoDebug)
00219                 {
00220                 printf (">>'%.*s' root:'%.*s' path:'%.*s' name:'%.*s' ext:'%.*s' suffix:'%.*s'\n", 
00221                         filepath, this.root, this.path, this.name, this.ext, this.suffix);
00222                 }
00223         }
00224                              
00225         /***********************************************************************
00226         
00227                 Convert path separators to the correct format. This mutates
00228                 the provided 'path' content, so .dup it as necessary.
00229 
00230         ***********************************************************************/
00231 
00232         static char[] normalize (inout char[] path)
00233         {
00234                 version (Win32)
00235                         {
00236                         foreach (int i, char c; path)
00237                                  if (c == '/')
00238                                      path[i] = '\\';
00239                         }
00240                      else
00241                         {
00242                         foreach (int i, char c; path)
00243                                  if (c == '\\')
00244                                      path[i] = '/';
00245                         }
00246                 return path;
00247         }
00248 
00249         /***********************************************************************
00250         
00251         ***********************************************************************/
00252 
00253         void reset ()
00254         {
00255                 fp = null;
00256                 fpWide = null;
00257         }
00258 
00259         /***********************************************************************
00260         
00261                 Returns true if this FilePath is *not* relative to the 
00262                 current working directory.
00263 
00264         ***********************************************************************/
00265 
00266         bool isAbsolute ()
00267         {
00268                 return (root.length || 
00269                        (path.length && path[0] == FileConst.PathSeparatorChar)
00270                        );
00271         }               
00272 
00273         /***********************************************************************
00274                 
00275                 Return the root of this path. Roots are constructs such as
00276                 "c:".
00277 
00278         ***********************************************************************/
00279 
00280         char[] getRoot ()
00281         {
00282                 return root;
00283         }               
00284 
00285         /***********************************************************************
00286         
00287                 Return the file path. Paths start with a '/' but do not
00288                 end with one. The root path is empty. Directory paths 
00289                 are split such that the directory name is placed into
00290                 the 'name' member.
00291 
00292         ***********************************************************************/
00293 
00294         char[] getPath ()
00295         {
00296                 return path;
00297         }             
00298 
00299         /***********************************************************************
00300         
00301                 Return the name of this file, or directory.
00302 
00303         ***********************************************************************/
00304 
00305         char[] getName ()
00306         {
00307                 return name;
00308         }               
00309 
00310         /***********************************************************************
00311         
00312                 Return the file-extension, sans seperator
00313 
00314         ***********************************************************************/
00315 
00316         char[] getExtension ()
00317         {
00318                 return ext;
00319         }              
00320 
00321         /***********************************************************************
00322         
00323                 Suffix is like extension, except it can include multiple
00324                 '.' sequences. For example, "wumpus1.foo.bar" has suffix
00325                 "foo.bar" and extension "bar".
00326 
00327         ***********************************************************************/
00328 
00329         char[] getSuffix ()
00330         {
00331                 return suffix;
00332         }              
00333 
00334         /***********************************************************************
00335 
00336                 Convert this FilePath to a char[]. This is expected to 
00337                 execute optimally in most cases.
00338 
00339         ***********************************************************************/
00340 
00341         char[] toString ()
00342         {
00343                 if (fp is null)
00344                    {  
00345                    // get the assembled path
00346                    char[] tmp = write (new Buffer(MaxFilePathSize)).toString;
00347 
00348                    // add a terminating '\0'
00349                    fp = new char[tmp.length + 1];
00350                    fp[tmp.length] = 0;
00351                    fp.length = fp.length - 1;
00352 
00353                    // copy new filepath content
00354                    fp[] = tmp;
00355                    }
00356                 return fp;
00357         }               
00358 
00359         /***********************************************************************
00360         
00361                 Write this FilePath to the given IWriter. This makes the
00362                 FilePath compatible with all Writers. Note that we could 
00363                 leverage the write(IBuffer) method here, but that would
00364                 bypass any special character converters attached to the
00365                 IWriter.
00366 
00367         ***********************************************************************/
00368 
00369         void write (IWriter write)
00370         {
00371                 if (root.length)
00372                     write (root) (FileConst.RootSeparatorChar);
00373 
00374                 if (path.length)
00375                     write (path);
00376                        
00377                 if (name.length)
00378                     write (name);
00379 
00380                 if (ext.length) 
00381                     write (FileConst.FileSeparatorChar) (ext);
00382         }               
00383 
00384         /***********************************************************************
00385 
00386                 Convert this FilePath to a char[] in the provided buffer
00387 
00388         ***********************************************************************/
00389 
00390         IBuffer write (IBuffer buf)
00391         {
00392                 if (root.length)
00393                     buf.append(root).append(FileConst.RootSeparatorString);
00394 
00395                 if (path.length)
00396                     buf.append(path);
00397 
00398                 if (name.length)
00399                     buf.append(name);
00400 
00401                 if (ext.length)
00402                     buf.append(FileConst.FileSeparatorString).append(ext);
00403 
00404                 return buf;
00405         }               
00406 
00407 /+
00408         /***********************************************************************
00409 
00410                 Convert this FilePath to a Uri. Note that a root (such as a
00411                 drive-letter, or device) is placed into the Uri authority
00412         
00413         ***********************************************************************/
00414 
00415         MutableUri toUri ()
00416         {
00417                 MutableUri uri = new MutableUri();
00418 
00419                 if (isAbsolute)
00420                     uri.setScheme ("file");
00421 
00422                 if (root.length)
00423                     uri.setHost (root);
00424 
00425                 char[] s = path~name;
00426                 if (ext.length)
00427                     s ~= FileConst.FileSeparatorString ~ ext;
00428 
00429                 version (Win32)
00430                          Text.replace (s, FileConst.PathSeparatorChar, '/');
00431                 uri.setPath (s);
00432                 return uri;
00433         }
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 Nov 11 18:44:19 2005 for Mango by  doxygen 1.4.0