00001 /******************************************************************************* 00002 00003 @file AbstractWriter.d 00004 00005 00006 Copyright (c) 2004 Kris Bell 00007 00008 This software is provided 'as-is', without any express or implied 00009 warranty. In no event will the authors be held liable for damages 00010 of any kind arising from the use of this software. 00011 00012 Permission is hereby granted to anyone to use this software for any 00013 purpose, including commercial applications, and to alter it and/or 00014 redistribute it freely, subject to the following restrictions: 00015 00016 1. The origin of this software must not be misrepresented; you must 00017 not claim that you wrote the original software. If you use this 00018 software in a product, an acknowledgment within documentation of 00019 said product would be appreciated but is not required. 00020 00021 2. Altered source versions must be plainly marked as such, and must 00022 not be misrepresented as being the original software. 00023 00024 3. This notice may not be removed or altered from any distribution 00025 of the source. 00026 00027 4. Derivative works are permitted, but they must carry this notice 00028 in full and credit the original source. 00029 00030 00031 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00032 00033 00034 @version Initial version, October 2004 00035 00036 @author Kris 00037 00038 *******************************************************************************/ 00039 00040 module mango.io.Writer; 00041 00042 private import mango.convert.Type; 00043 00044 private import mango.io.Exception; 00045 00046 public import mango.io.Buffer; 00047 00048 public import mango.io.model.IWriter, 00049 mango.io.model.IBuffer, 00050 mango.io.model.IConduit; 00051 00052 /******************************************************************************* 00053 00054 Writer base-class. Writers provide the means to append formatted 00055 data to an IBuffer, and expose a convenient method of handling a 00056 variety of data types. In addition to writing native types such 00057 as integer and char[], writers also process any class which has 00058 implemented the IWritable interface (one method). 00059 00060 All writers support the full set of native data types, plus their 00061 fundamental array variants. Operations may be chained back-to-back. 00062 00063 Writers support a C++ iostream type syntax, along with Java-esque 00064 put() notation. However, the Mango style is to place IO elements 00065 within their own parenthesis, like so: 00066 00067 write (count) (" green bottles"); 00068 00069 Note that each element is distict; this enables "strong typing", 00070 which should catch any typo errors at compile-time. The style is 00071 affectionately called "whisper". 00072 00073 The code below illustrates basic operation upon a memory buffer: 00074 00075 @code 00076 Buffer buf = new Buffer (256); 00077 00078 // map same buffer into both reader and writer 00079 IReader read = new Reader(buf); 00080 IWriter write = new Writer(buf); 00081 00082 int i = 10; 00083 long j = 20; 00084 double d = 3.14159; 00085 char[] c = "fred"; 00086 00087 // write data types out 00088 write (c) (i) (j) (d); 00089 00090 // read them back again 00091 read (c) (i) (j) (d); 00092 00093 // reset 00094 buf.clear(); 00095 00096 // same thing again, but using iostream syntax instead 00097 write << c << i << j << d; 00098 00099 // read them back again 00100 read >> c >> i >> j >> d; 00101 00102 // reset 00103 buf.clear(); 00104 00105 // same thing again, but using put() syntax instead 00106 write.put(c).put(i).put(j).put(d); 00107 read.get(c).get(i).get(j).get(d); 00108 00109 @endcode 00110 00111 Writers may also be used with any class implementing the IWritable 00112 interface. See PickleReader for an example of how this can be put 00113 to good use. 00114 00115 Note that 'newlines' are emitted via the standard "\n" approach. 00116 However, one might consider using the public CR element instead. 00117 00118 Writers also support formatted output via the DisplayWriter module, 00119 which has full support for printf() syntax: 00120 00121 @code 00122 Stdout.println ("%d green bottles", 10); 00123 @endcode 00124 00125 Lastly, each Writer may be configured with a text encoder. These 00126 encoders convert between an internal text representation, and the 00127 char/wchar/dchar representaion. BufferCodec.d contains classes for 00128 handling utf8, utf16, and utf32. The icu.UMango module has support 00129 for a wide variety of converters. Stdout is pre-configured with 00130 utf16 & utf8 encoders for Win32 and Posix respectively. 00131 00132 *******************************************************************************/ 00133 00134 class Writer : IWriter 00135 { 00136 protected IBuffer buffer; 00137 private bool prefixArray; 00138 private IBuffer.Converter textEncoder; 00139 00140 /*********************************************************************** 00141 00142 Construct a Writer upon the provided IBuffer. All formatted 00143 output will be directed to this buffer. 00144 00145 ***********************************************************************/ 00146 00147 this (IBuffer buffer) 00148 { 00149 this.buffer = buffer; 00150 00151 Buffer.Style s = buffer.getStyle; 00152 if (s != Buffer.Mixed) 00153 if ((s == Buffer.Text) ^ isTextBased()) 00154 error ("text/binary mismatch between Writer and Buffer"); 00155 prefixArray = cast(bool) !isTextBased; 00156 } 00157 00158 /*********************************************************************** 00159 00160 Construct a Writer on the buffer associated with the given 00161 conduit. 00162 00163 ***********************************************************************/ 00164 00165 this (IConduit conduit) 00166 { 00167 this (new Buffer(conduit)); 00168 } 00169 00170 /*********************************************************************** 00171 00172 ***********************************************************************/ 00173 00174 final void error (char[] msg) 00175 { 00176 buffer.error (msg); 00177 } 00178 00179 /*********************************************************************** 00180 00181 Return the associated buffer 00182 00183 ***********************************************************************/ 00184 00185 final IBuffer getBuffer () 00186 { 00187 return buffer; 00188 } 00189 00190 /*********************************************************************** 00191 00192 ***********************************************************************/ 00193 00194 final IConduit conduit () 00195 { 00196 return buffer.getConduit(); 00197 } 00198 00199 /*********************************************************************** 00200 00201 Bind an IEncoder to the writer. Encoders are intended to 00202 be used as a conversion mechanism between various character 00203 representations (encodings). Each writer may be configured 00204 with a distinct encoder. 00205 00206 ***********************************************************************/ 00207 00208 final void setEncoder (AbstractEncoder e) 00209 { 00210 e.bind (buffer); 00211 this.textEncoder = &e.encoder; 00212 } 00213 00214 /*********************************************************************** 00215 00216 Is this Writer text oriented? 00217 00218 ***********************************************************************/ 00219 00220 bool isTextBased() 00221 { 00222 return false; 00223 } 00224 00225 /*********************************************************************** 00226 00227 Flush the output of this writer. Returns false if the 00228 operation failed, true otherwise. 00229 00230 ***********************************************************************/ 00231 00232 IWriter flush () 00233 { 00234 buffer.flush (); 00235 return this; 00236 } 00237 00238 /*********************************************************************** 00239 00240 Output a newline. Do this indirectly so that it can be 00241 intercepted by subclasses. 00242 00243 ***********************************************************************/ 00244 00245 IWriter cr () 00246 { 00247 return put (CR); 00248 } 00249 00250 /*********************************************************************** 00251 00252 Flush this writer. This is a convenience method used by 00253 the "whisper" syntax. 00254 00255 ***********************************************************************/ 00256 00257 IWriter put () 00258 { 00259 return flush (); 00260 } 00261 00262 /*********************************************************************** 00263 00264 Write a class to the current buffer-position 00265 00266 ***********************************************************************/ 00267 00268 IWriter put (IWritable x) 00269 { 00270 assert (x); 00271 x.write (this); 00272 return this; 00273 } 00274 00275 /*********************************************************************** 00276 00277 Write a boolean value to the current buffer-position 00278 00279 ***********************************************************************/ 00280 00281 IWriter put (bool x) 00282 { 00283 return write (&x, x.sizeof, Type.Bool); 00284 } 00285 00286 /*********************************************************************** 00287 00288 Write an unsigned byte value to the current buffer-position 00289 00290 ***********************************************************************/ 00291 00292 IWriter put (ubyte x) 00293 { 00294 return write (&x, x.sizeof, Type.UByte); 00295 } 00296 00297 /*********************************************************************** 00298 00299 Write a byte value to the current buffer-position 00300 00301 ***********************************************************************/ 00302 00303 IWriter put (byte x) 00304 { 00305 return write (&x, x.sizeof, Type.Byte); 00306 } 00307 00308 /*********************************************************************** 00309 00310 Write an unsigned short value to the current buffer-position 00311 00312 ***********************************************************************/ 00313 00314 IWriter put (ushort x) 00315 { 00316 return write (&x, x.sizeof, Type.UShort); 00317 } 00318 00319 /*********************************************************************** 00320 00321 Write a short value to the current buffer-position 00322 00323 ***********************************************************************/ 00324 00325 IWriter put (short x) 00326 { 00327 return write (&x, x.sizeof, Type.Short); 00328 } 00329 00330 /*********************************************************************** 00331 00332 Write a unsigned int value to the current buffer-position 00333 00334 ***********************************************************************/ 00335 00336 IWriter put (uint x) 00337 { 00338 return write (&x, x.sizeof, Type.UInt); 00339 } 00340 00341 /*********************************************************************** 00342 00343 Write an int value to the current buffer-position 00344 00345 ***********************************************************************/ 00346 00347 IWriter put (int x) 00348 { 00349 return write (&x, x.sizeof, Type.Int); 00350 } 00351 00352 /*********************************************************************** 00353 00354 Write an unsigned long value to the current buffer-position 00355 00356 ***********************************************************************/ 00357 00358 IWriter put (ulong x) 00359 { 00360 return write (&x, x.sizeof, Type.ULong); 00361 } 00362 00363 /*********************************************************************** 00364 00365 Write a long value to the current buffer-position 00366 00367 ***********************************************************************/ 00368 00369 IWriter put (long x) 00370 { 00371 return write (&x, x.sizeof, Type.Long); 00372 } 00373 00374 /*********************************************************************** 00375 00376 Write a float value to the current buffer-position 00377 00378 ***********************************************************************/ 00379 00380 IWriter put (float x) 00381 { 00382 return write (&x, x.sizeof, Type.Float); 00383 } 00384 00385 /*********************************************************************** 00386 00387 Write a double value to the current buffer-position 00388 00389 ***********************************************************************/ 00390 00391 IWriter put (double x) 00392 { 00393 return write (&x, x.sizeof, Type.Double); 00394 } 00395 00396 /*********************************************************************** 00397 00398 Write a real value to the current buffer-position 00399 00400 ***********************************************************************/ 00401 00402 IWriter put (real x) 00403 { 00404 return write (&x, x.sizeof, Type.Real); 00405 } 00406 00407 /*********************************************************************** 00408 00409 Write a char value to the current buffer-position 00410 00411 ***********************************************************************/ 00412 00413 IWriter put (char x) 00414 { 00415 return encode (&x, char.sizeof, Type.Utf8); 00416 } 00417 00418 /*********************************************************************** 00419 00420 Write a wchar value to the current buffer-position 00421 00422 ***********************************************************************/ 00423 00424 IWriter put (wchar x) 00425 { 00426 return encode (&x, wchar.sizeof, Type.Utf16); 00427 } 00428 00429 /*********************************************************************** 00430 00431 Write a dchar value to the current buffer-position 00432 00433 ***********************************************************************/ 00434 00435 IWriter put (dchar x) 00436 { 00437 return encode (&x, dchar.sizeof, Type.Utf32); 00438 } 00439 00440 /*********************************************************************** 00441 00442 Write a byte array to the current buffer-position 00443 00444 ***********************************************************************/ 00445 00446 IWriter put (byte[] x) 00447 { 00448 return write (x, length (x.length) * byte.sizeof, Type.Byte); 00449 } 00450 00451 /*********************************************************************** 00452 00453 Write an unsigned byte array to the current buffer-position 00454 00455 ***********************************************************************/ 00456 00457 IWriter put (ubyte[] x) 00458 { 00459 return write (x, length (x.length) * ubyte.sizeof, Type.UByte); 00460 } 00461 00462 /*********************************************************************** 00463 00464 Write a short array to the current buffer-position 00465 00466 ***********************************************************************/ 00467 00468 IWriter put (short[] x) 00469 { 00470 return write (x, length (x.length) * short.sizeof, Type.Short); 00471 } 00472 00473 /*********************************************************************** 00474 00475 Write an unsigned short array to the current buffer-position 00476 00477 ***********************************************************************/ 00478 00479 IWriter put (ushort[] x) 00480 { 00481 return write (x, length (x.length) * ushort.sizeof, Type.UShort); 00482 } 00483 00484 /*********************************************************************** 00485 00486 Write an int array to the current buffer-position 00487 00488 ***********************************************************************/ 00489 00490 IWriter put (int[] x) 00491 { 00492 return write (x, length (x.length) * int.sizeof, Type.Int); 00493 } 00494 00495 /*********************************************************************** 00496 00497 Write an unsigned int array to the current buffer-position 00498 00499 ***********************************************************************/ 00500 00501 IWriter put (uint[] x) 00502 { 00503 return write (x, length (x.length) * uint.sizeof, Type.UInt); 00504 } 00505 00506 /*********************************************************************** 00507 00508 Write a long array to the current buffer-position 00509 00510 ***********************************************************************/ 00511 00512 IWriter put (long[] x) 00513 { 00514 return write (x, length (x.length) * long.sizeof, Type.Long); 00515 } 00516 00517 /*********************************************************************** 00518 00519 Write an unsigned long array to the current buffer-position 00520 00521 ***********************************************************************/ 00522 00523 IWriter put (ulong[] x) 00524 { 00525 return write (x, length (x.length) * ulong.sizeof, Type.ULong); 00526 } 00527 00528 /*********************************************************************** 00529 00530 Write a float array to the current buffer-position 00531 00532 ***********************************************************************/ 00533 00534 IWriter put (float[] x) 00535 { 00536 return write (x, length (x.length) * float.sizeof, Type.Float); 00537 } 00538 00539 /*********************************************************************** 00540 00541 Write a double array to the current buffer-position 00542 00543 ***********************************************************************/ 00544 00545 IWriter put (double[] x) 00546 { 00547 return write (x, length (x.length) * double.sizeof, Type.Double); 00548 } 00549 00550 /*********************************************************************** 00551 00552 Write a real array to the current buffer-position 00553 00554 ***********************************************************************/ 00555 00556 IWriter put (real[] x) 00557 { 00558 return write (x, length (x.length) * real.sizeof, Type.Real); 00559 } 00560 00561 /*********************************************************************** 00562 00563 Write a char array to the current buffer-position 00564 00565 ***********************************************************************/ 00566 00567 IWriter put (char[] x) 00568 { 00569 return encode (x.ptr, length(x.length) * char.sizeof, Type.Utf8); 00570 } 00571 00572 /*********************************************************************** 00573 00574 Write a wchar array to the current buffer-position 00575 00576 ***********************************************************************/ 00577 00578 IWriter put (wchar[] x) 00579 { 00580 return encode (x.ptr, length(x.length) * wchar.sizeof, Type.Utf16); 00581 } 00582 00583 /*********************************************************************** 00584 00585 Write a dchar array to the current buffer-position 00586 00587 ***********************************************************************/ 00588 00589 IWriter put (dchar[] x) 00590 { 00591 return encode (x.ptr, length(x.length) * dchar.sizeof, Type.Utf32); 00592 } 00593 00594 /*********************************************************************** 00595 00596 Dump content into the buffer. This is intercepted by a 00597 variety of subclasses 00598 00599 ***********************************************************************/ 00600 00601 protected IWriter write (void* src, uint bytes, int type) 00602 { 00603 buffer.append (src [0 .. bytes]); 00604 return this; 00605 } 00606 00607 /*********************************************************************** 00608 00609 Handle text output. This is intended to be intercepted 00610 by subclasses, though they should always pump content 00611 through here to take advantage of configured encoding 00612 00613 ***********************************************************************/ 00614 00615 protected IWriter encode (void* src, uint bytes, int type) 00616 { 00617 if (textEncoder) 00618 textEncoder (src, bytes, type); 00619 else 00620 buffer.append (src [0 .. bytes]); 00621 return this; 00622 } 00623 00624 /*********************************************************************** 00625 00626 Emit the length of an array: used for raw binary output 00627 of arrays. Array lengths are written into the buffer as 00628 a guide for when reading it back again 00629 00630 ***********************************************************************/ 00631 00632 private final uint length (uint len) 00633 { 00634 if (prefixArray) 00635 put (len); 00636 return len; 00637 } 00638 } 00639 00640 00641 /******************************************************************************* 00642 00643 A class to handle newline output. One might reasonably expect to 00644 emit a char[] for newlines; FileConst.NewlineString for example. 00645 Turns out that it's much more efficient to intercept line-breaks 00646 when they're implemented in a more formal manner (such as this). 00647 00648 For example, ColumnWriter() and TextWriter() both must intercept 00649 newline output so they can adjust formatting appropriately. It is 00650 much more efficient for such writers to intercept the IWritable 00651 put() method instead of scanning each char[] for the various \\n 00652 combinations. 00653 00654 Please use the INewlineWriter interface for emitting newlines. 00655 00656 *******************************************************************************/ 00657 00658 private import mango.io.FileConst; 00659 00660 class NewlineWriter : INewlineWriter 00661 { 00662 private char[] fmt; 00663 00664 /*********************************************************************** 00665 00666 Construct a default newline, using the char[] defined 00667 by FileConst.NewlineString 00668 00669 ***********************************************************************/ 00670 00671 this () 00672 { 00673 this (FileConst.NewlineString); 00674 } 00675 00676 /*********************************************************************** 00677 00678 Construct a newline using the provided character array 00679 00680 ***********************************************************************/ 00681 00682 this (char[] fmt) 00683 { 00684 this.fmt = fmt; 00685 } 00686 00687 /*********************************************************************** 00688 00689 Write this newline through the provided writer. This makes 00690 NewlineWriter IWritable compatible. 00691 00692 ***********************************************************************/ 00693 00694 void write (IWriter w) 00695 { 00696 w.put (fmt); 00697 } 00698 } 00699 00700 00701 /******************************************************************************* 00702 00703 public newline adaptor 00704 00705 *******************************************************************************/ 00706 00707 public static NewlineWriter CR; 00708 00709 static this () 00710 { 00711 CR = new NewlineWriter; 00712 } 00713