Formatierte Tabellen auf der Console ausgeben

Aus Byte-Welt Wiki
Zur Navigation springenZur Suche springen


Die folgende Klasse kann auf einfache Art und Weise eine formatierte Texttabelle als String erzeugen. Dabei können zusätzlich die Formatierungseigenschaften der Klasse java.util.Formatter verwendet werden. Die Formatierung ähnelt den Konsolenausgaben von MySQL-Anfragen. Viel Spaß!

<code=java> /*

* Table.java
*
* 14.10.2010
*
* (c) fastjack
*/

package blog.text;

import java.util.*;

/**

* This class defines a simple formatted text table. It can be used to create simple formatted
* text tables (like MySQL console outputs) with the formatting stuff from class
* "java.util.Formatter" defined in the enumerations.
*
* @author fastjack
*/

public class Table {

   private List<Col> cols = new ArrayList<Col>();
   private List<Row> rows = new ArrayList<Row>();

   /**
    * Adds a col.
    *
    * @param name the name
    * @return the table
    */
   public Table addCol(String name) {
       this.cols.add(new Col(name));
       return this;
   }

   /**
    * Adds a col.
    *
    * @param name the name
    * @param flags the flags
    * @return the table
    */
   public Table addCol(String name, Flags... flags) {
       this.cols.add(new Col(name, null, flags));
       return this;
   }

   /**
    * Adds a col.
    *
    * @param name the name
    * @param conversion the conversion
    * @param flags the flags
    * @return the table
    */
   public Table addCol(String name, Conversions conversion, Flags... flags) {
       this.cols.add(new Col(name, conversion, flags));
       return this;
   }

   /**
    * Adds a col.
    *
    * @param name the name
    * @param conversion the conversion
    * @param dtconversion the date/timeconversion
    * @param flags the flags
    * @return the table
    */
   public Table addCol(String name, Conversions conversion, DateTimeConversions dtconversion,
           Flags... flags) {
       this.cols.add(new Col(name, conversion, dtconversion, flags));
       return this;
   }

   /**
    * Gets the col size.
    *
    * @return the col size
    */
   public int getColSize() {
       return this.cols.size();
   }

   /**
    * Gets the col.
    *
    * @param index the index
    * @return the col
    */
   public Col getCol(int index) {
       return this.cols.get(index);
   }

   /**
    * Gets the row size.
    *
    * @return the row size
    */
   public int getRowSize() {
       return this.rows.size();
   }

   /**
    * Gets the row.
    *
    * @param index the index
    * @return the row
    */
   public Row getRow(int index) {
       return this.rows.get(index);
   }

   /**
    * Adds the row.
    *
    * @param values the values
    */
   public void addRow(Object... values) {
       this.rows.add(new Row(values));
   }

