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