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