/* * 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.geode.management.internal.cli.GfshParser; import org.apache.geode.management.internal.cli.shell.Gfsh; public class TableBuilder { public static Table newTable() { return new Table(); } public static class Table { private final List<RowGroup> rowGroups = new ArrayList<RowGroup>(); 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() { StringBuffer stringBuffer = new StringBuffer(); for (RowGroup rowGroup : this.rowGroups) { stringBuffer.append(rowGroup.buildRowGroup(isTabularResult)); } return stringBuffer.toString(); } public List<String> buildTableList() { List<String> list = new ArrayList<String>(); 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<Row>(); 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); StringBuffer stringBuffer = new StringBuffer(); for (Row row : rows) { String builtRow = row.buildRow(isTabularResult); stringBuffer.append(builtRow); if (!builtRow.trim().isEmpty() || 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<Column>(); 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) { StringBuffer stringBuffer = new StringBuffer(); 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.getMaxColLength(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 { // saj hook 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 static 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) { StringBuffer stringBuffer = new StringBuffer(); 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; StringBuffer stringBuffer = new StringBuffer(); 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 + "]"; } } }