001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.util.table; 028 029 030 031 import java.util.ArrayList; 032 import java.util.Collections; 033 import java.util.Comparator; 034 import java.util.List; 035 036 import org.opends.messages.Message; 037 038 039 040 /** 041 * A class which can be used to construct tables of information to be 042 * displayed in a terminal. Once built the table can be output using a 043 * {@link TableSerializer}. 044 */ 045 public final class TableBuilder { 046 047 // The current column number in the current row where 0 represents 048 // the left-most column in the table. 049 private int column = 0; 050 051 // The current with of each column. 052 private List<Integer> columnWidths = new ArrayList<Integer>(); 053 054 // The list of column headings. 055 private List<Message> header = new ArrayList<Message>(); 056 057 // The current number of rows in the table. 058 private int height = 0; 059 060 // The list of table rows. 061 private List<List<String>> rows = new ArrayList<List<String>>(); 062 063 // The linked list of sort keys comparators. 064 private List<Comparator<String>> sortComparators = 065 new ArrayList<Comparator<String>>(); 066 067 // The linked list of sort keys. 068 private List<Integer> sortKeys = new ArrayList<Integer>(); 069 070 // The current number of columns in the table. 071 private int width = 0; 072 073 074 075 /** 076 * Creates a new table printer. 077 */ 078 public TableBuilder() { 079 // No implementation required. 080 } 081 082 083 084 /** 085 * Adds a table sort key. The table will be sorted according to the 086 * case-insensitive string ordering of the cells in the specified 087 * column. 088 * 089 * @param column 090 * The column which will be used as a sort key. 091 */ 092 public void addSortKey(int column) { 093 addSortKey(column, String.CASE_INSENSITIVE_ORDER); 094 } 095 096 097 098 /** 099 * Adds a table sort key. The table will be sorted according to the 100 * provided string comparator. 101 * 102 * @param column 103 * The column which will be used as a sort key. 104 * @param comparator 105 * The string comparator. 106 */ 107 public void addSortKey(int column, Comparator<String> comparator) { 108 sortKeys.add(column); 109 sortComparators.add(comparator); 110 } 111 112 113 114 /** 115 * Appends a new blank cell to the current row. 116 */ 117 public void appendCell() { 118 appendCell(""); 119 } 120 121 122 123 /** 124 * Appends a new cell to the current row containing the provided 125 * boolean value. 126 * 127 * @param value 128 * The boolean value. 129 */ 130 public void appendCell(boolean value) { 131 appendCell(String.valueOf(value)); 132 } 133 134 135 136 /** 137 * Appends a new cell to the current row containing the provided 138 * byte value. 139 * 140 * @param value 141 * The byte value. 142 */ 143 public void appendCell(byte value) { 144 appendCell(String.valueOf(value)); 145 } 146 147 148 149 /** 150 * Appends a new cell to the current row containing the provided 151 * char value. 152 * 153 * @param value 154 * The char value. 155 */ 156 public void appendCell(char value) { 157 appendCell(String.valueOf(value)); 158 } 159 160 161 162 /** 163 * Appends a new cell to the current row containing the provided 164 * double value. 165 * 166 * @param value 167 * The double value. 168 */ 169 public void appendCell(double value) { 170 appendCell(String.valueOf(value)); 171 } 172 173 174 175 /** 176 * Appends a new cell to the current row containing the provided 177 * float value. 178 * 179 * @param value 180 * The float value. 181 */ 182 public void appendCell(float value) { 183 appendCell(String.valueOf(value)); 184 } 185 186 187 188 /** 189 * Appends a new cell to the current row containing the provided 190 * integer value. 191 * 192 * @param value 193 * The boolean value. 194 */ 195 public void appendCell(int value) { 196 appendCell(String.valueOf(value)); 197 } 198 199 200 201 /** 202 * Appends a new cell to the current row containing the provided 203 * long value. 204 * 205 * @param value 206 * The long value. 207 */ 208 public void appendCell(long value) { 209 appendCell(String.valueOf(value)); 210 } 211 212 213 214 /** 215 * Appends a new cell to the current row containing the provided 216 * object value. 217 * 218 * @param value 219 * The object value. 220 */ 221 public void appendCell(Object value) { 222 // Make sure that the first row has been created. 223 if (height == 0) { 224 startRow(); 225 } 226 227 // Create the cell. 228 String s = String.valueOf(value); 229 rows.get(height - 1).add(s); 230 column++; 231 232 // Update statistics. 233 if (column > width) { 234 width = column; 235 columnWidths.add(s.length()); 236 } else if (columnWidths.get(column - 1) < s.length()) { 237 columnWidths.set(column - 1, s.length()); 238 } 239 } 240 241 242 243 /** 244 * Appends a new blank column heading to the header row. 245 */ 246 public void appendHeading() { 247 appendHeading(Message.EMPTY); 248 } 249 250 251 252 /** 253 * Appends a new column heading to the header row. 254 * 255 * @param value 256 * The column heading value. 257 */ 258 public void appendHeading(Message value) { 259 header.add(value); 260 261 // Update statistics. 262 if (header.size() > width) { 263 width = header.size(); 264 columnWidths.add(value.length()); 265 } else if (columnWidths.get(header.size() - 1) < value.length()) { 266 columnWidths.set(header.size() - 1, value.length()); 267 } 268 } 269 270 271 272 /** 273 * Gets the width of the current row. 274 * 275 * @return Returns the width of the current row. 276 */ 277 public int getRowWidth() { 278 return column; 279 } 280 281 282 283 /** 284 * Gets the number of rows in table. 285 * 286 * @return Returns the number of rows in table. 287 */ 288 public int getTableHeight() { 289 return height; 290 } 291 292 293 294 /** 295 * Gets the number of columns in table. 296 * 297 * @return Returns the number of columns in table. 298 */ 299 public int getTableWidth() { 300 return width; 301 } 302 303 304 305 /** 306 * Prints the table in its current state using the provided table 307 * printer. 308 * 309 * @param printer 310 * The table printer. 311 */ 312 public void print(TablePrinter printer) { 313 // Create a new printer instance. 314 TableSerializer serializer = printer.getSerializer(); 315 316 // First sort the table. 317 List<List<String>> sortedRows = new ArrayList<List<String>>(rows); 318 319 Comparator<List<String>> comparator = new Comparator<List<String>>() { 320 321 public int compare(List<String> row1, List<String> row2) { 322 for (int i = 0; i < sortKeys.size(); i++) { 323 String cell1 = row1.get(sortKeys.get(i)); 324 String cell2 = row2.get(sortKeys.get(i)); 325 326 int rc = sortComparators.get(i).compare(cell1, cell2); 327 if (rc != 0) { 328 return rc; 329 } 330 } 331 332 // Both rows are equal. 333 return 0; 334 } 335 336 }; 337 338 Collections.sort(sortedRows, comparator); 339 340 // Now output the table. 341 serializer.startTable(height, width); 342 for (int i = 0; i < width; i++) { 343 serializer.addColumn(columnWidths.get(i)); 344 } 345 346 // Column headings. 347 serializer.startHeader(); 348 for (Message s : header) { 349 serializer.addHeading(s.toString()); 350 } 351 serializer.endHeader(); 352 353 // Table contents. 354 serializer.startContent(); 355 for (List<String> row : sortedRows) { 356 serializer.startRow(); 357 358 // Print each cell in the row, padding missing trailing cells. 359 for (int i = 0; i < width; i++) { 360 if (i < row.size()) { 361 serializer.addCell(row.get(i)); 362 } else { 363 serializer.addCell(""); 364 } 365 } 366 367 serializer.endRow(); 368 } 369 serializer.endContent(); 370 serializer.endTable(); 371 } 372 373 374 375 /** 376 * Appends a new row to the table. 377 */ 378 public void startRow() { 379 rows.add(new ArrayList<String>()); 380 height++; 381 column = 0; 382 } 383 }