View Javadoc

1   /**
2    * Copyright (c) 2012, University of Konstanz, Distributed Systems Group
3    * All rights reserved.
4    * 
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions are met:
7    * * Redistributions of source code must retain the above copyright
8    * notice, this list of conditions and the following disclaimer.
9    * * Redistributions in binary form must reproduce the above copyright
10   * notice, this list of conditions and the following disclaimer in the
11   * documentation and/or other materials provided with the distribution.
12   * * Neither the name of the University of Konstanz nor the
13   * names of its contributors may be used to endorse or promote products
14   * derived from this software without specific prior written permission.
15   * 
16   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19   * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20   * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   */
27  package org.perfidix.ouput.asciitable;
28  
29  import java.util.ArrayList;
30  import java.util.List;
31  
32  import org.perfidix.ouput.asciitable.AbstractTabularComponent.Alignment;
33  
34  /**
35   * This class represents a table which allows formatting of data as an ascii
36   * table. it takes care of automatically adjusting widths of the tables. A word
37   * on the orientations: at the current point of development, the orientation
38   * works on COLUMNS and NOT on ROWs. so you can set the orientation of a full
39   * ROW, but you cannot set the orientation for each field[x][y] in the table.
40   * 
41   * @author Alexander Onea, neue Couch
42   * @author Sebastian Graf, University of Konstanz
43   */
44  public final class NiceTable {
45  
46      /**
47       * Container for all rows.
48       */
49      private transient final List<AbstractTabularComponent> rows;
50  
51      /**
52       * Storing the length of chars in the different columns.
53       */
54      private transient final int[] columnLengths;
55  
56      /**
57       * An array holding the orientations of columns.
58       */
59      private transient final Alignment[] orientations;
60  
61      /**
62       * Constructor. needs the number of columns to show.
63       * 
64       * @param numberOfColumns
65       *            the number of columns to display.
66       */
67      public NiceTable(final int numberOfColumns) {
68  
69          columnLengths = new int[numberOfColumns];
70          orientations = new Alignment[numberOfColumns];
71          rows = new ArrayList<AbstractTabularComponent>();
72      }
73  
74      /**
75       * Adds a header.
76       * 
77       * @param title
78       *            the text to display within the header
79       */
80      public void addHeader(final String title) {
81          addHeader(title, '=', Alignment.Left);
82      }
83  
84      /**
85       * Adds a header row to the table. can be used to give a table some title.
86       * 
87       * <pre>
88       *  addHeader(&quot;hello&quot;,'.',NiceTable.LEFT)
89       *  would produce a row like
90       *  ... hello ..........................
91       *   {rows in here.}
92       * </pre>
93       * 
94       * @param title
95       *            the string to display as a header
96       * @param mark
97       *            the mark to use for the rest of the column
98       * @param orientation
99       *            the orientation of the header column.
100      */
101     public void addHeader(final String title, final char mark, final Alignment orientation) {
102         final Header header = new Header(title, mark, orientation, this);
103         rows.add(header);
104     }
105 
106     /**
107      * Adds a string row. checks that the strings added do not contain newlines,
108      * because if so, it has to split them in order to make them fit the row.
109      * 
110      * @param data
111      *            the array of data.
112      */
113     public void addRow(final String[] data) {
114         if (anyStringContainsNewLine(data)) {
115             final String[][] theMatrix = Util.createMatrix(data);
116             for (int i = 0; i < theMatrix.length; i++) {
117                 addRow(theMatrix[i]);
118             }
119         } else {
120             final Row myRow = new Row(this, data);
121             rows.add(myRow);
122         }
123 
124     }
125 
126     /**
127      * allows the addition of lines. think of it as a horizontal rule in a
128      * table.
129      * 
130      * @param fill
131      *            any character with which to draw the line.
132      */
133     public void addLine(final char fill) {
134         rows.add(new DynamicLine(fill, this));
135     }
136 
137     /**
138      * the main method doing the work. draws everyting.
139      * 
140      * @return the formatted table.
141      */
142     @Override
143     public String toString() {
144 
145         final StringBuilder builder = new StringBuilder();
146 
147         for (int i = 0; i < rows.size(); i++) {
148             final AbstractTabularComponent myObj = rows.get(i);
149             builder.append(myObj.draw());
150         }
151         return builder.toString();
152     }
153 
154     /**
155      * Returns the global column width at index columnIndex.
156      * 
157      * @param columnIndex
158      *            the index of the column for which to fetch the width.
159      * @return the width (in number of chars) for the column index.
160      */
161     protected int getColumnWidth(final int columnIndex) {
162 
163         return columnLengths[columnIndex];
164     }
165 
166     /**
167      * Performs an update on the column lengths.
168      * 
169      * @param index
170      *            the index of the column
171      * @param newSize
172      *            the new size of the column
173      */
174     protected void updateColumnWidth(final int index, final int newSize) {
175         columnLengths[index] = Math.max(columnLengths[index], newSize);
176     }
177 
178     /**
179      * Returns the actual number of columns this table has got.
180      * 
181      * @return the number of columns the table may contain.
182      */
183     private int numColumns() {
184         return this.columnLengths.length;
185     }
186 
187     /**
188      * Returns the orientation of a column.
189      * 
190      * @param columnIndex
191      *            integer
192      * @return Alignment for the column
193      */
194     protected Alignment getOrientation(final int columnIndex) {
195 
196         return orientations[columnIndex];
197     }
198 
199     /**
200      * Returns the row width.
201      * 
202      * @return int the width of the row
203      */
204     private int getRowWidth() {
205         int returnVal = 1;
206         if (rows.size() < 1) {
207             returnVal = 0;
208         }
209         for (int i = 0; i < rows.size(); i++) {
210             if (rows.get(i) instanceof Row) {
211                 returnVal = ((Row)rows.get(i)).getRowWidth();
212             }
213         }
214         return returnVal;
215     }
216 
217     /**
218      * Returns the total width of the table.
219      * 
220      * @return int the width of the table
221      */
222     protected int getTotalWidth() {
223         return this.getRowWidth() + 3 * numColumns() + 1;
224     }
225 
226     /**
227      * Tests whether any string contains a newline symbol.
228      * 
229      * @param data
230      *            the array to check.
231      * @return whether any of the strings contains a newline symbol.
232      */
233     private boolean anyStringContainsNewLine(final String[] data) {
234         boolean returnVal = false;
235         for (int i = 0; i < data.length; i++) {
236             if (Util.containsNewlines(data[i])) {
237                 returnVal = true;
238             }
239         }
240         return returnVal;
241     }
242 
243 }