   @Override
   public String toString() {
       StringBuilder s1 = new StringBuilder();
       StringBuilder s2 = new StringBuilder();
       // data
       for (int i = 0, n = this.rows.size(); i < n; i++) {
           Row r = this.rows.get(i);
           for (int ii = 0, nn = this.cols.size(); ii < nn; ii++) {
               Col c = this.cols.get(ii);
               Object o = r.getValue(ii);
               if (c.getConversion() == null) {
                   c.setConversion(this.getConversionForType(o));
               }
               int nl = Math.max(c.getName().length(), String.valueOf(o).length());
               if (nl > c.getWidth()) {
                   c.width = nl;
               }
               int ol = String.format(this.getToken(o, c.getWidth(), c.getConversion(),
                       c.getDateTimeConversion(), c.getFlags()), o).length();
               if (ol > c.getWidth()) {
                   c.width = ol;
               }
           }
       }
       for (int i = 0, n = this.rows.size(); i < n; i++) {
           Row r = this.rows.get(i);
           StringBuilder data = new StringBuilder();
           for (int ii = 0, nn = this.cols.size(); ii < nn; ii++) {
               Col c = this.cols.get(ii);
               Object o = r.getValue(ii);
               data.append("| ");
               data.append(this.getToken(o, c.getWidth(), c.getConversion(),
                       c.getDateTimeConversion(), c.getFlags()));
               if (ii < nn - 1) {
                   data.append(" ");
               }
           }
           data.append(" |\n");
           s2.append(String.format(data.toString(), r.getValues()));
       }
       // head
       StringBuilder sep = new StringBuilder();
       StringBuilder begin = new StringBuilder();
       StringBuilder end = new StringBuilder();
       StringBuilder head = new StringBuilder();
       Object[] oa = new Object[this.cols.size()];
       for (int i = 0, n = this.cols.size(); i < n; i++) {
           Col c = this.cols.get(i);
           oa[i] = c.getName();
           if (i == 0) {
               begin.append("+");
               end.append("+");
               sep.append("+");
           } else {
               begin.append("+");
               end.append("+");
               sep.append("+");
           }
           head.append("| %");
           head.append(String.valueOf(c.getWidth()));
           head.append("s");
           for (int ii = 0; ii < c.getWidth() + 2; ii++) {
               begin.append("-");
               sep.append("-");
               end.append("-");
           }
           if (i < n - 1) {
               head.append(" ");
           }
       }
       begin.append("+\n");
       sep.append("+\n");
       end.append("+\n");
       head.append(" |\n");
       s1.append(begin);
       s1.append(String.format(head.toString(), oa));
       s1.append(sep);
       s1.append(s2);
       s1.append(end);
       return s1.toString();
   }

   private String getToken(Object object, int width, Conversions conversion,
           DateTimeConversions dtconversion, Flags... flags) {
       StringBuilder s = new StringBuilder();
       s.append("%");
       if (flags != null && flags.length > 0) {
           for (int i = 0; i < flags.length; i++) {
               Flags f = flags[i];
               s.append(f.getToken());
           }
       }
       if (width > 0) {
           s.append(width);
       }
       if (conversion != null) {
           s.append(conversion.getToken());
       } else {
           s.append(this.getConversionForType(object).getToken());
       }
       if (dtconversion != null) {
           s.append(dtconversion.getToken());
       }
       return s.toString();
   }

   private Conversions getConversionForType(Object object) {
       if (object instanceof Byte) {
           return Conversions.DECIMAL;
       } else if (object instanceof Short) {
           return Conversions.DECIMAL;
       } else if (object instanceof Integer) {
           return Conversions.DECIMAL;
       } else if (object instanceof Character) {
           return Conversions.CHARACTER;
       } else if (object instanceof Long) {
           return Conversions.DECIMAL;
       } else if (object instanceof Float) {
           return Conversions.FLOAT;
       } else if (object instanceof Double) {
           return Conversions.FLOAT;
       } else if (object instanceof Boolean) {
           return Conversions.BOOLEAN;
       } else if (object instanceof String) {
           return Conversions.STRING;
       } else {
           return Conversions.STRING;
       }
   }

</code=java>

Die Klasse für die Zeilen:

<code=java>

   /**
    * This class defines a row, containing an object arry with values.
    */
   public class Row {
       private Object[] values = null;

       /**
        * Instantiates a new row.
        *
        * @param values the values
        */
       public Row(Object[] values) {
           super();
           this.values = values;
       }

       /**
        * Gets the value.
        *
        * @param index the index
        * @return the value
        */
       public Object getValue(int index) {
           return this.values[index];
       }

       /**
        * Gets the values.
        *
        * @return the values
        */
       public Object[] getValues() {
           return this.values;
       }
   }

</code=java>

Die Klasse für die Spalten:

<code=java>

   /**
    * This class defines a column.
    */
   public class Col {
       private Flags[] flags = null;
       private Conversions conversion = null;
       private DateTimeConversions dateTimeConversion = null;
       private String name = "";
       private int width = 0;

       /**
        * Instantiates a new col.
        *
        * @param name the name
        */
       public Col(String name) {
           this(name, null);
       }

