0

我想打印出基本上看起来像 JTextPane 的表格的内容,如下所示:

表格打印出来

我的输出目前看起来是这样的:

我当前的输出

如您所见,这些列根本没有排列。然而,在控制台中它是完美的,看起来像这样:

控制台打印

(有点截断,但你明白了,它是对齐的!)

为了做到这一点,我从https://github.com/htorun/dbtableprinter借用了一些代码。但是,代码是围绕打印到控制台构建的,因此我正在转换一些位以使用我的代码,即打印方法,因为我不希望在控制台中输出。我从我的 GUI 类中调用 2 个方法来代替 print 和 println:


//to replace print


public void printTableText(String toPrint){
    try {
        doc.insertString(doc.getLength(), toPrint, null);
    } catch (BadLocationException ex) {
        ex.printStackTrace();
    }
}



//to replace println

public void printText(String toPrint){
    try {
        toPrint = "\n" +  toPrint;
        doc.insertString(doc.getLength(), toPrint, null);
    } catch (BadLocationException ex) {
        ex.printStackTrace();
    }
}

本质上,他们的代码解析 JDBC 表中的元数据,以获取给定查询的行数、列数和它们各自的数据类型,并设置列宽、填充等以格式化控制台中的打印表视图. 它使用以下代码:(注意使用上述方法打印相关代码是我的,其他的都不是)


 public void printResultSet(ResultSet rs, int maxStringColWidth) {
        try {
            if (rs == null) {
                System.err.println("DBTablePrinter Error: Result set is null!");
                return;
            }
            if (rs.isClosed()) {
                System.err.println("DBTablePrinter Error: Result Set is closed!");
                return;
            }
            if (maxStringColWidth < 1) {
                System.err.println("DBTablePrinter Info: Invalid max. varchar column width. Using default!");
                maxStringColWidth = DEFAULT_MAX_TEXT_COL_WIDTH;
            }

            // Get the meta data object of this ResultSet.
            ResultSetMetaData rsmd;
            rsmd = rs.getMetaData();

            // Total number of columns in this ResultSet
            int columnCount = rsmd.getColumnCount();

            // List of Column objects to store each columns of the ResultSet
            // and the String representation of their values.
            List<Column> columns = new ArrayList<>(columnCount);

            // List of table names. Can be more than one if it is a joined
            // table query
            List<String> tableNames = new ArrayList<>(columnCount);

            // Get the columns and their meta data.
            // NOTE: columnIndex for rsmd.getXXX methods STARTS AT 1 NOT 0
            for (int i = 1; i <= columnCount; i++) {
                Column c = new Column(rsmd.getColumnLabel(i),
                        rsmd.getColumnType(i), rsmd.getColumnTypeName(i));
                c.setWidth(c.getLabel().length());
                c.setTypeCategory(whichCategory(c.getType()));
                columns.add(c);

                if (!tableNames.contains(rsmd.getTableName(i))) {
                    tableNames.add(rsmd.getTableName(i));
                }
            }

            // Go through each row, get values of each column and adjust
            // column widths.
            int rowCount = 0;
            while (rs.next()) {

                // NOTE: columnIndex for rs.getXXX methods STARTS AT 1 NOT 0
                for (int i = 0; i < columnCount; i++) {
                    Column c = columns.get(i);
                    String value;
                    int category = c.getTypeCategory();

                    if (category == CATEGORY_OTHER) {

                        // Use generic SQL type name instead of the actual value
                        // for column types BLOB, BINARY etc.
                        value = "(" + c.getTypeName() + ")";

                    } else {
                        value = rs.getString(i+1) == null ? "NULL" : rs.getString(i+1);
                    }
                    switch (category) {
                        case CATEGORY_DOUBLE:

                            // For real numbers, format the string value to have 3 digits
                            // after the point. THIS IS TOTALLY ARBITRARY and can be
                            // improved to be CONFIGURABLE.
                            if (!value.equals("NULL")) {
                                Double dValue = rs.getDouble(i+1);
                                value = String.format("%.3f", dValue);
                            }
                            break;

                        case CATEGORY_STRING:

                            // Left justify the text columns
                            c.justifyLeft();

                            // and apply the width limit
                            if (value.length() > maxStringColWidth) {
                                value = value.substring(0, maxStringColWidth - 3) + "...";
                            }
                            break;
                    }

                    // Adjust the column width
                    c.setWidth(value.length() > c.getWidth() ? value.length() : c.getWidth());
                    c.addValue(value);
                } // END of for loop columnCount
                rowCount++;

            } // END of while (rs.next)

            /*
            At this point we have gone through meta data, get the
            columns and created all Column objects, iterated over the
            ResultSet rows, populated the column values and adjusted
            the column widths.

            We cannot start printing just yet because we have to prepare
            a row separator String.
             */

            // For the fun of it, I will use StringBuilder
            StringBuilder strToPrint = new StringBuilder();
            StringBuilder rowSeparator = new StringBuilder();

            /*
            Prepare column labels to print as well as the row separator.
            It should look something like this:
            +--------+------------+------------+-----------+  (row separator)
            | EMP_NO | BIRTH_DATE | FIRST_NAME | LAST_NAME |  (labels row)
            +--------+------------+------------+-----------+  (row separator)
             */

            // Iterate over columns
            for (Column c : columns) {
                int width = c.getWidth();

              // Center the column label
                String toPrint;
                String name = c.getLabel();
                int diff = width - name.length();

                if ((diff%2) == 1) {
                    // diff is not divisible by 2, add 1 to width (and diff)
                    // so that we can have equal padding to the left and right
                    // of the column label.
                    width++;
                    diff++;
                    c.setWidth(width);
                }

                int paddingSize = diff/2; // InteliJ says casting to int is redundant.

                // Cool String repeater code thanks to user102008 at stackoverflow.com
             
                String padding = new String(new char[paddingSize]).replace("\0", " ");

                toPrint = "| " + padding + name + padding + " ";
              // END centering the column label

                strToPrint.append(toPrint);

                rowSeparator.append("+");
                rowSeparator.append(new String(new char[width + 2]).replace("\0", "-"));
            }

            String lineSeparator = System.getProperty("line.separator");

            // Is this really necessary ??
            lineSeparator = lineSeparator == null ? "\n" : lineSeparator;

            rowSeparator.append("+").append(lineSeparator);

            strToPrint.append("|").append(lineSeparator);
            strToPrint.insert(0, rowSeparator);
            strToPrint.append(rowSeparator);

            StringJoiner sj = new StringJoiner(", ");
            for (String name : tableNames) {
                sj.add(name);
            }

            String info = "Printing " + rowCount;
            info += rowCount > 1 ? " rows from " : " row from ";
            info += tableNames.size() > 1 ? "tables " : "table ";
            info += sj.toString();

            window.printTableText(info);
            System.out.println(info);

            // Print out the formatted column labels
            System.out.print(strToPrint.toString());
            window.printTableText(strToPrint.toString());

            String format;

            // Print out the rows
            for (int i = 0; i < rowCount; i++) {
                for (Column c : columns) {

                    // This should form a format string like: "%-60s"
                    format = String.format("| %%%s%ds ", c.getJustifyFlag(), c.getWidth());
                    System.out.print(
                            String.format(format, c.getValue(i))
                    );
                    window.printTableText(String.format(format, c.getValue(i)));
                }

                System.out.println("|");
                System.out.print(rowSeparator);
                window.printText("|");
                window.printTableText(String.valueOf(rowSeparator));
            }

            System.out.println();

            /*
                Hopefully this should have printed something like this:
                +--------+------------+------------+-----------+--------+-------------+
                | EMP_NO | BIRTH_DATE | FIRST_NAME | LAST_NAME | GENDER |  HIRE_DATE  |
                +--------+------------+------------+-----------+--------+-------------+
                |  10001 | 1953-09-02 | Georgi     | Facello   | M      |  1986-06-26 |
                +--------+------------+------------+-----------+--------+-------------+
                |  10002 | 1964-06-02 | Bezalel    | Simmel    | F      |  1985-11-21 |
                +--------+------------+------------+-----------+--------+-------------+
             */

        } catch (SQLException e) {
            System.err.println("SQL exception in DBTablePrinter. Message:");
            System.err.println(e.getMessage());
        }
    }

鉴于控制台输出是完美的,我得出的结论是我的打印方法存在一些问题,或者 StyledDocuments 如何处理我借用的 DBTablePrinter 类中使用的格式。我是 JTextPanes 和 StyledDocuments 的新手,所以也许只是发生了一些我不知道的简单事情。

4

0 回答 0