Integrate a patched version of FlipTables and improve ls output
1 files added
5 files modified
| | |
| | | |
| | | private String fingerprint; |
| | | |
| | | private String toString; |
| | | |
| | | public SshKey(String data) { |
| | | this.rawData = data; |
| | | } |
| | |
| | | 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) { |
| | |
| | | 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; |
| | |
| | | |
| | | @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; |
| | | } |
| | | } |
| | |
| | | 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. |
| | |
| | | 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()); |
| | | 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" |
| | | stdout.println("#" + (i + 1) + ": " + keys.get(i).getFingerprint()); |
| | | 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)); |
| | | } |
| | | } |
| | | } |
| | |
| | | */ |
| | | 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; |
| | |
| | | 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 { |
| | |
| | | @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, |
| | |
| | | */ |
| | | 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; |
| | |
| | | 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 { |
| | |
| | | @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)); |
| | | } |
| | | } |
| | | } |
| | |
| | | */ |
| | | 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; |
| | |
| | | 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 { |
| | |
| | | @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" : "")); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | /* |
| | | * 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); |
| | | } |
| | | } |