org.apache.dubbo.qos.textui.TTable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dubbo Show documentation
Show all versions of dubbo Show documentation
The all in one project of dubbo
/*
* 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.dubbo.qos.textui;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import static java.lang.Math.abs;
import static java.lang.Math.max;
import static java.lang.String.format;
import static org.apache.dubbo.common.utils.StringUtils.EMPTY_STRING;
import static org.apache.dubbo.common.utils.StringUtils.length;
import static org.apache.dubbo.common.utils.StringUtils.repeat;
import static org.apache.dubbo.common.utils.StringUtils.replace;
/**
* Table
*/
public class TTable implements TComponent {
// column definition
private final ColumnDefine[] columnDefineArray;
// border
private final Border border = new Border();
// padding
private int padding;
public TTable(ColumnDefine[] columnDefineArray) {
this.columnDefineArray = null == columnDefineArray
? new ColumnDefine[0]
: columnDefineArray;
}
public TTable(int columnNum) {
this.columnDefineArray = new ColumnDefine[columnNum];
for (int index = 0; index < this.columnDefineArray.length; index++) {
columnDefineArray[index] = new ColumnDefine();
}
}
@Override
public String rendering() {
final StringBuilder tableSB = new StringBuilder();
// process width cache
final int[] widthCacheArray = new int[getColumnCount()];
for (int index = 0; index < widthCacheArray.length; index++) {
widthCacheArray[index] = abs(columnDefineArray[index].getWidth());
}
final int rowCount = getRowCount();
for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
final boolean isFirstRow = rowIndex == 0;
final boolean isLastRow = rowIndex == rowCount - 1;
// print first separation line
if (isFirstRow
&& border.has(Border.BORDER_OUTER_TOP)) {
tableSB.append(drawSeparationLine(widthCacheArray)).append(System.lineSeparator());
}
// print inner separation lines
if (!isFirstRow
&& border.has(Border.BORDER_INNER_H)) {
tableSB.append(drawSeparationLine(widthCacheArray)).append(System.lineSeparator());
}
// draw one line
tableSB.append(drawRow(widthCacheArray, rowIndex));
// print ending separation line
if (isLastRow
&& border.has(Border.BORDER_OUTER_BOTTOM)) {
tableSB.append(drawSeparationLine(widthCacheArray)).append(System.lineSeparator());
}
}
return tableSB.toString();
}
private String drawRow(int[] widthCacheArray, int rowIndex) {
final StringBuilder rowSB = new StringBuilder();
final Scanner[] scannerArray = new Scanner[getColumnCount()];
try {
boolean hasNextLine;
do {
hasNextLine = false;
final StringBuilder segmentSB = new StringBuilder();
for (int colIndex = 0; colIndex < getColumnCount(); colIndex++) {
final int width = widthCacheArray[colIndex];
final boolean isFirstColOfRow = colIndex == 0;
final boolean isLastColOfRow = colIndex == widthCacheArray.length - 1;
final String borderChar;
if (isFirstColOfRow
&& border.has(Border.BORDER_OUTER_LEFT)) {
borderChar = "|";
} else if (!isFirstColOfRow
&& border.has(Border.BORDER_INNER_V)) {
borderChar = "|";
} else {
borderChar = EMPTY_STRING;
}
if (null == scannerArray[colIndex]) {
scannerArray[colIndex] = new Scanner(
new StringReader(wrap(getData(rowIndex, columnDefineArray[colIndex]), width)));
}
final Scanner scanner = scannerArray[colIndex];
final String data;
if (scanner.hasNextLine()) {
data = scanner.nextLine();
hasNextLine = true;
} else {
data = EMPTY_STRING;
}
if (width > 0) {
final ColumnDefine columnDefine = columnDefineArray[colIndex];
final String dataFormat = getDataFormat(columnDefine, width, data);
final String paddingChar = repeat(" ", padding);
segmentSB.append(format(borderChar + paddingChar + dataFormat + paddingChar, data));
}
if (isLastColOfRow) {
if (border.has(Border.BORDER_OUTER_RIGHT)) {
segmentSB.append('|');
}
segmentSB.append(System.lineSeparator());
}
}
if (hasNextLine) {
rowSB.append(segmentSB);
}
} while (hasNextLine);
return rowSB.toString();
} finally {
for (Scanner scanner : scannerArray) {
if (null != scanner) {
scanner.close();
}
}
}
}
private String getData(int rowIndex, ColumnDefine columnDefine) {
return columnDefine.getRowCount() <= rowIndex
? EMPTY_STRING
: columnDefine.rows.get(rowIndex);
}
private String getDataFormat(ColumnDefine columnDefine, int width, String data) {
switch (columnDefine.align) {
case MIDDLE: {
final int length = length(data);
final int diff = width - length;
final int left = diff / 2;
return repeat(" ", diff - left) + "%s" + repeat(" ", left);
}
case RIGHT: {
return "%" + width + "s";
}
case LEFT:
default: {
return "%-" + width + "s";
}
}
}
/**
* get row count
*/
private int getRowCount() {
int rowCount = 0;
for (ColumnDefine columnDefine : columnDefineArray) {
rowCount = max(rowCount, columnDefine.getRowCount());
}
return rowCount;
}
/**
* position to last column
*/
private int indexLastCol(final int[] widthCacheArray) {
for (int colIndex = widthCacheArray.length - 1; colIndex >= 0; colIndex--) {
final int width = widthCacheArray[colIndex];
if (width <= 0) {
continue;
}
return colIndex;
}
return 0;
}
/**
* draw separation line
*/
private String drawSeparationLine(final int[] widthCacheArray) {
final StringBuilder separationLineSB = new StringBuilder();
final int lastCol = indexLastCol(widthCacheArray);
final int colCount = widthCacheArray.length;
for (int colIndex = 0; colIndex < colCount; colIndex++) {
final int width = widthCacheArray[colIndex];
if (width <= 0) {
continue;
}
final boolean isFirstCol = colIndex == 0;
final boolean isLastCol = colIndex == lastCol;
if (isFirstCol
&& border.has(Border.BORDER_OUTER_LEFT)) {
separationLineSB.append('+');
}
if (!isFirstCol
&& border.has(Border.BORDER_INNER_V)) {
separationLineSB.append('+');
}
separationLineSB.append(repeat("-", width + 2 * padding));
if (isLastCol
&& border.has(Border.BORDER_OUTER_RIGHT)) {
separationLineSB.append('+');
}
}
return separationLineSB.toString();
}
/**
* Add a row
*/
public TTable addRow(Object... columnDataArray) {
if (null != columnDataArray) {
for (int index = 0; index < columnDefineArray.length; index++) {
final ColumnDefine columnDefine = columnDefineArray[index];
if (index < columnDataArray.length
&& null != columnDataArray[index]) {
columnDefine.rows.add(replaceTab(columnDataArray[index].toString()));
} else {
columnDefine.rows.add(EMPTY_STRING);
}
}
}
return this;
}
/**
* alignment
*/
public enum Align {
/**
* left-alignment
*/
LEFT,
/**
* right-alignment
*/
RIGHT,
/**
* middle-alignment
*/
MIDDLE
}
/**
* column definition
*/
public static class ColumnDefine {
// column width
private final int width;
// whether to auto resize
private final boolean isAutoResize;
// alignment
private final Align align;
// data rows
private final List rows = new ArrayList();
public ColumnDefine(int width, boolean isAutoResize, Align align) {
this.width = width;
this.isAutoResize = isAutoResize;
this.align = align;
}
public ColumnDefine(Align align) {
this(0, true, align);
}
public ColumnDefine(int width) {
this(width, false, Align.LEFT);
}
public ColumnDefine(int width, Align align) {
this(width, false, align);
}
public ColumnDefine() {
this(Align.LEFT);
}
/**
* get current width
*
* @return width
*/
public int getWidth() {
// if not auto resize, return preset width
if (!isAutoResize) {
return width;
}
// if it's auto resize, then calculate the possible max width
int maxWidth = 0;
for (String data : rows) {
maxWidth = max(width(data), maxWidth);
}
return maxWidth;
}
/**
* get rows for the current column
*
* @return current column's rows
*/
public int getRowCount() {
return rows.size();
}
}
/**
* set padding
*
* @param padding padding
*/
public TTable padding(int padding) {
this.padding = padding;
return this;
}
/**
* get column count
*
* @return column count
*/
public int getColumnCount() {
return columnDefineArray.length;
}
/**
* replace tab to four spaces
*
* @param string the original string
* @return the replaced string
*/
private static String replaceTab(String string) {
return replace(string, "\t", " ");
}
/**
* visible width for the given string.
*
* for example: "abc\n1234"'s width is 4.
*
* @param string the given string
* @return visible width
*/
private static int width(String string) {
int maxWidth = 0;
try (Scanner scanner = new Scanner(new StringReader(string))) {
while (scanner.hasNextLine()) {
maxWidth = max(length(scanner.nextLine()), maxWidth);
}
}
return maxWidth;
}
/**
* get border
*
* @return table border
*/
public Border getBorder() {
return border;
}
/**
* border style
*/
public class Border {
private int borders = BORDER_OUTER | BORDER_INNER;
/**
* border outer top
*/
public static final int BORDER_OUTER_TOP = 1 << 0;
/**
* border outer right
*/
public static final int BORDER_OUTER_RIGHT = 1 << 1;
/**
* border outer bottom
*/
public static final int BORDER_OUTER_BOTTOM = 1 << 2;
/**
* border outer left
*/
public static final int BORDER_OUTER_LEFT = 1 << 3;
/**
* inner border: horizon
*/
public static final int BORDER_INNER_H = 1 << 4;
/**
* inner border: vertical
*/
public static final int BORDER_INNER_V = 1 << 5;
/**
* outer border
*/
public static final int BORDER_OUTER = BORDER_OUTER_TOP | BORDER_OUTER_BOTTOM | BORDER_OUTER_LEFT | BORDER_OUTER_RIGHT;
/**
* inner border
*/
public static final int BORDER_INNER = BORDER_INNER_H | BORDER_INNER_V;
/**
* no border
*/
public static final int BORDER_NON = 0;
/**
* whether has one of the specified border styles
*
* @param borderArray border styles
* @return whether has one of the specified border styles
*/
public boolean has(int... borderArray) {
if (null == borderArray) {
return false;
}
for (int b : borderArray) {
if ((this.borders & b) == b) {
return true;
}
}
return false;
}
/**
* get border style
*
* @return border style
*/
public int get() {
return borders;
}
/**
* set border style
*
* @param border border style
* @return this
*/
public Border set(int border) {
this.borders = border;
return this;
}
public Border add(int border) {
return set(get() | border);
}
public Border remove(int border) {
return set(get() ^ border);
}
}
public static String wrap(String string, int width) {
final StringBuilder sb = new StringBuilder();
final char[] buffer = string.toCharArray();
int count = 0;
for (char c : buffer) {
if (count == width) {
count = 0;
sb.append('\n');
if (c == '\n') {
continue;
}
}
if (c == '\n') {
count = 0;
} else {
count++;
}
sb.append(c);
}
return sb.toString();
}
}