Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

root/trunk/tango/text/convert/Layout.d

Revision 3969, 38.0 kB (checked in by kris, 1 week ago)

renamed a private method

  • Property svn:mime-type set to text/x-dsrc
  • Property svn:eol-style set to native
Line 
1 /*******************************************************************************
2
3         copyright:      Copyright (c) 2005 Kris. All rights reserved
4
5         license:        BSD style: $(LICENSE)
6
7         version:        Initial release: 2005
8
9         author:         Kris, Keinfarbton
10
11         This module provides a general-purpose formatting system to
12         convert values to text suitable for display. There is support
13         for alignment, justification, and common format specifiers for
14         numbers.
15
16         Layout can be customized via configuring various handlers and
17         associated meta-data. This is utilized to plug in text.locale
18         for handling custom formats, date/time and culture-specific
19         conversions.
20
21         The format notation is influenced by that used by the .NET
22         and ICU frameworks, rather than C-style printf or D-style
23         writef notation.
24
25 ******************************************************************************/
26
27 module tango.text.convert.Layout;
28
29 private import  tango.core.Exception;
30
31 private import  Utf = tango.text.convert.Utf;
32
33 private import  Float  = tango.text.convert.Float,
34                 Integer = tango.text.convert.Integer;
35
36 private import  tango.io.model.IConduit : OutputStream;
37
38 /*******************************************************************************
39
40         Platform issues ...
41
42 *******************************************************************************/
43
44 version (GNU)
45         {
46         private import std.stdarg;
47         alias void* Arg;
48         alias va_list ArgList;
49         }
50      else
51         {
52         alias void* Arg;
53         alias void* ArgList;
54         }
55
56 /*******************************************************************************
57
58         Contains methods for replacing format items in a string with string
59         equivalents of each argument.
60
61 *******************************************************************************/
62
63 class Layout(T)
64 {
65         public alias convert opCall;
66         public alias uint delegate (T[]) Sink;
67
68         /**********************************************************************
69
70         **********************************************************************/
71
72         public final T[] sprint (T[] result, T[] formatStr, ...)
73         {
74                 return vprint (result, formatStr, _arguments, _argptr);
75         }
76
77         /**********************************************************************
78
79         **********************************************************************/
80
81         public final T[] vprint (T[] result, T[] formatStr, TypeInfo[] arguments, ArgList args)
82         {
83                 T*  p = result.ptr;
84                 int available = result.length;
85
86                 uint sink (T[] s)
87                 {
88                         int len = s.length;
89                         if (len > available)
90                             len = available;
91
92                         available -= len;
93                         p [0..len] = s[0..len];
94                         p += len;
95                         return len;
96                 }
97
98                 convert (&sink, arguments, args, formatStr);
99                 return result [0 .. p-result.ptr];
100         }
101
102         /**********************************************************************
103
104                 Replaces the _format item in a string with the string
105                 equivalent of each argument.
106
107                 Params:
108                   formatStr  = A string containing _format items.
109                   args       = A list of arguments.
110
111                 Returns: A copy of formatStr in which the items have been
112                 replaced by the string equivalent of the arguments.
113
114                 Remarks: The formatStr parameter is embedded with _format
115                 items of the form: $(BR)$(BR)
116                   {index[,alignment][:_format-string]}$(BR)$(BR)
117                   $(UL $(LI index $(BR)
118                     An integer indicating the element in a list to _format.)
119                   $(LI alignment $(BR)
120                     An optional integer indicating the minimum width. The
121                     result is padded with spaces if the length of the value
122                     is less than alignment.)
123                   $(LI _format-string $(BR)
124                     An optional string of formatting codes.)
125                 )$(BR)
126
127                 The leading and trailing braces are required. To include a
128                 literal brace character, use two leading or trailing brace
129                 characters.$(BR)$(BR)
130                 If formatStr is "{0} bottles of beer on the wall" and the
131                 argument is an int with the value of 99, the return value
132                 will be:$(BR) "99 bottles of beer on the wall".
133
134         **********************************************************************/
135
136         public final T[] convert (T[] formatStr, ...)
137         {
138                 return convert (_arguments, _argptr, formatStr);
139         }
140
141         /**********************************************************************
142
143         **********************************************************************/
144
145         public final uint convert (Sink sink, T[] formatStr, ...)
146         {
147                 return convert (sink, _arguments, _argptr, formatStr);
148         }
149
150         /**********************************************************************
151
152             Tentative convert using an OutputStream as sink - may still be
153             removed.
154
155             Since: 0.99.7
156
157         **********************************************************************/
158
159         public final uint convert (OutputStream output, T[] formatStr, ...)
160         {
161                 return convert (cast(Sink) &output.write, _arguments, _argptr, formatStr);
162         }
163
164         /**********************************************************************
165
166         **********************************************************************/
167
168         public final T[] convert (TypeInfo[] arguments, ArgList args, T[] formatStr)
169         {
170                 T[] output;
171
172                 uint sink (T[] s)
173                 {
174                         output ~= s;
175                         return s.length;
176                 }
177
178                 convert (&sink, arguments, args, formatStr);
179                 return output;
180         }
181
182         /**********************************************************************
183
184         **********************************************************************/
185
186         public final T[] convertOne (T[] result, TypeInfo ti, Arg arg)
187         {
188                 return dispatch (result, null, ti, arg);
189         }
190
191         /**********************************************************************
192
193         **********************************************************************/
194
195         public final uint convert (Sink sink, TypeInfo[] arguments, ArgList args, T[] formatStr)
196         {
197                 assert (formatStr, "null format specifier");
198                 assert (arguments.length < 64, "too many args in Layout.convert");
199
200                 version (GNU)
201                         {
202                         Arg[64] arglist = void;
203                         int[64] intargs = void;
204                         byte[64] byteargs = void;
205                         long[64] longargs = void;
206                         short[64] shortargs = void;
207                         void[][64] voidargs = void;
208                         real[64] realargs = void;
209                         float[64] floatargs = void;
210                         double[64] doubleargs = void;
211        
212                         foreach (i, arg; arguments)
213                                 {
214                                 static if (is(typeof(args.ptr)))
215                                     arglist[i] = args.ptr;
216                                 else
217                                     arglist[i] = args;
218                                 /* Since floating point types don't live on
219                                  * the stack, they must be accessed by the
220                                  * correct type. */
221                                 bool converted = false;
222                                 switch (arg.classinfo.name[9])
223                                        {
224                                        case TypeCode.FLOAT:
225                                             floatargs[i] = va_arg!(float)(args);
226                                             arglist[i] = &floatargs[i];
227                                             converted = true;
228                                             break;
229        
230                                        case TypeCode.DOUBLE:
231                                             doubleargs[i] = va_arg!(double)(args);
232                                             arglist[i] = &doubleargs[i];
233                                             converted = true;
234                                             break;
235        
236                                        case TypeCode.REAL:
237                                             realargs[i] = va_arg!(real)(args);
238                                             arglist[i] = &realargs[i];
239                                             converted = true;
240                                             break;
241        
242                                        default:
243                                             break;
244                                         }
245                                 if (! converted)
246                                    {
247                                    switch (arg.tsize)
248                                           {
249                                           case 1:
250                                                byteargs[i] = va_arg!(byte)(args);
251                                                arglist[i] = &byteargs[i];
252                                                break;
253                                           case 2:
254                                                shortargs[i] = va_arg!(short)(args);
255                                                arglist[i] = &shortargs[i];
256                                                break;
257                                           case 4:
258                                                intargs[i] = va_arg!(int)(args);
259                                                arglist[i] = &intargs[i];
260                                                break;
261                                           case 8:
262                                                longargs[i] = va_arg!(long)(args);
263                                                arglist[i] = &longargs[i];
264                                                break;
265                                           case 16:
266                                                voidargs[i] = va_arg!(void[])(args);
267                                                arglist[i] = &voidargs[i];
268                                                break;
269                                           default:
270                                                assert (false, "Unknown size: " ~ Integer.toString (arg.tsize));
271                                           }
272                                    }
273                                 }
274                         }
275                      else
276                         {
277                         Arg[64] arglist = void;
278                         foreach (i, arg; arguments)
279                                 {
280                                 arglist[i] = args;
281                                 args += (arg.tsize + int.sizeof - 1) & ~ (int.sizeof - 1);
282                                 }
283                         }
284                 return parse (formatStr, arguments, arglist, sink);
285         }
286
287         /**********************************************************************
288
289                 Parse the format-string, emitting formatted args and text
290                 fragments as we go.
291
292         **********************************************************************/
293
294         private uint parse (T[] layout, TypeInfo[] ti, Arg[] args, Sink sink)
295         {
296                 T[384] result = void;
297                 int length, nextIndex;
298
299
300                 T* s = layout.ptr;
301                 T* fragment = s;
302                 T* end = s + layout.length;
303
304                 while (true)
305                       {
306                       while (s < end && *s != '{')
307                              ++s;
308
309                       // emit fragment
310                       length += sink (fragment [0 .. s - fragment]);
311
312                       // all done?
313                       if (s is end)
314                           break;
315
316                       // check for "{{" and skip if so
317                       if (*++s is '{')
318                          {
319                          fragment = s++;
320                          continue;
321                          }
322
323                       int index = 0;
324                       bool indexed = false;
325
326                       // extract index
327                       while (*s >= '0' && *s <= '9')
328                             {
329                             index = index * 10 + *s++ -'0';
330                             indexed = true;
331                             }
332
333                       // skip spaces
334                       while (s < end && *s is ' ')
335                              ++s;
336
337                       bool crop;
338                       bool left;
339                       bool right;
340                       int  width;
341
342                       // has minimum or maximum width?
343                       if (*s is ',' || *s is '.')
344                          {
345                          if (*s is '.')
346                              crop = true;
347
348                          while (++s < end && *s is ' ') {}
349                          if (*s is '-')
350                             {
351                             left = true;
352                             ++s;
353                             }
354                          else
355                             right = true;
356
357                          // get width
358                          while (*s >= '0' && *s <= '9')
359                                 width = width * 10 + *s++ -'0';
360
361                          // skip spaces
362                          while (s < end && *s is ' ')
363                                 ++s;
364                          }
365
366                       T[] format;
367
368                       // has a format string?
369                       if (*s is ':' && s < end)
370                          {
371                          T* fs = ++s;
372
373                          // eat everything up to closing brace
374                          while (s < end && *s != '}')
375                                 ++s;
376                          format = fs [0 .. s - fs];
377                          }
378
379                       // insist on a closing brace
380                       if (*s != '}')
381                          {
382                          length += sink ("{malformed format}");
383                          continue;
384                          }
385
386                       // check for default index & set next default counter
387                       if (! indexed)
388                             index = nextIndex;
389                       nextIndex = index + 1;
390
391                       // next char is start of following fragment
392                       fragment = ++s;
393
394                       // handle alignment
395                       void process (T[] str)
396                       {
397                                 int padding = width - str.length;
398
399                                 if (crop)
400                                    {
401                                    if (padding < 0)
402                                       {
403                                       if (left)
404                                          {
405                                          length += sink ("...");
406                                          length += sink (Utf.cropLeft (str[-padding..$]));
407                                          }
408                                       else
409                                          {
410                                          length += sink (Utf.cropRight (str[0..width]));
411                                          length += sink ("...");
412                                          }
413                                       }
414                                    else
415                                        length += sink (str);
416                                    }
417                                 else
418                                    {
419                                    // if right aligned, pad out with spaces
420                                    if (right && padding > 0)
421                                        length += spaces (sink, padding);
422
423                                    // emit formatted argument
424                                    length += sink (str);
425
426                                    // finally, pad out on right
427                                    if (left && padding > 0)
428                                        length += spaces (sink, padding);
429                                    }
430                       }
431
432                       // an astonishing number of typehacks needed to handle arrays :(
433                       void processElement (TypeInfo _ti, Arg _arg)
434                       {
435                                 if (_ti.classinfo.name.length is 20 && _ti.classinfo.name[9..$] == "StaticArray" )
436                                    {
437                                    auto tiStat = cast(TypeInfo_StaticArray)_ti;
438                                    auto p = _arg;
439                                    length += sink ("[");
440                                    for (int i = 0; i < tiStat.len; i++)
441                                        {
442                                        if (p !is _arg )
443                                            length += sink (", ");
444                                        processElement (tiStat.value, p);
445                                        p += tiStat.tsize/tiStat.len;
446                                        }
447                                    length += sink ("]");
448                                    }
449                                 else
450                                 if (_ti.classinfo.name.length is 25 && _ti.classinfo.name[9..$] == "AssociativeArray")
451                                    {
452                                    auto tiAsso = cast(TypeInfo_AssociativeArray)_ti;
453                                    auto tiKey = tiAsso.key;
454                                    auto tiVal = tiAsso.next();
455                                    // the knowledge of the internal k/v storage is used
456                                    // so this might break if, that internal storage changes
457                                    alias ubyte AV; // any type for key, value might be ok, the sizes are corrected later
458                                    alias ubyte AK;
459                                    auto aa = *cast(AV[AK]*) _arg;
460
461                                    length += sink ("{");
462                                    bool first = true;
463                                  
464                                    int roundUp (int sz)
465                                    {
466                                         return (sz + (void*).sizeof -1) & ~((void*).sizeof - 1);
467                                    }
468
469                                    foreach (inout v; aa)
470                                            {
471                                            // the key is befor the value, so substrace with fixed key size from above
472                                            auto pk = cast(Arg)( &v - roundUp(AK.sizeof));
473                                            // now the real value pos is plus the real key size
474                                            auto pv = cast(Arg)(pk + roundUp(tiKey.tsize()));
475
476                                            if (!first)
477                                                 length += sink (", ");
478                                            processElement (tiKey, pk);
479                                            length += sink (" => ");
480                                            processElement (tiVal, pv);
481                                            first = false;
482                                            }
483                                    length += sink ("}");
484                                    }
485                                 else
486                                 if (_ti.classinfo.name[9] is TypeCode.ARRAY &&
487                                    (_ti !is typeid(char[]))  &&
488                                    (_ti !is typeid(wchar[])) &&
489                                    (_ti !is typeid(dchar[])))
490                                    {
491                                    // for all non string array types (including char[][])
492                                    auto arr = *cast(void[]*)_arg;
493                                    auto len = arr.length;
494                                    auto ptr = cast(Arg) arr.ptr;
495                                    auto elTi = _ti.next();
496                                    auto size = elTi.tsize();
497                                    length += sink ("[");
498                                    while (len > 0)
499                                          {
500                                          if (ptr !is arr.ptr)
501                                              length += sink (", ");
502                                          processElement (elTi, ptr);
503                                          len -= 1;
504                                          ptr += size;
505                                          }
506                                    length += sink ("]");
507   &