       /**
        * Instantiates a new col.
        *
        * @param name the name
        * @param conversion the conversion
        * @param flags the flags
        */
       public Col(String name, Conversions conversion, Flags... flags) {
           this(name, conversion, null, flags);
       }

       /**
        * Instantiates a new col.
        *
        * @param name the name
        * @param conversion the conversion
        * @param dateTimeConversion the date time conversion
        * @param flags the flags
        */
       public Col(String name, Conversions conversion, DateTimeConversions dateTimeConversion,
               Flags... flags) {
           super();
           this.dateTimeConversion = dateTimeConversion;
           this.conversion = conversion;
           this.flags = flags;
           this.name = name;
           this.width = this.name.length();
       }

       /**
        * Gets the name.
        *
        * @return the name
        */
       public String getName() {
           return this.name;
       }

       /**
        * Gets the conversion.
        *
        * @return the conversion
        */
       public Conversions getConversion() {
           return this.conversion;
       }

       /**
        * Sets the conversion.
        *
        * @param conversion the new conversion
        */
       public void setConversion(Conversions conversion) {
           this.conversion = conversion;
       }

       /**
        * Gets the width.
        *
        * @return the width
        */
       public int getWidth() {
           return this.width;
       }

       /**
        * Gets the flags.
        *
        * @return the flags
        */
       public Flags[] getFlags() {
           return this.flags;
       }

       /**
        * Gets the date time conversion.
        *
        * @return the date time conversion
        */
       public DateTimeConversions getDateTimeConversion() {
           return this.dateTimeConversion;
       }

   }

</code=java>

Die Flaggen:

<code=java>

   /**
    * This enumeration defines the flags.
    */
   public enum Flags {
       LEFT("-"),
       CONVERSION_DEPENDENT("#"),
       SIGN("+"),
       POSITIVE_LEADING_SPACE("  "),
       ZERO_PADDING("0"),
       LOCALE_GROUPING_SEPARATORS(","),
       PARENTHESES_FOR_NEGATIVES("(");

       String token = "";

       /**
        * Instantiates a new flag.
        *
        * @param token the token
        */
       Flags(String token) {
           this.token = token;
       }

       /**
        * Gets the token.
        *
        * @return the token
        */
       String getToken() {
           return token;
       }
   }

</code=java>

Die Konversionen:

<code=java>

   /**
    * This enumeration defines conversions.
    */
   public enum Conversions {
       BOOLEAN("b"),
       CHARACTER("c"),
       DATE_TIME("t"),
       DECIMAL("d"),
       FLOAT("f"),
       FLOAT_HEX("a"),
       FLOAT_SCIENCE("e"),
       FLOAT_SCIENCE_PREC("g"),
       HEX("h"),
       HEX_INT("h"),
       LINE_SEPARATOR("n"),
       OCTAL("o"),
       PERCENT("%"),
       STRING("s");

       String token = "";

       /**
        * Instantiates a new conversion.
        *
        * @param token the token
        */
       Conversions(String token) {
           this.token = token;
       }

       /**
        * Gets the token.
        *
        * @return the token
        */
       String getToken() {
           return token;
       }

   }

</code=java>

Zeit/Datums- Konversionen:

<code=java>

