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