Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

Styled.d

Go to the documentation of this file.
00001 /*******************************************************************************
00002 
00003         @file Styled.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, Feb 2005
00034         @author         Kris
00035 
00036 
00037 *******************************************************************************/
00038 
00039 module mango.format.Styled;
00040 
00041 version (Ares)
00042          private import  std.c.ctype;
00043       else
00044          private import  std.ctype;
00045 
00046 private import  mango.io.Exception;
00047 
00048 /******************************************************************************
00049 
00050         Root class of the formatting hierarchy.
00051 
00052         While these functions are all static, they are encapsulated within 
00053         a class inheritance to preserve some namespace cohesion. One might 
00054         use structs for encapsualtion instead, but then inheritance would 
00055         be lost. Note that this root class is abstract to prevent accidental 
00056         instantiation of subclasses.
00057 
00058         See Styled.parse() for format specifiers.
00059 
00060 ******************************************************************************/
00061 
00062 abstract class Styled
00063 {
00064         private typedef int delegate (char[]) Utf8Sink;
00065         private typedef int function (double, inout Style) DblFormat;
00066         
00067         // array of spaces to pad styled-output
00068         private static char[256] Spaces = ' ';
00069 
00070         /**********************************************************************
00071 
00072                 Declare stylistic flags 
00073 
00074         **********************************************************************/
00075 
00076         enum Flags 
00077         {
00078                 Fill   = 1,                     // do some kind of padding
00079                 Left    = Fill << 1,            // left justify
00080                 Prec    = Left << 1,            // precision was provided
00081                 Hash    = Prec << 1,            // prefix integer with type
00082                 Space   = Hash << 1,            // prefix with space
00083                 Zero    = Space << 1,           // prefix integer with zero
00084                 Sign    = Zero << 1,            // unused
00085                 Comma   = Sign << 1,            // unused
00086                 Plus    = Comma << 1,           // prefix decimal with '+'
00087                 Array   = Plus << 1,            // array flag
00088         };
00089 
00090         /**********************************************************************
00091 
00092                 The state of a stylistic conversion. This can be embedded
00093                 within a class, or instantiated on the stack. Be sure to
00094                 initialize the Style via methods opCall() & setFormat()
00095 
00096         **********************************************************************/
00097 
00098         struct Style
00099         {
00100                 Utf8Sink        utf8;
00101                 int             type,
00102                                 width,
00103                                 precision;
00104                 Flags           flags;
00105                 char[]          head,
00106                                 tail,
00107                                 meta;
00108                 DblFormat       dFormat;
00109                 char[]          workspace;  
00110 
00111                 /**************************************************************
00112 
00113                         Configure this Style with an output handler, a
00114                         workspace area, and a floating point handler.
00115                         The latter is optional.
00116 
00117                 **************************************************************/
00118 
00119                 void opCall (Utf8Sink utf8, char[] workspace, DblFormat dFormat = null)
00120                 {
00121                         this.utf8 = utf8;
00122                         this.dFormat = dFormat;
00123                         this.workspace = workspace;
00124                 }
00125 
00126                 /**************************************************************
00127 
00128                         Set the format string for this Style
00129 
00130                 **************************************************************/
00131 
00132                 void setFormat (char[] format)
00133                 {
00134                         meta = format;
00135                 }
00136 
00137                 /**************************************************************
00138 
00139                         Clear the current state. This is typically used
00140                         internally only.
00141 
00142                 **************************************************************/
00143 
00144                 void clear ()
00145                 {
00146                         flags = 0;
00147                         head = tail = null;   
00148                         width = workspace.length;
00149                 }
00150         
00151                 /**************************************************************
00152 
00153                         Emit a field, surrounded by optional prefix and
00154                         postfix strings, and optionally padded with spaces.
00155 
00156                 **************************************************************/
00157 
00158                 int emit (char[] field)
00159                 {
00160                         int i = 0;
00161                         int pad = 0;
00162 
00163                         // emit prefix?
00164                         if (head.length)
00165                             i += utf8 (head);                   
00166 
00167                         // should we pad output?
00168                         if (flags & Flags.Fill && flags & Flags.Space)
00169                            {
00170                            pad = width - field.length;
00171                            if (pad < 0)
00172                                pad = 0;
00173                            else
00174                               if (pad > Spaces.length)
00175                                   error ("space padding greater than 256");
00176 
00177                            // right-aligned?
00178                            if (flags & Flags.Left == 0)
00179                               {
00180                               i += utf8 (Spaces[0..pad]);
00181                               pad = 0;
00182                               }                        
00183                            }
00184                            
00185                         // emit field itself
00186                         i += utf8 (field);
00187 
00188                         // any trailing padding?
00189                         if (pad)
00190                             i += utf8 (Spaces[0..pad]);
00191 
00192                         // emit postfix
00193                         if (tail.length)
00194                             i += utf8 (tail);   
00195                                             
00196                         return i;
00197                 }
00198 
00199                 /**************************************************************
00200 
00201                         Parse a format specifier into its constituent
00202                         flags and values. Syntax follows the traditional
00203                         printf() approach, as follows:
00204 
00205                         %[flags][width][.precision]type
00206 
00207                         Where 'type' is one of:
00208 
00209                         s : string format
00210                         c : character format
00211                         d : signed format
00212                         u : unsigned format
00213                         x : hexadecimal format
00214                         X : uppercase hexadecimal format
00215                         e : scientific notation 
00216                         f : floating point format
00217                         g : 'e' or 'f', based upon width
00218 
00219                         Note that there are no variants on the format
00220                         types ~ long, int, short, and byte differences
00221                         are all handled internally.
00222                         
00223                         The 'flags' supported:
00224 
00225                         space : prefix negative integer with one space;
00226                                 pad any type when combined with a width
00227                                 specifier
00228                         -     : left-align fields padded with spaces
00229                         +     : prefix positive integer with one '+'
00230                         0     : prefix integers with zeroes; requires a
00231                                 width specification
00232                         #     : prefix integers with a type specifier
00233                         @     : Array specifier
00234 
00235                         The 'width' should be specified for either zero or
00236                         space padding, and may be used with all formatting
00237                         types.
00238 
00239                         A 'precision' can be used to stipulate the number
00240                         of decimal-places, or a slice of a text string.
00241 
00242                         Note that the Format package supports array-output
00243                         in addition to the usual printf() output.
00244 
00245                 **************************************************************/
00246 
00247                 char parse (char[] format)
00248                 {
00249                         clear ();
00250                         head = format;
00251                         char* p = format.ptr;
00252 
00253                         for (int i = format.length; --i > 0; ++p)
00254                              if (*p == '%')
00255                                  if (p[1] == '%')
00256                                      ++p;
00257                                  else
00258                                     {
00259                                     int len = p - format.ptr;
00260                                     head = format [0..len];   
00261                                     while (1)
00262                                           {
00263                                           switch (--i, *++p)
00264                                                  {
00265                                                  case '-': 
00266                                                       flags |= Flags.Left;
00267                                                       continue;
00268 
00269                                                  case '+': 
00270                                                       flags |= Flags.Plus;
00271                                                       continue;
00272 
00273                                                  case '#': 
00274                                                       flags |= Flags.Hash;
00275                                                       continue;
00276 
00277                                                  case ' ': 
00278                                                       flags |= Flags.Space;
00279                                                       continue;
00280 
00281                                                  case '0': 
00282                                                       flags |= Flags.Zero;
00283                                                       continue;
00284 
00285                                                  case '@': 
00286                                                       flags |= Flags.Array;
00287                                                       continue;
00288 
00289                                                  default: 
00290                                                       ++i;
00291                                                       break;
00292                                                  }
00293                                           break;
00294                                           }
00295 
00296                                     if (isdigit(*p))
00297                                        {
00298                                        int tmp;
00299                                        do {
00300                                           tmp = tmp * 10 + (*p - '0');
00301                                           } while (--i && isdigit(*++p));
00302 
00303                                        flags |= Flags.Fill;
00304                                        width = tmp;
00305                                        }
00306                                     else
00307                                        flags &= ~Flags.Zero;
00308 
00309 
00310                                     if (*p == '.')
00311                                        {
00312                                        int tmp;
00313                                        while (i-- && isdigit(*++p))
00314                                               tmp = tmp * 10 + (*p - '0');
00315 
00316                                        flags |= Flags.Prec;
00317                                        precision = tmp;
00318                                        }
00319 
00320                                     if (--i < 0)
00321                                         error ("missing format specifier");
00322 
00323                                     tail = format [format.length-i..format.length];
00324                                     return type = *p;
00325                                     }
00326 
00327                         return type = 0;
00328                 }
00329         }
00330 
00331 
00332         /**********************************************************************
00333 
00334                 Throw an exception with the provided messsage
00335 
00336         **********************************************************************/
00337 
00338         final static void error (char[] msg)
00339         {
00340                 throw new FormatException (msg);
00341         }
00342 }
00343 
00344 
00345 /*******************************************************************************
00346 
00347         This exception is thrown by the Format package if it finds issue
00348         with the format specifiers and so on.
00349 
00350 *******************************************************************************/
00351 
00352 class FormatException : Exception
00353 {
00354         /***********************************************************************
00355         
00356                 Construct exception with the provided text string
00357 
00358         ***********************************************************************/
00359 
00360         this (char[] msg)
00361         {
00362                 super (msg);
00363         }
00364 }
00365 

Generated on Fri Nov 11 18:44:22 2005 for Mango by  doxygen 1.4.0