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