From: James Moger Date: Sat, 22 Mar 2014 20:34:27 +0000 (-0400) Subject: Integrate a patched version of FlipTables and improve ls output X-Git-Tag: v1.5.0~68^2~42 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=4a69998080bd01180c5e46e4f4fd2c50586fea80;p=gitblit.git Integrate a patched version of FlipTables and improve ls output --- diff --git a/src/main/java/com/gitblit/transport/ssh/SshKey.java b/src/main/java/com/gitblit/transport/ssh/SshKey.java index cac6c417..498b6bd4 100644 --- a/src/main/java/com/gitblit/transport/ssh/SshKey.java +++ b/src/main/java/com/gitblit/transport/ssh/SshKey.java @@ -28,6 +28,8 @@ public class SshKey implements Serializable { private String fingerprint; + private String toString; + public SshKey(String data) { this.rawData = data; } @@ -96,12 +98,6 @@ public class SshKey implements Serializable { public String getFingerprint() { if (fingerprint == null) { StringBuilder sb = new StringBuilder(); - // TODO append the keysize - int keySize = 0; - if (keySize > 0) { - sb.append(keySize).append(' '); - } - // append the key hash as colon-separated pairs String hash; if (rawData != null) { @@ -116,19 +112,6 @@ public class SshKey implements Serializable { sb.append(hash.charAt(i)).append(hash.charAt(i + 1)).append(':'); } sb.setLength(sb.length() - 1); - - // append the comment - String c = getComment(); - if (!StringUtils.isEmpty(c)) { - sb.append(' '); - sb.append(c); - } - - // append the algorithm - String alg = getAlgorithm(); - if (!StringUtils.isEmpty(alg)) { - sb.append(" (").append(alg).append(")"); - } fingerprint = sb.toString(); } return fingerprint; @@ -151,6 +134,29 @@ public class SshKey implements Serializable { @Override public String toString() { - return getFingerprint(); + if (toString == null) { + StringBuilder sb = new StringBuilder(); + // TODO append the keysize + int keySize = 0; + if (keySize > 0) { + sb.append(keySize).append(' '); + } + // append fingerprint + sb.append(' '); + sb.append(getFingerprint()); + // append the comment + String c = getComment(); + if (!StringUtils.isEmpty(c)) { + sb.append(' '); + sb.append(c); + } + // append algorithm + String alg = getAlgorithm(); + if (!StringUtils.isEmpty(alg)) { + sb.append(" (").append(alg).append(")"); + } + toString = sb.toString(); + } + return toString; } } diff --git a/src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java b/src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java index 52fa875d..b8c18a03 100644 --- a/src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java +++ b/src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java @@ -30,6 +30,8 @@ import com.gitblit.transport.ssh.SshKey; import com.gitblit.transport.ssh.commands.CommandMetaData; import com.gitblit.transport.ssh.commands.DispatchCommand; import com.gitblit.transport.ssh.commands.SshCommand; +import com.gitblit.utils.FlipTable; +import com.gitblit.utils.FlipTable.Borders; /** * The dispatcher and it's commands for SSH public key management. @@ -144,16 +146,32 @@ public class KeysDispatcher extends DispatchCommand { stdout.println("You have not registered any public keys for ssh authentication."); return; } + + if (showRaw) { + asRaw(keys); + } else { + asTable(keys); + } + } + + /* output in the same format as authorized_keys */ + protected void asRaw(List keys) { + for (SshKey key : keys) { + stdout.println(key.getRawData()); + } + } + + protected void asTable(List keys) { + String[] headers = { "#", "Fingerprint", "Comment", "Type" }; + String[][] data = new String[keys.size()][]; for (int i = 0; i < keys.size(); i++) { - if (showRaw) { - // output in the same format as authorized_keys - stdout.println(keys.get(i).getRawData()); - } else { - // show 1-based index numbers with the fingerprint - // this is useful for comparing with "ssh-add -l" - stdout.println("#" + (i + 1) + ": " + keys.get(i).getFingerprint()); - } + // show 1-based index numbers with the fingerprint + // this is useful for comparing with "ssh-add -l" + SshKey k = keys.get(i); + data[i] = new String[] { "" + (i + 1), k.getFingerprint(), k.getComment(), k.getAlgorithm() }; } + + stdout.println(FlipTable.of(headers, data, Borders.BODY_COLS)); } } } diff --git a/src/main/java/com/gitblit/transport/ssh/gitblit/ProjectsDispatcher.java b/src/main/java/com/gitblit/transport/ssh/gitblit/ProjectsDispatcher.java index cd687545..926d4806 100644 --- a/src/main/java/com/gitblit/transport/ssh/gitblit/ProjectsDispatcher.java +++ b/src/main/java/com/gitblit/transport/ssh/gitblit/ProjectsDispatcher.java @@ -15,12 +15,10 @@ */ package com.gitblit.transport.ssh.gitblit; -import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.List; import org.kohsuke.args4j.Option; -import org.parboiled.common.StringUtils; import com.gitblit.manager.IGitblit; import com.gitblit.models.ProjectModel; @@ -28,6 +26,8 @@ import com.gitblit.models.UserModel; import com.gitblit.transport.ssh.commands.CommandMetaData; import com.gitblit.transport.ssh.commands.DispatchCommand; import com.gitblit.transport.ssh.commands.SshCommand; +import com.gitblit.utils.FlipTable; +import com.gitblit.utils.FlipTable.Borders; @CommandMetaData(name = "projects", description = "Project management commands") public class ProjectsDispatcher extends DispatchCommand { @@ -44,36 +44,57 @@ public class ProjectsDispatcher extends DispatchCommand { @Option(name = "--verbose", aliases = { "-v" }, usage = "verbose") private boolean verbose; + @Option(name = "--tabbed", aliases = { "-t" }, usage = "as tabbed output") + private boolean tabbed; + @Override public void run() { IGitblit gitblit = getContext().getGitblit(); UserModel user = getContext().getClient().getUser(); - SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); List projects = gitblit.getProjectModels(user, false); - int nameLen = 0; - int descLen = 0; - for (ProjectModel project : projects) { - int len = project.name.length(); - if (len > nameLen) { - nameLen = len; - } - if (!StringUtils.isEmpty(project.description)) { - len = project.description.length(); - if (len > descLen) { - descLen = len; - } + + if (tabbed) { + asTabbed(projects); + } else { + asTable(projects); + } + } + + protected void asTable(List list) { + String[] headers; + if (verbose) { + String[] h = { "Name", "Description", "Last Modified", "# Repos" }; + headers = h; + } else { + String[] h = { "Name", "Description" }; + headers = h; + } + + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + String[][] data = new String[list.size()][]; + for (int i = 0; i < list.size(); i++) { + ProjectModel p = list.get(i); + + if (verbose) { + data[i] = new String[] { p.name, p.description, df.format(p.lastChange), "" + p.repositories.size() }; + } else { + data[i] = new String[] { p.name, p.description }; } } + stdout.println(FlipTable.of(headers, data, Borders.BODY_COLS)); + } + protected void asTabbed(List list) { String pattern; if (verbose) { - pattern = MessageFormat.format("%-{0,number,0}s\t%-{1,number,0}s\t%s", nameLen, descLen); + pattern = "%s\t%s\t%s"; } else { pattern = "%s"; } - for (ProjectModel project : projects) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + for (ProjectModel project : list) { stdout.println(String.format(pattern, project.name, project.description == null ? "" : project.description, diff --git a/src/main/java/com/gitblit/transport/ssh/gitblit/RepositoriesDispatcher.java b/src/main/java/com/gitblit/transport/ssh/gitblit/RepositoriesDispatcher.java index 3aaed54a..30f7393c 100644 --- a/src/main/java/com/gitblit/transport/ssh/gitblit/RepositoriesDispatcher.java +++ b/src/main/java/com/gitblit/transport/ssh/gitblit/RepositoriesDispatcher.java @@ -15,12 +15,10 @@ */ package com.gitblit.transport.ssh.gitblit; -import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.List; import org.kohsuke.args4j.Option; -import org.parboiled.common.StringUtils; import com.gitblit.manager.IGitblit; import com.gitblit.models.RepositoryModel; @@ -28,6 +26,10 @@ import com.gitblit.models.UserModel; import com.gitblit.transport.ssh.commands.CommandMetaData; import com.gitblit.transport.ssh.commands.DispatchCommand; import com.gitblit.transport.ssh.commands.SshCommand; +import com.gitblit.utils.ArrayUtils; +import com.gitblit.utils.FlipTable; +import com.gitblit.utils.FlipTable.Borders; +import com.google.common.base.Joiner; @CommandMetaData(name = "repositories", aliases = { "repos" }, description = "Repository management commands") public class RepositoriesDispatcher extends DispatchCommand { @@ -44,40 +46,77 @@ public class RepositoriesDispatcher extends DispatchCommand { @Option(name = "--verbose", aliases = { "-v" }, usage = "verbose") private boolean verbose; + @Option(name = "--tabbed", aliases = { "-t" }, usage = "as tabbed output") + private boolean tabbed; + @Override public void run() { IGitblit gitblit = getContext().getGitblit(); UserModel user = getContext().getClient().getUser(); - SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); List repositories = gitblit.getRepositoryModels(user); - int nameLen = 0; - int descLen = 0; - for (RepositoryModel repo : repositories) { - int len = repo.name.length(); - if (len > nameLen) { - nameLen = len; - } - if (!StringUtils.isEmpty(repo.description)) { - len = repo.description.length(); - if (len > descLen) { - descLen = len; + if (tabbed) { + asTabbed(repositories); + } else { + asTable(repositories); + } + } + + protected void asTable(List list) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + String[] headers; + if (verbose) { + String[] h = { "Name", "Description", "Owners", "Last Modified", "Size" }; + headers = h; + } else { + String[] h = { "Name", "Description" }; + headers = h; + } + + String[][] data = new String[list.size()][]; + for (int i = 0; i < list.size(); i++) { + RepositoryModel r = list.get(i); + + if (verbose) { + String lm = df.format(r.lastChange); + String owners = ""; + if (!ArrayUtils.isEmpty(r.owners)) { + owners = Joiner.on(",").join(r.owners); } + String size = r.size; + if (!r.hasCommits) { + size = "(empty)"; + } + data[i] = new String[] { r.name, r.description, owners, lm, size }; + } else { + data[i] = new String[] { r.name, r.description }; } } + stdout.println(FlipTable.of(headers, data, Borders.BODY_COLS)); + } + protected void asTabbed(List list) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); String pattern; if (verbose) { - pattern = MessageFormat.format("%-{0,number,0}s\t%-{1,number,0}s\t%s", nameLen, descLen); + pattern = "%s\t%s\t%s\t%s\t%s"; } else { pattern = "%s"; } - for (RepositoryModel repo : repositories) { - stdout.println(String.format(pattern, - repo.name, - repo.description == null ? "" : repo.description, - df.format(repo.lastChange))); + for (RepositoryModel r : list) { + String lm = df.format(r.lastChange); + String owners = ""; + if (!ArrayUtils.isEmpty(r.owners)) { + owners = Joiner.on(",").join(r.owners); + } + String size = r.size; + if (!r.hasCommits) { + size = "(empty)"; + } + + stdout.println(String.format(pattern, r.name, r.description == null ? "" : r.description, + owners, lm, size)); } } } diff --git a/src/main/java/com/gitblit/transport/ssh/gitblit/UsersDispatcher.java b/src/main/java/com/gitblit/transport/ssh/gitblit/UsersDispatcher.java index 0cbf3544..10ce26f0 100644 --- a/src/main/java/com/gitblit/transport/ssh/gitblit/UsersDispatcher.java +++ b/src/main/java/com/gitblit/transport/ssh/gitblit/UsersDispatcher.java @@ -15,12 +15,10 @@ */ package com.gitblit.transport.ssh.gitblit; -import java.text.MessageFormat; import java.util.List; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.Option; -import org.parboiled.common.StringUtils; import com.gitblit.manager.IGitblit; import com.gitblit.models.RegistrantAccessPermission; @@ -28,6 +26,8 @@ import com.gitblit.models.UserModel; import com.gitblit.transport.ssh.commands.CommandMetaData; import com.gitblit.transport.ssh.commands.DispatchCommand; import com.gitblit.transport.ssh.commands.SshCommand; +import com.gitblit.utils.FlipTable; +import com.gitblit.utils.FlipTable.Borders; @CommandMetaData(name = "users", description = "User management commands", admin = true) public class UsersDispatcher extends DispatchCommand { @@ -65,41 +65,66 @@ public class UsersDispatcher extends DispatchCommand { @Option(name = "--verbose", aliases = { "-v" }, usage = "verbose") private boolean verbose; + @Option(name = "--tabbed", aliases = { "-t" }, usage = "as tabbed output") + private boolean tabbed; + @Override public void run() { IGitblit gitblit = getContext().getGitblit(); List users = gitblit.getAllUsers(); - int displaynameLen = 0; - int usernameLen = 0; - for (UserModel user : users) { - int len = user.getDisplayName().length(); - if (len > displaynameLen) { - displaynameLen = len; - } - if (!StringUtils.isEmpty(user.username)) { - len = user.username.length(); - if (len > usernameLen) { - usernameLen = len; - } + + if (tabbed) { + asTabbed(users); + } else { + asTable(users); + } + } + + protected void asTable(List list) { + String[] headers; + if (verbose) { + String[] h = { "Name", "Display name", "Type", "E-mail", "Create?", "Fork?"}; + headers = h; + } else { + String[] h = { "Name", "Display name", "Type", "E-mail"}; + headers = h; + } + + String[][] data = new String[list.size()][]; + for (int i = 0; i < list.size(); i++) { + UserModel u = list.get(i); + + String name = u.disabled ? "-" : ((u.canAdmin() ? "*" : " ")) + u.username; + if (verbose) { + data[i] = new String[] { name, u.displayName == null ? "" : u.displayName, + u.accountType.name(), u.emailAddress == null ? "" : u.emailAddress , + u.canCreate() ? "Y":"", u.canFork() ? "Y" : ""}; + } else { + data[i] = new String[] { name, u.displayName == null ? "" : u.displayName, + u.accountType.name(), u.emailAddress == null ? "" : u.emailAddress }; } } + stdout.print(FlipTable.of(headers, data, Borders.BODY_COLS)); + stdout.println("* = admin account, - = disabled account"); + stdout.println(); + } + protected void asTabbed(List users) { String pattern; if (verbose) { - pattern = MessageFormat.format("%-{0,number,0}s\t%-{1,number,0}s\t%-10s\t%s", displaynameLen, usernameLen); + pattern = "%s\ts\t%s\t%s\t%s\t%s"; } else { - pattern = MessageFormat.format("%-{0,number,0}s\t%-{1,number,0}s", displaynameLen, usernameLen); + pattern = "%s"; } - for (UserModel user : users) { - if (user.disabled) { - continue; - } + for (UserModel u : users) { stdout.println(String.format(pattern, - user.getDisplayName(), - (user.canAdmin() ? "*":" ") + user.username, - user.accountType, - user.emailAddress == null ? "" : user.emailAddress)); + u.disabled ? "-" : ((u.canAdmin() ? "*" : " ")) + u.username, + u.getDisplayName(), + u.accountType, + u.emailAddress == null ? "" : u.emailAddress, + u.canCreate() ? "Y":"", + u.canFork() ? "Y" : "")); } } } diff --git a/src/main/java/com/gitblit/utils/FlipTable.java b/src/main/java/com/gitblit/utils/FlipTable.java new file mode 100644 index 00000000..1f788403 --- /dev/null +++ b/src/main/java/com/gitblit/utils/FlipTable.java @@ -0,0 +1,211 @@ +/* + * Copyright 2014 Jake Wharton + * Copyright 2014 gitblit.com. + * + * Licensed 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 com.gitblit.utils; + + +/** + * This is a forked version of FlipTables which supports controlling the + * displayed borders. + * + * FULL = all borders + * BODY_COLS = header + perimeter + column separators + * COLS = header + column separators + * BODY = header + perimeter + * HEADER = header only + * + *
+ * ╔═════════════╤════════════════════════════╤══════════════╗
+ * ║ Name        │ Function                   │ Author       ║
+ * ╠═════════════╪════════════════════════════╪══════════════╣
+ * ║ Flip Tables │ Pretty-print a text table. │ Jake Wharton ║
+ * ╚═════════════╧════════════════════════════╧══════════════╝
+ * 
+ */ +public final class FlipTable { + private static final String EMPTY = "(empty)"; + + public static enum Borders { + FULL(7), BODY_COLS(5), COLS(4), BODY(1), HEADER(0); + + final int bitmask; + + private Borders(int bitmask) { + this.bitmask = bitmask; + } + + boolean body() { + return isset(0x1); + } + + boolean rows() { + return isset(0x2); + } + + boolean columns() { + return isset(0x4); + } + + boolean isset(int v) { + return (bitmask & v) == v; + } + } + + /** Create a new table with the specified headers and row data. */ + public static String of(String[] headers, String[][] data) { + return of(headers, data, Borders.FULL); + } + + /** Create a new table with the specified headers and row data. */ + public static String of(String[] headers, String[][] data, Borders borders) { + if (headers == null) + throw new NullPointerException("headers == null"); + if (headers.length == 0) + throw new IllegalArgumentException("Headers must not be empty."); + if (data == null) + throw new NullPointerException("data == null"); + return new FlipTable(headers, data, borders).toString(); + } + + private final String[] headers; + private final String[][] data; + private final Borders borders; + private final int columns; + private final int[] columnWidths; + private final int emptyWidth; + + private FlipTable(String[] headers, String[][] data, Borders borders) { + this.headers = headers; + this.data = data; + this.borders = borders; + + columns = headers.length; + columnWidths = new int[columns]; + for (int row = -1; row < data.length; row++) { + String[] rowData = (row == -1) ? headers : data[row]; + if (rowData.length != columns) { + throw new IllegalArgumentException(String.format("Row %s's %s columns != %s columns", row + 1, + rowData.length, columns)); + } + for (int column = 0; column < columns; column++) { + for (String rowDataLine : rowData[column].split("\\n")) { + columnWidths[column] = Math.max(columnWidths[column], rowDataLine.length()); + } + } + } + + // Account for column dividers and their spacing. + int emptyWidth = 3 * (columns - 1); + for (int columnWidth : columnWidths) { + emptyWidth += columnWidth; + } + this.emptyWidth = emptyWidth; + + if (emptyWidth < EMPTY.length()) { + // Make sure we're wide enough for the empty text. + columnWidths[columns - 1] += EMPTY.length() - emptyWidth; + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + printDivider(builder, "╔═╤═╗"); + printData(builder, headers, true); + if (data.length == 0) { + if (borders.body()) { + printDivider(builder, "╠═╧═╣"); + builder.append('║').append(pad(emptyWidth, EMPTY)).append("║\n"); + printDivider(builder, "╚═══╝"); + } else { + printDivider(builder, "╚═╧═╝"); + builder.append(' ').append(pad(emptyWidth, EMPTY)).append(" \n"); + } + } else { + for (int row = 0; row < data.length; row++) { + if (row == 0) { + if (borders.body()) { + if (borders.columns()) { + printDivider(builder, "╠═╪═╣"); + } else { + printDivider(builder, "╠═╧═╣"); + } + } else { + if (borders.columns()) { + printDivider(builder, "╚═╪═╝"); + } else { + printDivider(builder, "╚═╧═╝"); + } + } + } else if (borders.rows()) { + if (borders.columns()) { + printDivider(builder, "╟─┼─╢"); + } else { + printDivider(builder, "╟─┼─╢"); + } + } + printData(builder, data[row], false); + } + if (borders.body()) { + if (borders.columns()) { + printDivider(builder, "╚═╧═╝"); + } else { + printDivider(builder, "╚═══╝"); + } + } + } + return builder.toString(); + } + + private void printDivider(StringBuilder out, String format) { + for (int column = 0; column < columns; column++) { + out.append(column == 0 ? format.charAt(0) : format.charAt(2)); + out.append(pad(columnWidths[column], "").replace(' ', format.charAt(1))); + } + out.append(format.charAt(4)).append('\n'); + } + + private void printData(StringBuilder out, String[] data, boolean isHeader) { + for (int line = 0, lines = 1; line < lines; line++) { + for (int column = 0; column < columns; column++) { + if (column == 0) { + if (isHeader || borders.body()) { + out.append('║'); + } else { + out.append(' '); + } + } else if (isHeader || borders.columns()) { + out.append('│'); + } else { + out.append(' '); + } + String[] cellLines = data[column].split("\\n"); + lines = Math.max(lines, cellLines.length); + String cellLine = line < cellLines.length ? cellLines[line] : ""; + out.append(pad(columnWidths[column], cellLine)); + } + if (isHeader || borders.body()) { + out.append("║\n"); + } else { + out.append('\n'); + } + } + } + + private static String pad(int width, String data) { + return String.format(" %1$-" + width + "s ", data); + } +}