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 }