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    }