Skip to content

Commit c0bdd46

Browse files
Abyss-lordpancx
and
pancx
authored
[#5768] improvement(CLI): improve formatted output (#5770)
### What changes were proposed in this pull request? Enhanced the Gravitino CLI to support properly formatted output. Fixed alignment issues in formatted output when it includes Chinese characters. The bounding box length calculation is now based on the number of characters rather than the character display width . The correct output should look like this(Here are the results of my local tests): <img width="752" alt="image" src="https://github.com/user-attachments/assets/ae880b4b-280d-4dc4-9a25-f0deb3867bbc"> Only change the implementation of the TableFormatImpl class, the same effect is achieved when the `catalog list --output table` command is supported ### Why are the changes needed? Fix: #5768 ### Does this PR introduce _any_ user-facing change? NO. ### How was this patch tested? ```shell gcli catalog details --name <catalog> --metalake <metalake> --output table gcli metalake details --name <metalake> --output table gcli metalake list --output table ``` --------- Co-authored-by: pancx <pancx@chinatelecom.cn>
1 parent 0c9c0d0 commit c0bdd46

File tree

2 files changed

+98
-7
lines changed

2 files changed

+98
-7
lines changed

clients/cli/src/main/java/org/apache/gravitino/cli/outputs/TableFormat.java

+48-7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Arrays;
2323
import java.util.Collections;
2424
import java.util.List;
25+
import java.util.regex.Pattern;
2526
import org.apache.gravitino.Catalog;
2627
import org.apache.gravitino.Metalake;
2728

@@ -81,6 +82,12 @@ public void output(Catalog catalog) {
8182

8283
static final class TableFormatImpl {
8384
private int[] maxElementLengths;
85+
// This expression is primarily used to match characters that have a display width of
86+
// 2, such as characters from Korean, Chinese
87+
private static final Pattern FULL_WIDTH_PATTERN =
88+
Pattern.compile(
89+
"[\u1100-\u115F\u2E80-\uA4CF\uAC00-\uD7A3\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE6F\uFF00-\uFF60\uFFE0-\uFFE6]");
90+
private int[][] elementOutputWidths;
8491
private final String horizontalDelimiter = "-";
8592
private final String verticalDelimiter = "|";
8693
private final String crossDelimiter = "+";
@@ -96,10 +103,9 @@ public void print(List<String> headers, List<List<String>> rows) {
96103
throw new IllegalArgumentException("Number of columns is not equal.");
97104
}
98105
maxElementLengths = new int[headers.size()];
106+
elementOutputWidths = new int[rows.size()][headers.size()];
99107
updateMaxLengthsFromList(headers);
100108
updateMaxLengthsFromNestedList(rows);
101-
102-
// print headers
103109
printLine();
104110
System.out.println();
105111
for (int i = 0; i < headers.size(); ++i) {
@@ -115,9 +121,22 @@ public void print(List<String> headers, List<List<String>> rows) {
115121
for (int i = 0; i < rows.size(); ++i) {
116122
List<String> columns = rows.get(i);
117123
for (int j = 0; j < columns.size(); ++j) {
118-
System.out.printf(
119-
verticalDelimiter + indent + "%-" + maxElementLengths[j] + "s" + indent,
120-
columns.get(j));
124+
String column = columns.get(j);
125+
// Handle cases where the width and number of characters are inconsistent
126+
if (elementOutputWidths[i][j] != column.length()) {
127+
if (elementOutputWidths[i][j] > maxElementLengths[j]) {
128+
System.out.printf(
129+
verticalDelimiter + indent + "%-" + column.length() + "s" + indent, column);
130+
} else {
131+
int paddingLength =
132+
maxElementLengths[j] - (elementOutputWidths[i][j] - column.length());
133+
System.out.printf(
134+
verticalDelimiter + indent + "%-" + paddingLength + "s" + indent, column);
135+
}
136+
} else {
137+
System.out.printf(
138+
verticalDelimiter + indent + "%-" + maxElementLengths[j] + "s" + indent, column);
139+
}
121140
}
122141
System.out.println(verticalDelimiter);
123142
}
@@ -130,20 +149,42 @@ private void updateMaxLengthsFromList(List<String> elements) {
130149
String s;
131150
for (int i = 0; i < elements.size(); ++i) {
132151
s = elements.get(i);
133-
if (s.length() > maxElementLengths[i]) maxElementLengths[i] = s.length();
152+
if (getOutputWidth(s) > maxElementLengths[i]) maxElementLengths[i] = getOutputWidth(s);
134153
}
135154
}
136155

137156
private void updateMaxLengthsFromNestedList(List<List<String>> elements) {
157+
int rowIdx = 0;
138158
for (List<String> row : elements) {
139159
String s;
140160
for (int i = 0; i < row.size(); ++i) {
141161
s = row.get(i);
142-
if (s.length() > maxElementLengths[i]) maxElementLengths[i] = s.length();
162+
int consoleWidth = getOutputWidth(s);
163+
elementOutputWidths[rowIdx][i] = consoleWidth;
164+
if (consoleWidth > maxElementLengths[i]) maxElementLengths[i] = consoleWidth;
143165
}
166+
rowIdx++;
144167
}
145168
}
146169

170+
private int getOutputWidth(String s) {
171+
int width = 0;
172+
for (int i = 0; i < s.length(); i++) {
173+
width += getCharWidth(s.charAt(i));
174+
}
175+
176+
return width;
177+
}
178+
179+
private static int getCharWidth(char ch) {
180+
String s = String.valueOf(ch);
181+
if (FULL_WIDTH_PATTERN.matcher(s).find()) {
182+
return 2;
183+
}
184+
185+
return 1;
186+
}
187+
147188
private void printLine() {
148189
System.out.print(crossDelimiter);
149190
for (int i = 0; i < maxElementLengths.length; ++i) {

clients/cli/src/test/java/org/apache/gravitino/cli/integration/test/TableFormatOutputIT.java

+50
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,24 @@ public void startUp() {
6262
gravitinoUrl
6363
};
6464
Main.main(create_catalog_args);
65+
66+
String[] create_catalog_with_comment_args = {
67+
"catalog",
68+
"create",
69+
commandArg(GravitinoOptions.METALAKE),
70+
"my_metalake",
71+
commandArg(GravitinoOptions.NAME),
72+
"postgres2",
73+
commandArg(GravitinoOptions.PROVIDER),
74+
"postgres",
75+
commandArg(GravitinoOptions.PROPERTIES),
76+
"jdbc-url=jdbc:postgresql://postgresql-host/mydb,jdbc-user=user,jdbc-password=password,jdbc-database=db,jdbc-driver=org.postgresql.Driver",
77+
commandArg(GravitinoOptions.URL),
78+
gravitinoUrl,
79+
commandArg(GravitinoOptions.COMMENT),
80+
"catalog, 用于测试"
81+
};
82+
Main.main(create_catalog_with_comment_args);
6583
}
6684

6785
@Test
@@ -160,6 +178,38 @@ public void testCatalogDetailsCommand() {
160178
output);
161179
}
162180

181+
@Test
182+
public void testCatalogDetailsCommandFullCornerCharacter() {
183+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
184+
PrintStream originalOut = System.out;
185+
System.setOut(new PrintStream(outputStream));
186+
187+
String[] args = {
188+
"catalog",
189+
"details",
190+
commandArg(GravitinoOptions.METALAKE),
191+
"my_metalake",
192+
commandArg(GravitinoOptions.NAME),
193+
"postgres2",
194+
commandArg(GravitinoOptions.OUTPUT),
195+
"table",
196+
commandArg(GravitinoOptions.URL),
197+
gravitinoUrl
198+
};
199+
Main.main(args);
200+
// Restore the original System.out
201+
System.setOut(originalOut);
202+
// Get the output and verify it
203+
String output = new String(outputStream.toByteArray(), StandardCharsets.UTF_8).trim();
204+
assertEquals(
205+
"+-----------+------------+-----------------+-------------------+\n"
206+
+ "| catalog | type | provider | comment |\n"
207+
+ "+-----------+------------+-----------------+-------------------+\n"
208+
+ "| postgres2 | RELATIONAL | jdbc-postgresql | catalog, 用于测试 |\n"
209+
+ "+-----------+------------+-----------------+-------------------+",
210+
output);
211+
}
212+
163213
private String commandArg(String arg) {
164214
return String.format("--%s", arg);
165215
}

0 commit comments

Comments
 (0)