   /**
    * This enumeration defines date/time conversions.
    */
   public enum DateTimeConversions {
       FULL_MONTH_NAME("B"),
       ABBREVATED_MONTH_NAME("b"),
       ABBREVATED_MONTH_NAME_H("h"),
       FULL_DAY_OF_THE_WEEK_NAME("A"),
       SHORT_DAY_OF_THE_WEEK_NAME("a"),
       FOUR_DIGIT_YEAR("C"),
       LEADING_ZEROS_YEAR("Y"),
       LAST_TWO_DIGITS_YEAR("y"),
       THREE_DIGIT_DAY_OF_THE_YEAR("j"),
       TWO_DIGIT_MONTH("m"),
       TWO_DIGIT_DAY_OF_MONTH("d"),
       DAY_OF_MONTH("e"),
       HOUR_OF_DAY_LEADING_24("H"),
       HOUR_OF_DAY_LEADING_12("I"),
       HOUR_OF_DAY_24("k"),
       HOUR_OF_DAY_12("l"),
       TWO_DIGIT_MINUTE("M"),
       TWO_DIGIT_SECOND("S"),
       THREE_DIGIT_MILLISECOND("L"),
       TWO_DIGIT_NANOSECOND("N"),
       MORING_AFTERNOON_MARKER("p"),
       RFC822_TIME_ZONE_OFFSET("z"),
       TIME_ZONE_ABBREVATION("Z"),
       BEGINNING_SECONDS("s"),
       BEGINNING_MILLISECONDS("Q"),
       TIME_HOUR_CLOCK_24("R"),
       LONG_TIME_HOUR_CLOCK_24("T"),
       TIME_HOUR_CLOCK_12("r"),
       LONG_DATE("D"),
       ISO8601_DATE("F"),
       FULL_DATE("c");

       String token = "";

       /**
        * Instantiates a new date time conversion.
        *
        * @param token the token
        */
       DateTimeConversions(String token) {
           this.token = token;
       }

       /**
        * Gets the token.
        *
        * @return the token
        */
       String getToken() {
           return token;
       }
   }

} </code=java>

Der folgende Anwendungsfall:

<code=java>

public static void main(String[] args) {
       Table t = new Table();
       t = t.addCol("char", Flags.LEFT)
               .addCol("byte", Flags.ZERO_PADDING)
               .addCol("short", Flags.PARENTHESES_FOR_NEGATIVES, Flags.LEFT)
               .addCol("int", Flags.LOCALE_GROUPING_SEPARATORS)
               .addCol("long", Flags.LOCALE_GROUPING_SEPARATORS, Flags.SIGN)
               .addCol("float", Conversions.FLOAT_HEX)
               .addCol("double", Conversions.FLOAT_SCIENCE)
               .addCol("boolean")
               .addCol("string")
               .addCol("object")
               .addCol("null")
               .addCol("Date", Conversions.DATE_TIME, DateTimeConversions.FULL_DATE);
       t.addRow('a', (byte) 1, (short) 1, 100, 100L, 1.0F, 1.0, true, "ab", new Object(), null,
               new Date());
       t.addRow('a', (byte) 1, (short) -1, 1000, 1000L, 1.0F, 1.0, false, "abbas", new Object(), null,
               new Date());
       t.addRow('a', (byte) 1, (short) 1, 10000, -10000L, 1.0F, 1.0, true, "sdhjsdhjdsghsdghds",
               new Object(), null, new Date());
       Col c = t.getCol(0);
       System.out.println(t);
   }

</code=java>

Erzeugt die Ausgabe:

+------+------+-------+--------+---------+---------+--------------+---------+--------------------+--------------------------+------+-------------------------------+
| char | byte | short |    int |    long |   float |       double | boolean |             string |                   object | null |                          Date |
+------+------+-------+--------+---------+---------+--------------+---------+--------------------+--------------------------+------+-------------------------------+
| a    | 0001 | 1     |    100 |    +100 | 0x1.0p0 | 1,000000e+00 |    true |                 ab | java.lang.Object@1e97676 | null |  So Apr 03 17:46:15 MESZ 2011 |
| a    | 0001 | (1)   |  1.000 |  +1.000 | 0x1.0p0 | 1,000000e+00 |   false |              abbas |  java.lang.Object@7bd9f2 | null |  So Apr 03 17:46:15 MESZ 2011 |
| a    | 0001 | 1     | 10.000 | -10.000 | 0x1.0p0 | 1,000000e+00 |    true | sdhjsdhjdsghsdghds | java.lang.Object@121cc40 | null |  So Apr 03 17:46:15 MESZ 2011 |
+------+------+-------+--------+---------+---------+--------------+---------+--------------------+--------------------------+------+-------------------------------+