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