00001 /******************************************************************************* 00002 00003 @file ColumnWriter.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, March 2004 00034 @author Kris 00035 00036 00037 *******************************************************************************/ 00038 00039 module mango.io.ColumnWriter; 00040 00041 private import mango.io.Exception; 00042 00043 private import mango.convert.Type; 00044 00045 public import mango.io.DisplayWriter; 00046 00047 /******************************************************************************* 00048 00049 Print readable output to an IWriter, distributed across a set of 00050 columns. Columns start at zero rather than one, so adjust counts 00051 appropriately. All output is currently left-aligned. 00052 00053 This is how one might use ColumnWriter: 00054 00055 @code 00056 static int[] columns = [0, 4, 14, 24]; 00057 00058 // map a ColumnWriter directly onto Stdout 00059 ColumnWriter write = new ColumnWriter (Stdout.getBuffer(), new ColumnList(columns)); 00060 00061 // set 2 digits of precision 00062 write.setPrecision (2); 00063 00064 // Mango style 00065 write (1) (20.34944) ("test") (CR); 00066 00067 // iostream style 00068 write << 1 << 20.34944 << "test" << CR; 00069 00070 // put() style 00071 write.put(1).put(20.34944).put("test").cr(); 00072 @endcode 00073 00074 Note that ColumnWriter may be used with files, sockets, or any other 00075 buffer-oriented construct. 00076 00077 *******************************************************************************/ 00078 00079 class ColumnWriter : DisplayWriter 00080 { 00081 alias DisplayWriter.put put; 00082 00083 private int output; 00084 private ColumnList columns; 00085 private static ubyte spaces[256]; 00086 00087 /*********************************************************************** 00088 00089 Construct a ColumnWriter upon the given IBuffer, with the 00090 specified set of columns. 00091 00092 ***********************************************************************/ 00093 00094 this (IBuffer buffer, ColumnList columns) 00095 in { 00096 assert (buffer); 00097 assert (columns); 00098 } 00099 body 00100 { 00101 super (buffer); 00102 00103 this.columns = columns; 00104 reset (); 00105 } 00106 00107 /*********************************************************************** 00108 00109 Populate the space padding with valid spaces. 00110 00111 ***********************************************************************/ 00112 00113 static this () 00114 { 00115 spaces[] = ' '; 00116 } 00117 00118 /*********************************************************************** 00119 00120 Reset everything back to zero. Typical usage will invoke 00121 this whenever a Newline is emitted. 00122 00123 Note that we maintain our own internal count of how many 00124 bytes have been output: this is because we cannot depend 00125 on the Buffer to provide that for us if (a) the buffer is 00126 very small, or (b) the buffer is flushed after each write 00127 (Stdout etc). 00128 00129 ***********************************************************************/ 00130 00131 void reset () 00132 { 00133 output = 0; 00134 columns.reset (); 00135 } 00136 /+ 00137 /*********************************************************************** 00138 00139 Return the width of the current column. 00140 00141 ***********************************************************************/ 00142 00143 override int getWidth () 00144 { 00145 return columns.getWidth; 00146 } 00147 +/ 00148 /*********************************************************************** 00149 00150 Intercept the IWritable type so we can reset our columns 00151 when a newline is emitted. 00152 00153 ***********************************************************************/ 00154 00155 override IWriter put (IWritable x) 00156 { 00157 // have superclass print the IWritable 00158 super.put (x); 00159 00160 // reset columns on a newline 00161 if (cast (INewlineWriter) x) 00162 reset (); 00163 00164 return this; 00165 } 00166 00167 /*********************************************************************** 00168 00169 Intercept the output so we can write some spaces first. 00170 Note that our superclass (DisplayWriter) converts each 00171 of its arguments to a char[] first, so this override is 00172 able to catch everything emitted. 00173 00174 @todo - add the equivalent intercepts for both wchar[] 00175 and dchar[] methods. 00176 00177 ***********************************************************************/ 00178 00179 protected override IWriter encode (void* src, uint bytes, int type) 00180 { 00181 pad(); 00182 /+ 00183 // encode the string 00184 void[] x = buffer.getCodec.from (src[0..bytes], type); 00185 00186 // adjust output with the encoded length 00187 int t = buffer.type(); 00188 output += (x.length / ((t is Type.Raw) ? 1 : Type.widths[t])); 00189 00190 // append encoded string 00191 buffer.append (x); 00192 return this; 00193 +/ 00194 output += bytes; 00195 return super.encode (src, bytes, type); 00196 } 00197 00198 /*********************************************************************** 00199 00200 Pad the output with spaces to reach the next column 00201 position. This should be invoked before anything is 00202 written to the buffer. 00203 00204 ***********************************************************************/ 00205 00206 private final void pad () 00207 { 00208 int padding = columns.next() - output; 00209 00210 // pad output to next column position? 00211 if (padding > 0) 00212 if (padding <= spaces.sizeof) 00213 { 00214 // yep - write a set of spaces 00215 super.encode (spaces, padding, Type.Utf8); 00216 output += padding; 00217 } 00218 else 00219 throw new IOException ("Invalid column step (> 256)"); 00220 } 00221 } 00222 00223 00224 /******************************************************************************* 00225 00226 A list of columns for the ColumnWriter to utilize. 00227 00228 *******************************************************************************/ 00229 00230 class ColumnList 00231 { 00232 private int index; 00233 private int[] columns; 00234 00235 private bool rightAlign; // this needs to be per column instead 00236 00237 /*********************************************************************** 00238 00239 Construct a ColumnList via an array of integers. 00240 00241 ***********************************************************************/ 00242 00243 this (int[] columns) 00244 in { 00245 assert (columns); 00246 } 00247 body 00248 { 00249 reset (); 00250 this.columns = columns; 00251 } 00252 00253 /*********************************************************************** 00254 00255 Start returning columns from the beginning. 00256 00257 ***********************************************************************/ 00258 00259 void reset () 00260 { 00261 index = 0; 00262 } 00263 00264 /*********************************************************************** 00265 00266 Return width of the current column 00267 00268 ***********************************************************************/ 00269 00270 int getWidth () 00271 { 00272 if (rightAlign) 00273 { 00274 int i = index; 00275 00276 if (i == 0) 00277 ++i; 00278 00279 if (i < columns.length) 00280 return columns[i] - columns[i-1]; 00281 } 00282 return 0; 00283 } 00284 00285 /*********************************************************************** 00286 00287 Returns next column in the sequence. Assume that we'll be 00288 invoked (quasi-legally) when there's no more columns left. 00289 00290 ***********************************************************************/ 00291 00292 int next () 00293 { 00294 if (index < columns.length) 00295 ++index; 00296 00297 return columns [index-1]; 00298 } 00299 } 00300