James Moger
2014-03-22 4a69998080bd01180c5e46e4f4fd2c50586fea80
Integrate a patched version of FlipTables and improve ls output
1 files added
5 files modified
508 ■■■■ changed files
src/main/java/com/gitblit/transport/ssh/SshKey.java 46 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java 36 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/ProjectsDispatcher.java 57 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/RepositoriesDispatcher.java 83 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/UsersDispatcher.java 75 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/utils/FlipTable.java 211 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/SshKey.java
@@ -28,6 +28,8 @@
    private String fingerprint;
    private String toString;
    public SshKey(String data) {
        this.rawData = data;
    }
@@ -96,12 +98,6 @@
    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 @@
                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 @@
    @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;
    }
}
src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java
@@ -30,6 +30,8 @@
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 @@
                stdout.println("You have not registered any public keys for ssh authentication.");
                return;
            }
            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());
                }
            if (showRaw) {
                asRaw(keys);
            } else {
                asTable(keys);
            }
        }
        /* output in the same format as authorized_keys */
        protected void asRaw(List<SshKey> keys) {
            for (SshKey key : keys) {
                stdout.println(key.getRawData());
            }
        }
        protected void asTable(List<SshKey> keys) {
            String[] headers = { "#", "Fingerprint", "Comment", "Type" };
            String[][] data = new String[keys.size()][];
            for (int i = 0; i < keys.size(); i++) {
                // 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));
        }
    }
}
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.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 @@
        @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<ProjectModel> 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<ProjectModel> 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<ProjectModel> 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,
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.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 @@
        @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<RepositoryModel> 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<RepositoryModel> 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<RepositoryModel> 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));
            }
        }
    }
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.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 @@
        @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<UserModel> 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<UserModel> 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<UserModel> 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" : ""));
            }
        }
    }
src/main/java/com/gitblit/utils/FlipTable.java
New file
@@ -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
 *
 * <pre>
 * ╔═════════════╤════════════════════════════╤══════════════╗
 * ║ Name        │ Function                   │ Author       ║
 * ╠═════════════╪════════════════════════════╪══════════════╣
 * ║ Flip Tables │ Pretty-print a text table. │ Jake Wharton ║
 * ╚═════════════╧════════════════════════════╧══════════════╝
 * </pre>
 */
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);
    }
}