| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license |
| * agreements. See the NOTICE file distributed with this work for additional information regarding |
| * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the License. You may obtain a |
| * copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package org.apache.geode.management.internal.cli.result; |
| |
| /** |
| * Helper class to build rows of columnized strings & build a table from those rows. |
| * |
| * Sample usage: |
| * |
| * <code> |
| * public final Table createTable() { |
| * Table resultTable = TableBuilder.newTable(); |
| * resultTable.setColumnSeparator(" | "); |
| * |
| * resultTable.newBlankRow(); |
| * resultTable.newRow().newLeftCol("Displaying all fields for member: " + memberName); |
| * resultTable.newBlankRow(); |
| * RowGroup rowGroup = resultTable.newRowGroup(); |
| * rowGroup.newRow().newCenterCol("FIELD1").newCenterCol("FIELD2"); |
| * rowGroup.newRowSeparator('-'); |
| * for (int i = 0; i < counter; i++) { |
| * rowGroup.newRow().newLeftCol(myFirstField[i]).newLeftCol(mySecondField[i]); |
| * } |
| * resultTable.newBlankRow(); |
| * |
| * return resultTable; |
| * } |
| * </code> |
| * |
| * Will result in this: |
| * |
| * <literal> |
| * |
| * Displaying all fields for member: Member1 |
| * |
| * FIELD1 | FIELD2 -------------- | --------------- My First Field | My Second Field Another Fld1 | |
| * Another Fld2 Last Fld1 | Last Fld2 |
| * |
| * </literal> |
| * |
| * |
| * @since GemFire 7.0 |
| */ |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.apache.commons.lang3.StringUtils; |
| |
| import org.apache.geode.management.internal.cli.GfshParser; |
| |
| public class TableBuilder { |
| |
| public static Table newTable() { |
| return new Table(); |
| } |
| |
| public static class Table { |
| private final List<RowGroup> rowGroups = new ArrayList<>(); |
| private String columnSeparator = " "; |
| private boolean isTabularResult = false; |
| |
| public void setTabularResult(boolean isTabularResult) { |
| this.isTabularResult = isTabularResult; |
| } |
| |
| public void setColumnSeparator(final String columnSeparator) { |
| this.columnSeparator = columnSeparator; |
| } |
| |
| public String getColumnSeparator() { |
| return this.columnSeparator; |
| } |
| |
| public RowGroup newRowGroup() { |
| RowGroup rowGroup = new RowGroup(this); |
| this.rowGroups.add(rowGroup); |
| return rowGroup; |
| } |
| |
| public Row newRow() { |
| RowGroup rowGroup = newRowGroup(); |
| return rowGroup.newRow(); |
| } |
| |
| public void newBlankRow() { |
| RowGroup rowGroup = newRowGroup(); |
| rowGroup.newBlankRow(); |
| } |
| |
| public RowGroup getLastRowGroup() { |
| if (rowGroups.size() == 0) { |
| return null; |
| } |
| return rowGroups.get(rowGroups.size() - 1); |
| } |
| |
| /** |
| * Computes total Max Row Length across table - for all row groups. |
| */ |
| private int getMaxLength() { |
| int rowGroupMaxTotalLength = 0; |
| for (RowGroup rowGroup : this.rowGroups) { |
| List<Row> rows = rowGroup.rows; |
| int rowGroupTotalLength = 0; |
| |
| int rowMaxTotalLength = 0; |
| for (Row row : rows) { |
| int rowTotalLength = 0; |
| for (int i = 0; i < row.getNumCols(); i++) { |
| rowTotalLength += row.getMaxColLength(i); |
| } |
| |
| if (rowGroup.getColumnSeparator() != null) { |
| rowTotalLength += row.getNumCols() * rowGroup.getColumnSeparator().length(); |
| } |
| if (rowMaxTotalLength < rowTotalLength) { |
| rowMaxTotalLength = rowTotalLength; |
| } |
| } |
| |
| rowGroupTotalLength += rowMaxTotalLength; |
| if (rowGroupMaxTotalLength < rowGroupTotalLength) { |
| rowGroupMaxTotalLength = rowGroupTotalLength; |
| } |
| } |
| return (int) (rowGroupMaxTotalLength * 1.1); |
| } |
| |
| public String buildTable() { |
| StringBuilder stringBuffer = new StringBuilder(); |
| for (RowGroup rowGroup : this.rowGroups) { |
| stringBuffer.append(rowGroup.buildRowGroup(isTabularResult)); |
| } |
| |
| return stringBuffer.toString(); |
| } |
| |
| public List<String> buildTableList() { |
| List<String> list = new ArrayList<>(); |
| for (RowGroup rowGroup : this.rowGroups) { |
| list.add(rowGroup.buildRowGroup(isTabularResult)); |
| } |
| |
| return list; |
| } |
| |
| @Override |
| public String toString() { |
| return "Table [rowGroups=" + rowGroups + "]"; |
| } |
| } |
| |
| /** |
| * A group of rows. Widths for all columns within a group will be the same and when built will |
| * automatically be set to the length of the longest value in the column. |
| * |
| * @since GemFire 7.0 |
| */ |
| public static class RowGroup { |
| private final Table table; |
| private final List<Row> rows = new ArrayList<>(); |
| private int[] colSizes; |
| |
| private String columnSeparator; |
| |
| private RowGroup(final Table table) { |
| this.table = table; |
| } |
| |
| private Table getTable() { |
| return this.table; |
| } |
| |
| public Row newRow() { |
| Row row = new Row(this); |
| rows.add(row); |
| return row; |
| } |
| |
| public void newRowSeparator(Character character, boolean isTablewideSeparator) { |
| Row row = new Row(this, character, isTablewideSeparator); |
| rows.add(row); |
| } |
| |
| public void newBlankRow() { |
| Row row = newRow(); |
| row.newCenterCol(""); |
| row.isBlank = true; |
| } |
| |
| public String buildRowGroup(boolean isTabularResult) { |
| this.colSizes = computeColSizes(isTabularResult); |
| |
| StringBuilder stringBuffer = new StringBuilder(); |
| for (Row row : rows) { |
| String builtRow = row.buildRow(isTabularResult); |
| stringBuffer.append(builtRow); |
| if (StringUtils.isNotBlank(builtRow) || row.isBlank) { |
| stringBuffer.append(GfshParser.LINE_SEPARATOR); |
| } |
| } |
| return stringBuffer.toString(); |
| } |
| |
| private int getColSize(final int colNum) { |
| return this.colSizes[colNum]; |
| } |
| |
| private int[] computeColSizes(boolean isTabularResult) { |
| int[] localColSizes = new int[getNumCols()]; |
| |
| for (int i = 0; i < localColSizes.length; i++) { |
| localColSizes[i] = getMaxColLength(i); |
| } |
| |
| if (isTabularResult) { |
| localColSizes = TableBuilderHelper.recalculateColSizesForScreen( |
| TableBuilderHelper.getScreenWidth(), localColSizes, getColumnSeparator()); |
| } |
| |
| return localColSizes; |
| } |
| |
| private int getNumCols() { |
| int maxNumCols = 0; |
| |
| for (Row row : rows) { |
| int numCols = row.getNumCols(); |
| if (numCols > maxNumCols) { |
| maxNumCols = numCols; |
| } |
| } |
| |
| return maxNumCols; |
| } |
| |
| private int getMaxColLength(final int colNum) { |
| int maxLength = 0; |
| |
| for (Row row : rows) { |
| int colLength = row.getMaxColLength(colNum); |
| if (colLength > maxLength) { |
| maxLength = colLength; |
| } |
| } |
| |
| return maxLength; |
| } |
| |
| public void setColumnSeparator(final String columnSeparator) { |
| this.columnSeparator = columnSeparator; |
| } |
| |
| public String getColumnSeparator() { |
| return this.columnSeparator != null ? this.columnSeparator : table.getColumnSeparator(); |
| } |
| |
| @Override |
| public String toString() { |
| return "RowGroup [rows=" + rows + "]"; |
| } |
| } |
| |
| public static class Row { |
| private final RowGroup rowGroup; |
| private final Character rowSeparator; |
| private final List<Column> columns = new ArrayList<>(); |
| boolean isBlank; |
| private boolean isTablewideSeparator; |
| |
| private String columnSeparator; |
| |
| private Row(final RowGroup rowGroup) { |
| this.rowGroup = rowGroup; |
| this.rowSeparator = null; |
| } |
| |
| private Row(final RowGroup rowGroup, final Character rowSeparator, |
| final boolean isTablewideSeparator) { |
| this.rowGroup = rowGroup; |
| this.rowSeparator = rowSeparator; |
| this.isTablewideSeparator = isTablewideSeparator; |
| } |
| |
| public Row newLeftCol(Object value) { |
| Column column = new Column(value, Align.LEFT); |
| this.columns.add(column); |
| return this; |
| } |
| |
| public Row newRightCol(Object value) { |
| Column column = new Column(value, Align.RIGHT); |
| this.columns.add(column); |
| return this; |
| } |
| |
| public Row newCenterCol(Object value) { |
| Column column = new Column(value, Align.CENTER); |
| this.columns.add(column); |
| return this; |
| } |
| |
| public boolean isEmpty() { |
| return columns.isEmpty(); |
| } |
| |
| private int getNumCols() { |
| return this.columns.size(); |
| } |
| |
| private int getMaxColLength(final int colNum) { |
| if (colNum >= this.columns.size()) |
| return 0; |
| |
| return this.columns.get(colNum).getLength(); |
| } |
| |
| public void setColumnSeparator(final String columnSeparator) { |
| this.columnSeparator = columnSeparator; |
| } |
| |
| public String getColumnSeparator() { |
| return this.columnSeparator != null ? this.columnSeparator : rowGroup.getColumnSeparator(); |
| } |
| |
| private String buildRow(boolean isTabularResult) { |
| StringBuilder stringBuffer = new StringBuilder(); |
| if (this.rowSeparator != null) { |
| if (isTablewideSeparator) { |
| int maxColLength = this.rowGroup.getTable().getMaxLength(); |
| // Trim only for tabular results |
| if (isTabularResult) { |
| maxColLength = TableBuilderHelper.trimWidthForScreen(maxColLength); |
| } |
| |
| for (int j = 0; j < maxColLength; j++) { |
| stringBuffer.append(this.rowSeparator); |
| } |
| } else { |
| int maxNumCols = this.rowGroup.getNumCols(); |
| |
| for (int i = 0; i < maxNumCols; i++) { |
| int maxColLength = this.rowGroup.getColSize(i); |
| for (int j = 0; j < maxColLength; j++) { |
| stringBuffer.append(this.rowSeparator); |
| } |
| if (i < (maxNumCols - 1)) { |
| stringBuffer.append(this.rowGroup.getColumnSeparator()); |
| } |
| } |
| } |
| } else { |
| for (int i = 0; i < this.columns.size(); i++) { |
| boolean lastColumn = !(i < (this.columns.size() - 1)); |
| stringBuffer |
| .append(this.columns.get(i).buildColumn(this.rowGroup.getColSize(i), lastColumn)); |
| if (!lastColumn) { |
| stringBuffer.append(getColumnSeparator()); |
| } |
| } |
| } |
| |
| return stringBuffer.toString(); |
| } |
| |
| @Override |
| public String toString() { |
| return "Row [columns=" + columns + "]"; |
| } |
| } |
| |
| private enum Align { |
| LEFT, RIGHT, CENTER |
| } |
| |
| private static class Column { |
| |
| private final Align align; |
| private final String stringValue; |
| |
| private Column(final Object value, final Align align) { |
| if (value == null) { |
| this.stringValue = ""; |
| } else { |
| this.stringValue = value.toString(); |
| } |
| |
| this.align = align; |
| } |
| |
| private int getLength() { |
| return this.stringValue.length(); |
| } |
| |
| private String buildColumn(int colWidth, boolean trimIt) { |
| |
| // If string value is greater than colWidth |
| // This can happen because colSizes are re-computed |
| // to fit the screen width |
| if (this.stringValue.length() > colWidth) { |
| StringBuilder stringBuffer = new StringBuilder(); |
| int endIndex = colWidth - 2; |
| if (endIndex < 0) |
| return ""; |
| return stringBuffer.append(stringValue.substring(0, endIndex)).append("..").toString(); |
| } |
| |
| int numSpaces = colWidth - this.stringValue.length(); |
| if (trimIt) |
| numSpaces = 0; |
| |
| StringBuilder stringBuffer = new StringBuilder(); |
| |
| switch (align) { |
| case LEFT: |
| stringBuffer.append(stringValue); |
| for (int i = 0; i < numSpaces; i++) { |
| stringBuffer.append(" "); |
| } |
| break; |
| case RIGHT: |
| for (int i = 0; i < numSpaces; i++) { |
| stringBuffer.append(" "); |
| } |
| stringBuffer.append(stringValue); |
| break; |
| case CENTER: |
| int i = 0; |
| for (; i < numSpaces / 2; i++) { |
| stringBuffer.append(" "); |
| } |
| stringBuffer.append(stringValue); |
| for (; i < numSpaces; i++) { |
| stringBuffer.append(" "); |
| } |
| break; |
| } |
| |
| return stringBuffer.toString(); |
| |
| } |
| |
| |
| |
| private String buildColumn(int colWidth) { |
| return buildColumn(colWidth, false); |
| } |
| |
| @Override |
| public String toString() { |
| return "Column [align=" + align + ", stringValue=" + stringValue + "]"; |
| } |
| } |
| } |