You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by kr...@apache.org on 2019/09/27 20:18:43 UTC

[knox] branch master updated (61bdb98 -> 5c2c7a7)

This is an automated email from the ASF dual-hosted git repository.

krisden pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git.


    from 61bdb98  KNOX-2021 - Fix typo in gateway-docker/README.md (#155)
     new 8e7e9d9  KNOX-2022 - Splitting up KnoxShellTable and do minor cleanup
     new 5c2c7a7  KNOX-2022 - KnoxShellTable contains Comparables instead of Strings

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../apache/knox/gateway/shell/KnoxShellTable.java  | 658 ---------------------
 .../java/org/apache/knox/gateway/shell/Shell.java  |   1 +
 .../shell/table/CSVKnoxShellTableBuilder.java      |  72 +++
 .../shell/table/JDBCKnoxShellTableBuilder.java     | 118 ++++
 .../shell/table/JSONKnoxShellTableBuilder.java     |  50 ++
 .../shell/table/JoinKnoxShellTableBuilder.java     |  81 +++
 .../knox/gateway/shell/table/KnoxShellTable.java   | 170 ++++++
 .../gateway/shell/table/KnoxShellTableBuilder.java |  32 +-
 .../gateway/shell/table/KnoxShellTableCell.java    |  70 +++
 .../gateway/shell/table/KnoxShellTableFilter.java  |  85 +++
 .../shell/table/KnoxShellTableFilterException.java |  13 +-
 .../shell/table/KnoxShellTableRenderer.java        | 164 +++++
 .../shell/table/KnoxShellTableRowDeserializer.java |  76 +++
 .../shell/table/KnoxShellTableFilterTest.java      |  78 +++
 .../shell/{ => table}/KnoxShellTableTest.java      |  25 +-
 15 files changed, 996 insertions(+), 697 deletions(-)
 delete mode 100644 gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxShellTable.java
 create mode 100644 gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/CSVKnoxShellTableBuilder.java
 create mode 100644 gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JDBCKnoxShellTableBuilder.java
 create mode 100644 gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JSONKnoxShellTableBuilder.java
 create mode 100644 gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JoinKnoxShellTableBuilder.java
 create mode 100644 gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTable.java
 copy gateway-service-definitions/src/main/java/org/apache/knox/gateway/service/definition/DispatchParam.java => gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableBuilder.java (60%)
 create mode 100644 gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableCell.java
 create mode 100644 gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilter.java
 copy gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/YarnUIV2URLCreator.java => gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilterException.java (76%)
 create mode 100644 gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableRenderer.java
 create mode 100644 gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableRowDeserializer.java
 create mode 100644 gateway-shell/src/test/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilterTest.java
 rename gateway-shell/src/test/java/org/apache/knox/gateway/shell/{ => table}/KnoxShellTableTest.java (93%)


[knox] 01/02: KNOX-2022 - Splitting up KnoxShellTable and do minor cleanup

Posted by kr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

krisden pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git

commit 8e7e9d93bceb09fb77fe0bb77fdf01ab5ef6dbf7
Author: Sandor Molnar <sm...@apache.org>
AuthorDate: Thu Sep 26 17:27:16 2019 +0200

    KNOX-2022 - Splitting up KnoxShellTable and do minor cleanup
---
 .../apache/knox/gateway/shell/KnoxShellTable.java  | 658 ---------------------
 .../java/org/apache/knox/gateway/shell/Shell.java  |   1 +
 .../shell/table/CSVKnoxShellTableBuilder.java      |  72 +++
 .../shell/table/JDBCKnoxShellTableBuilder.java     | 117 ++++
 .../shell/table/JSONKnoxShellTableBuilder.java     |  44 ++
 .../shell/table/JoinKnoxShellTableBuilder.java     |  75 +++
 .../knox/gateway/shell/table/KnoxShellTable.java   | 169 ++++++
 .../gateway/shell/table/KnoxShellTableBuilder.java |  44 ++
 .../gateway/shell/table/KnoxShellTableCell.java    |  61 ++
 .../gateway/shell/table/KnoxShellTableFilter.java  |  58 ++
 .../shell/table/KnoxShellTableRenderer.java        | 164 +++++
 .../shell/{ => table}/KnoxShellTableTest.java      |  22 +-
 12 files changed, 816 insertions(+), 669 deletions(-)

diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxShellTable.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxShellTable.java
deleted file mode 100644
index dfbbf09..0000000
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxShellTable.java
+++ /dev/null
@@ -1,658 +0,0 @@
-/*
- * 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.knox.gateway.shell;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.net.URLConnection;
-import java.nio.charset.StandardCharsets;
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.regex.Pattern;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.knox.gateway.util.JsonUtils;
-
-import com.fasterxml.jackson.core.JsonFactory;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-/**
-* Simple table representation and text based rendering of a table via toString().
-* Headers are optional but when used must have the same count as columns
-* within the rows.
-*/
-public class KnoxShellTable {
-  private static int CELL_PAD_SIZE = 2;
-  private static char CELL_CORNER_CHAR = '+';
-  private static char CELL_WALL_CHAR = '|';
-  private static char CELL_DASH_CHAR = '-';
-
-  private List<String> headers = new ArrayList<String>();
-  private List<List<String>> rows = new ArrayList<List<String>>();
-  private String title;
-
-  public KnoxShellTable title(String title) {
-    this.title = title;
-    return this;
-  }
-
-  public KnoxShellTable header(String header) {
-    headers.add(header);
-    return this;
-  }
-
-  public KnoxShellTable row() {
-    List<String> row = new ArrayList<String>();
-    rows.add(row);
-    return this;
-  }
-
-  public KnoxShellTable value(String value) {
-    int index = rows.size() - 1;
-    if (index == -1) {
-      index = 0;
-    }
-    List<String> row = rows.get(index);
-    row.add(value);
-    return this;
-  }
-
-  public KnoxShellTableCell cell(int colIndex, int rowIndex) {
-    return new KnoxShellTableCell(colIndex, rowIndex);
-  }
-
-  public List<String> values(int colIndex) {
-    ArrayList<String> col = new ArrayList<String>();
-    rows.forEach(row -> col.add(row.get(colIndex)));
-    return col;
-  }
-
-  public List<String> values(String colName) {
-    int colIndex = headers.indexOf(colName);
-    ArrayList<String> col = new ArrayList<String>();
-    rows.forEach(row -> col.add(row.get(colIndex)));
-    return col;
-  }
-
-  public KnoxShellTable apply(KnoxShellTableCell cell) {
-    if (!headers.isEmpty()) {
-      headers.set(cell.colIndex, cell.header);
-    }
-    if (!rows.isEmpty()) {
-      rows.get(cell.rowIndex).set(cell.colIndex, cell.value);
-    }
-    return this;
-  }
-
-  public class KnoxShellTableCell {
-    private int colIndex;
-    private int rowIndex;
-    private String header;
-    private String value;
-
-    KnoxShellTableCell(int colIndex, int rowIndex) {
-      this.colIndex = colIndex;
-      this.rowIndex = rowIndex;
-      if (!headers.isEmpty()) {
-        this.header = headers.get(colIndex);
-      }
-      if (!rows.isEmpty()) {
-        this.value = rows.get(rowIndex).get(colIndex);
-      }
-    }
-
-    KnoxShellTableCell(String name, int rowIndex) {
-      this.rowIndex = rowIndex;
-      if (!headers.isEmpty()) {
-        this.header = name;
-        this.colIndex = headers.indexOf(name);
-      }
-      if (!rows.isEmpty()) {
-        this.value = rows.get(rowIndex).get(colIndex);
-      }
-    }
-
-    public KnoxShellTableCell value(String value) {
-      this.value = value;
-      return this;
-    }
-
-    public KnoxShellTableCell header(String name) {
-      this.header = name;
-      return this;
-    }
-
-    public String value() {
-      return this.value;
-    }
-
-    public String header() {
-      return this.header;
-    }
-  }
-
-  public List<String> getHeaders() {
-    if (headers.isEmpty()) {
-      return null;
-    }
-    return headers;
-  }
-
-  public List<List<String>> getRows() {
-    return rows;
-  }
-
-  public String getTitle() {
-    return title;
-  }
-
-  @Override
-  public String toString() {
-    if (!headers.isEmpty() && !rows.isEmpty() && headers.size() != rows.get(0).size()) {
-      throw new IllegalStateException("Number of columns and headers must be the same.");
-    }
-    StringBuilder sb = new StringBuilder();
-    Map<Integer, Integer> widthMap = getWidthMap();
-
-    if (title != null && !title.isEmpty()) {
-      sb.append(this.title);
-      newLine(sb, 1);
-    }
-    int colCount = 0;
-    if (!rows.isEmpty()) {
-      colCount = rows.get(0).size();
-    }
-    if (!headers.isEmpty()) {
-      colCount = headers.size();
-      createBorder(sb, colCount, widthMap);
-      newLine(sb, 1);
-
-      sb.append(CELL_WALL_CHAR);
-      for (int i = 0; i < colCount; i++) {
-        sb.append(centerString(widthMap.get(i) + 4, headers.get(i))).append(CELL_WALL_CHAR);
-      }
-      newLine(sb, 1);
-    }
-    createBorder(sb, colCount, widthMap);
-
-    for (List<String> row : rows) {
-      newLine(sb, 1);
-      sb.append(CELL_WALL_CHAR);
-      for (int i = 0; i < row.size(); i++) {
-        sb.append(centerString(widthMap.get(i) + 4, row.get(i))).append(CELL_WALL_CHAR);
-      }
-    }
-
-    newLine(sb, 1);
-    createBorder(sb, colCount, widthMap);
-    newLine(sb, 1);
-
-    return sb.toString();
-  }
-
-  private void newLine(StringBuilder sb, int count) {
-    for (int i = 0; i < count; i++) {
-      sb.append('\n');
-    }
-  }
-
-  private String centerString(int width, String s) {
-    s = ensureEvenLength(s);
-    return String.format(Locale.ROOT, "%-" + width + "s", String.format(Locale.ROOT, "%" + (s.length() + (width - s.length()) / 2) + "s", s));
-  }
-
-  private String ensureEvenLength(String s) {
-    if (s.length() % 2 != 0) {
-      s = s + " ";
-    }
-    return s;
-  }
-
-  private void createBorder(StringBuilder sb, int headerCount, Map<Integer, Integer> widthMap) {
-    for (int i = 0; i < headerCount; i++) {
-      if (i == 0) {
-        sb.append(CELL_CORNER_CHAR);
-      }
-
-      for (int j = 0; j < widthMap.get(i) + CELL_PAD_SIZE * 2; j++) {
-        sb.append(CELL_DASH_CHAR);
-      }
-      sb.append(CELL_CORNER_CHAR);
-    }
-  }
-
-  private Map<Integer, Integer> getWidthMap() {
-    Map<Integer, Integer> map = new HashMap<>();
-    String cellValue = null;
-    String headerValue = null;
-
-    // set max's to header sizes for each col
-    for (int i = 0; i < headers.size(); i++) {
-      headerValue = ensureEvenLength(headers.get(i));
-      map.put(i, headerValue.length());
-    }
-    // if there are any cell values longer than the header length set max to longest
-    // cell value length
-    for (List<String> row : rows) {
-      for (int i = 0; i < row.size(); i++) {
-        cellValue = ensureEvenLength(row.get(i));
-        if (map.get(i) == null || cellValue.length() > map.get(i)) {
-          map.put(i, cellValue.length());
-        }
-      }
-    }
-    return map;
-  }
-
-  public static KnoxShellTableBuilder builder() {
-    return new KnoxShellTableBuilder();
-  }
-
-  public static class KnoxShellTableBuilder {
-    protected String title;
-
-    public KnoxShellTableBuilder title(String title) {
-      this.title = title;
-      return this;
-    }
-
-    public CSVKnoxShellTableBuilder csv() {
-      return new CSVKnoxShellTableBuilder();
-    }
-
-    public JSONKnoxShellTableBuilder json() {
-      return new JSONKnoxShellTableBuilder();
-    }
-
-    public JoinKnoxShellTableBuilder join() {
-      return new JoinKnoxShellTableBuilder();
-    }
-
-    public JDBCKnoxShellTableBuilder jdbc() {
-      return new JDBCKnoxShellTableBuilder();
-    }
-  }
-
-  public static class JoinKnoxShellTableBuilder extends KnoxShellTableBuilder {
-    private KnoxShellTable left;
-    private KnoxShellTable right;
-    private int leftIndex = -1;
-    private int rightIndex = -1;
-
-    public JoinKnoxShellTableBuilder() {
-    }
-
-    @Override
-    public JoinKnoxShellTableBuilder title(String title) {
-      this.title = title;
-      return this;
-    }
-
-    public JoinKnoxShellTableBuilder left(KnoxShellTable left) {
-      this.left = left;
-      return this;
-    }
-
-    public JoinKnoxShellTableBuilder right(KnoxShellTable right) {
-      this.right = right;
-      return this;
-    }
-
-    public KnoxShellTable on(int leftIndex, int rightIndex) {
-      KnoxShellTable joined = new KnoxShellTable();
-      if (title != null) {
-        joined.title(title);
-      }
-
-      this.leftIndex = leftIndex;
-      this.rightIndex = rightIndex;
-
-      joined.headers.addAll(new ArrayList<String>(left.headers));
-      for (List<String> row : left.rows) {
-        joined.rows.add(new ArrayList<String>(row));
-      }
-      List<String> col = right.values(rightIndex);
-      ArrayList<String> row;
-      String leftKey;
-      int matchedIndex;
-
-      joined.headers.addAll(new ArrayList<String>(right.headers));
-      for (Iterator<List<String>> it = joined.rows.iterator(); it.hasNext();) {
-        row = (ArrayList<String>) it.next();
-        leftKey = row.get(leftIndex);
-        if (leftKey != null) {
-          matchedIndex = col.indexOf(leftKey);
-          if (matchedIndex > -1) {
-            row.addAll(right.rows.get(matchedIndex));
-          }
-          else {
-            it.remove();
-          }
-        }
-      }
-      return joined;
-    }
-  }
-
-  public static class JSONKnoxShellTableBuilder extends KnoxShellTableBuilder {
-    boolean withHeaders;
-
-    @Override
-    public JSONKnoxShellTableBuilder title(String title) {
-      this.title = title;
-      return this;
-    }
-
-    public JSONKnoxShellTableBuilder withHeaders() {
-      withHeaders = true;
-      return this;
-    }
-
-    public KnoxShellTable string(String json) throws IOException {
-      KnoxShellTable table = getKnoxShellTableFromJsonString(json);
-      return table;
-    }
-
-    public KnoxShellTable path(String path) throws IOException {
-      String json = FileUtils.readFileToString(new File(path), StandardCharsets.UTF_8);
-      KnoxShellTable table = getKnoxShellTableFromJsonString(json);
-      return table;
-    }
-
-    public KnoxShellTable getKnoxShellTableFromJsonString(String json) throws IOException {
-      KnoxShellTable table = null;
-      JsonFactory factory = new JsonFactory();
-      ObjectMapper mapper = new ObjectMapper(factory);
-      TypeReference<KnoxShellTable> typeRef
-            = new TypeReference<KnoxShellTable>() {};
-      table = mapper.readValue(json, typeRef);
-      if (title != null) {
-        table.title(title);
-      }
-      return table;
-    }
-  }
-
-  public static class CSVKnoxShellTableBuilder extends KnoxShellTableBuilder {
-    boolean withHeaders;
-
-    @Override
-    public CSVKnoxShellTableBuilder title(String title) {
-      this.title = title;
-      return this;
-    }
-
-    public CSVKnoxShellTableBuilder withHeaders() {
-      withHeaders = true;
-      return this;
-    }
-
-    public KnoxShellTable url(String url) throws IOException {
-      int rowIndex = 0;
-      URLConnection connection;
-      BufferedReader csvReader = null;
-      KnoxShellTable table = null;
-      try {
-        URL urlToCsv = new URL(url);
-        connection = urlToCsv.openConnection();
-        csvReader = new BufferedReader(new InputStreamReader(
-            connection.getInputStream(), StandardCharsets.UTF_8));
-        table = new KnoxShellTable();
-        if (title != null) {
-          table.title(title);
-        }
-        String row = null;
-        while ((row = csvReader.readLine()) != null) {
-            boolean addingHeaders = (withHeaders && rowIndex == 0);
-            if (!addingHeaders) {
-              table.row();
-            }
-            String[] data = row.split(",");
-
-            for (String value : data) {
-              if (addingHeaders) {
-                table.header(value);
-              }
-              else {
-                table.value(value);
-              }
-            }
-            rowIndex++;
-        }
-      }
-      finally {
-        csvReader.close();
-      }
-      return table;
-    }
-  }
-
-  public static class JDBCKnoxShellTableBuilder extends KnoxShellTableBuilder {
-    private String connect;
-    private String username;
-    private String pwd;
-    private String driver;
-    private Connection conn;
-    private boolean tableManagedConnection = true;
-
-    @Override
-    public JDBCKnoxShellTableBuilder title(String title) {
-      this.title = title;
-      return this;
-    }
-
-    public JDBCKnoxShellTableBuilder connect(String connect) {
-      this.connect = connect;
-      return this;
-    }
-
-    public JDBCKnoxShellTableBuilder username(String username) {
-      this.username = username;
-      return this;
-    }
-
-    public JDBCKnoxShellTableBuilder pwd(String pwd) {
-      this.pwd = pwd;
-      return this;
-    }
-
-    public JDBCKnoxShellTableBuilder driver(String driver) {
-      this.driver = driver;
-      return this;
-    }
-
-    public JDBCKnoxShellTableBuilder connection(Connection connection) {
-      this.conn = connection;
-      this.tableManagedConnection = false;
-      return this;
-    }
-
-    public KnoxShellTable sql(String sql) throws IOException, SQLException {
-      KnoxShellTable table = null;
-      Statement statement = null;
-      ResultSet result = null;
-      if (conn == null) {
-        conn = DriverManager.getConnection(connect);
-      }
-      try {
-        if (conn != null) {
-          statement = conn.createStatement();
-  //table.builder().jdbc().connect("jdbc:derby:codejava/webdb1").username("lmccay").password("xxxx").sql("SELECT * FROM book");
-          result = statement.executeQuery(sql);
-          table = new KnoxShellTable();
-          ResultSetMetaData metadata = result.getMetaData();
-          table.title(metadata.getTableName(1));
-          int colcount = metadata.getColumnCount();
-          for(int i = 1; i < colcount + 1; i++) {
-            table.header(metadata.getColumnName(i));
-          }
-          while (result.next()) {
-            table.row();
-            for(int i = 1; i < colcount + 1; i++) {
-              table.value(result.getString(metadata.getColumnName(i)));
-            }
-          }
-        }
-      }
-      finally {
-        result.close();
-        if (conn != null && tableManagedConnection) {
-          conn.close();
-        }
-        if (statement != null) {
-          statement.close();
-        }
-        if (result != null && !result.isClosed()) {
-          result.close();
-        }
-      }
-      return table;
-    }
-  }
-
-  public String toJSON() {
-    return JsonUtils.renderAsJsonString(this);
-  }
-
-  public String toCSV() {
-    StringBuilder csv = new StringBuilder();
-    String header;
-    for(int i = 0; i < headers.size(); i++) {
-      header = headers.get(i);
-      csv.append(header);
-      if (i < headers.size() - 1) {
-        csv.append(',');
-      }
-      else {
-        csv.append('\n');
-      }
-    }
-    for(List<String> row : rows) {
-      for(int ii = 0; ii < row.size(); ii++) {
-        csv.append(row.get(ii));
-        if (ii < row.size() - 1) {
-          csv.append(',');
-        }
-        else {
-          csv.append('\n');
-        }
-      }
-    }
-
-    return csv.toString();
-  }
-
-  public KnoxShellTable select(String cols) {
-    KnoxShellTable table = new KnoxShellTable();
-    List<ArrayList<String>> columns = new ArrayList<ArrayList<String>>();
-    String[] colnames = cols.split(",");
-    for (String colName : colnames) {
-      table.header(colName);
-      columns.add((ArrayList<String>) values(headers.indexOf(colName)));
-    }
-    for (int i = 0; i < rows.size(); i ++) {
-      table.row();
-      for (List<String> col : columns) {
-        table.value(col.get(i));
-      }
-    }
-    return table;
-  }
-
-  public KnoxShellTable sort(String colName) {
-    KnoxShellTable table = new KnoxShellTable();
-
-    String value;
-    List<String> col = values(colName);
-    List<RowIndex> index = new ArrayList<RowIndex>();
-    for (int i = 0; i < col.size(); i++) {
-      value = col.get(i);
-      index.add(new RowIndex(value, i));
-    }
-    Collections.sort(index);
-    table.headers = new ArrayList<String>(headers);
-    for (RowIndex i : index) {
-      table.rows.add(new ArrayList<String>(this.rows.get(i.index)));
-    }
-    return table;
-  }
-
-  public static class RowIndex implements Comparable<RowIndex> {
-    String value;
-    int index;
-
-    public RowIndex(String value, int index) {
-      this.value = value;
-      this.index = index;
-    }
-
-    @Override
-    public int compareTo(RowIndex other) {
-      return (this.value.compareTo(other.value));
-    }
-  }
-
-  public KnoxShellTableFilter filter() {
-    return new KnoxShellTableFilter();
-  }
-
-  public class KnoxShellTableFilter {
-    String name;
-    int index;
-
-    public KnoxShellTableFilter name(String name) {
-      this.name = name;
-      index = headers.indexOf(name);
-      return this;
-    }
-
-    public KnoxShellTableFilter index(int index) {
-      this.index = index;
-      return this;
-    }
-
-    public KnoxShellTable regex(String regex) {
-      KnoxShellTable table = new KnoxShellTable();
-      table.headers.addAll(headers);
-      for (List<String> row : rows) {
-        if (Pattern.matches(regex, row.get(index))) {
-          table.row();
-          row.forEach(value -> {
-            table.value(value);
-          });
-        }
-      }
-      return table;
-    }
-  }
-}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/Shell.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/Shell.java
index f0aaf41..a674979 100644
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/Shell.java
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/Shell.java
@@ -22,6 +22,7 @@ import org.apache.knox.gateway.shell.hbase.HBase;
 import org.apache.knox.gateway.shell.hdfs.Hdfs;
 import org.apache.knox.gateway.shell.job.Job;
 import org.apache.knox.gateway.shell.manager.Manager;
+import org.apache.knox.gateway.shell.table.KnoxShellTable;
 import org.apache.knox.gateway.shell.workflow.Workflow;
 import org.apache.knox.gateway.shell.yarn.Yarn;
 import org.apache.log4j.PropertyConfigurator;
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/CSVKnoxShellTableBuilder.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/CSVKnoxShellTableBuilder.java
new file mode 100644
index 0000000..db5a9e7
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/CSVKnoxShellTableBuilder.java
@@ -0,0 +1,72 @@
+/*
+ * 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.knox.gateway.shell.table;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+
+public class CSVKnoxShellTableBuilder extends KnoxShellTableBuilder {
+
+  private boolean withHeaders;
+
+  public CSVKnoxShellTableBuilder withHeaders() {
+    withHeaders = true;
+    return this;
+  }
+
+  public KnoxShellTable url(String url) throws IOException {
+    int rowIndex = 0;
+    URLConnection connection;
+    BufferedReader csvReader = null;
+    KnoxShellTable table = null;
+    try {
+      URL urlToCsv = new URL(url);
+      connection = urlToCsv.openConnection();
+      csvReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
+      table = new KnoxShellTable();
+      if (title != null) {
+        table.title(title);
+      }
+      String row = null;
+      while ((row = csvReader.readLine()) != null) {
+        boolean addingHeaders = (withHeaders && rowIndex == 0);
+        if (!addingHeaders) {
+          table.row();
+        }
+        String[] data = row.split(",");
+
+        for (String value : data) {
+          if (addingHeaders) {
+            table.header(value);
+          } else {
+            table.value(value);
+          }
+        }
+        rowIndex++;
+      }
+    } finally {
+      csvReader.close();
+    }
+    return table;
+  }
+
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JDBCKnoxShellTableBuilder.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JDBCKnoxShellTableBuilder.java
new file mode 100644
index 0000000..d8abaf1
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JDBCKnoxShellTableBuilder.java
@@ -0,0 +1,117 @@
+/*
+ * 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.knox.gateway.shell.table;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class JDBCKnoxShellTableBuilder extends KnoxShellTableBuilder {
+
+  private String connectionUrl;
+  private String driver;
+  private Connection conn;
+  private boolean tableManagedConnection = true;
+
+  @Override
+  public JDBCKnoxShellTableBuilder title(String title) {
+    this.title = title;
+    return this;
+  }
+
+  public JDBCKnoxShellTableBuilder connectTo(String connectionUrl) {
+    this.connectionUrl = connectionUrl;
+    return this;
+  }
+
+  public JDBCKnoxShellTableBuilder driver(String driver) throws Exception {
+    this.driver = driver;
+    loadDriver();
+    return this;
+  }
+
+  private void loadDriver() throws Exception {
+    try {
+      Class.forName(driver).newInstance();
+    } catch (ClassNotFoundException e) {
+      System.out.println(String.format("Unable to load the JDBC driver %s. Check your CLASSPATH.", driver));
+      throw e;
+    } catch (InstantiationException e) {
+      System.out.println(String.format("Unable to instantiate the JDBC driver %s", driver));
+      throw e;
+    } catch (IllegalAccessException e) {
+      System.out.println(String.format("Not allowed to access the JDBC driver %s", driver));
+      throw e;
+    }
+  }
+
+  public JDBCKnoxShellTableBuilder connection(Connection connection) {
+    this.conn = connection;
+    this.tableManagedConnection = false;
+    return this;
+  }
+
+  public KnoxShellTable sql(String sql) throws IOException, SQLException {
+    KnoxShellTable table = null;
+    conn = conn == null ? DriverManager.getConnection(connectionUrl) : conn;
+    if (conn != null) {
+      try (Statement statement = conn.createStatement(); ResultSet result = statement.executeQuery(sql);) {
+        table = new KnoxShellTable();
+        final ResultSetMetaData metadata = result.getMetaData();
+        table.title(metadata.getTableName(1));
+        int colcount = metadata.getColumnCount();
+        for (int i = 1; i < colcount + 1; i++) {
+          table.header(metadata.getColumnName(i));
+        }
+        while (result.next()) {
+          table.row();
+          for (int i = 1; i < colcount + 1; i++) {
+            table.value(result.getString(metadata.getColumnName(i)));
+          }
+        }
+      } finally {
+        if (conn != null && tableManagedConnection) {
+          conn.close();
+        }
+      }
+    }
+    return table;
+  }
+
+  public KnoxShellTable build(ResultSet resultSet) throws SQLException {
+    KnoxShellTable table = new KnoxShellTable();
+    ResultSetMetaData metadata = resultSet.getMetaData();
+    table.title(metadata.getTableName(1));
+    int colcount = metadata.getColumnCount();
+    for (int i = 1; i < colcount + 1; i++) {
+      table.header(metadata.getColumnName(i));
+    }
+    while (resultSet.next()) {
+      table.row();
+      for (int i = 1; i < colcount + 1; i++) {
+        table.value(resultSet.getString(metadata.getColumnName(i)));
+      }
+    }
+    return table;
+  }
+
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JSONKnoxShellTableBuilder.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JSONKnoxShellTableBuilder.java
new file mode 100644
index 0000000..1be5512
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JSONKnoxShellTableBuilder.java
@@ -0,0 +1,44 @@
+/*
+ * 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.knox.gateway.shell.table;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.commons.io.FileUtils;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class JSONKnoxShellTableBuilder extends KnoxShellTableBuilder {
+
+  public KnoxShellTable fromJson(String json) throws IOException {
+    final KnoxShellTable table = new ObjectMapper(new JsonFactory()).readValue(json, new TypeReference<KnoxShellTable>() {
+    });
+    if (title != null) {
+      table.title(title);
+    }
+    return table;
+  }
+
+  public KnoxShellTable path(String path) throws IOException {
+    return fromJson(FileUtils.readFileToString(new File(path), StandardCharsets.UTF_8));
+  }
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JoinKnoxShellTableBuilder.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JoinKnoxShellTableBuilder.java
new file mode 100644
index 0000000..bc504dd
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JoinKnoxShellTableBuilder.java
@@ -0,0 +1,75 @@
+/*
+ * 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.knox.gateway.shell.table;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class JoinKnoxShellTableBuilder extends KnoxShellTableBuilder {
+
+  private KnoxShellTable left;
+  private KnoxShellTable right;
+
+  @Override
+  public JoinKnoxShellTableBuilder title(String title) {
+    this.title = title;
+    return this;
+  }
+
+  public JoinKnoxShellTableBuilder left(KnoxShellTable left) {
+    this.left = left;
+    return this;
+  }
+
+  public JoinKnoxShellTableBuilder right(KnoxShellTable right) {
+    this.right = right;
+    return this;
+  }
+
+  public KnoxShellTable on(int leftIndex, int rightIndex) {
+    final KnoxShellTable joinedTable = new KnoxShellTable();
+    if (title != null) {
+      joinedTable.title(title);
+    }
+
+    joinedTable.headers.addAll(new ArrayList<String>(left.headers));
+    for (List<String> row : left.rows) {
+      joinedTable.rows.add(new ArrayList<String>(row));
+    }
+    ArrayList<String> row;
+    String leftKey;
+    int matchedIndex;
+
+    joinedTable.headers.addAll(new ArrayList<String>(right.headers));
+    for (Iterator<List<String>> it = joinedTable.rows.iterator(); it.hasNext();) {
+      row = (ArrayList<String>) it.next();
+      leftKey = row.get(leftIndex);
+      if (leftKey != null) {
+        matchedIndex = right.values(rightIndex).indexOf(leftKey);
+        if (matchedIndex > -1) {
+          row.addAll(right.rows.get(matchedIndex));
+        } else {
+          it.remove();
+        }
+      }
+    }
+    return joinedTable;
+  }
+
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTable.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTable.java
new file mode 100644
index 0000000..cf40a09
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTable.java
@@ -0,0 +1,169 @@
+/*
+ * 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.knox.gateway.shell.table;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.knox.gateway.util.JsonUtils;
+
+/**
+ * Simple table representation and text based rendering of a table via
+ * toString(). Headers are optional but when used must have the same count as
+ * columns within the rows.
+ */
+public class KnoxShellTable {
+
+  List<String> headers = new ArrayList<String>();
+  List<List<String>> rows = new ArrayList<List<String>>();
+  String title;
+
+  public KnoxShellTable title(String title) {
+    this.title = title;
+    return this;
+  }
+
+  public KnoxShellTable header(String header) {
+    headers.add(header);
+    return this;
+  }
+
+  public KnoxShellTable row() {
+    rows.add(new ArrayList<String>());
+    return this;
+  }
+
+  public KnoxShellTable value(String value) {
+    final int index = rows.isEmpty() ? 0 : rows.size() - 1;
+    final List<String> row = rows.get(index);
+    row.add(value);
+    return this;
+  }
+
+  public KnoxShellTableCell cell(int colIndex, int rowIndex) {
+    return new KnoxShellTableCell(headers, rows, colIndex, rowIndex);
+  }
+
+  public List<String> values(int colIndex) {
+    ArrayList<String> col = new ArrayList<String>();
+    rows.forEach(row -> col.add(row.get(colIndex)));
+    return col;
+  }
+
+  public List<String> values(String colName) {
+    int colIndex = headers.indexOf(colName);
+    ArrayList<String> col = new ArrayList<String>();
+    rows.forEach(row -> col.add(row.get(colIndex)));
+    return col;
+  }
+
+  public KnoxShellTable apply(KnoxShellTableCell cell) {
+    if (!headers.isEmpty()) {
+      headers.set(cell.colIndex, cell.header);
+    }
+    if (!rows.isEmpty()) {
+      rows.get(cell.rowIndex).set(cell.colIndex, cell.value);
+    }
+    return this;
+  }
+
+  public List<String> getHeaders() {
+    return headers == null || headers.isEmpty() ? null : headers;
+  }
+
+  public List<List<String>> getRows() {
+    return rows;
+  }
+
+  public String getTitle() {
+    return title;
+  }
+
+  public static KnoxShellTableBuilder builder() {
+    return new KnoxShellTableBuilder();
+  }
+
+  public KnoxShellTableFilter filter() {
+    return new KnoxShellTableFilter().table(this);
+  }
+
+  public KnoxShellTable select(String cols) {
+    KnoxShellTable table = new KnoxShellTable();
+    List<List<String>> columns = new ArrayList<List<String>>();
+    String[] colnames = cols.split(",");
+    for (String colName : colnames) {
+      table.header(colName);
+      columns.add((ArrayList<String>) values(headers.indexOf(colName)));
+    }
+    for (int i = 0; i < rows.size(); i++) {
+      table.row();
+      for (List<String> col : columns) {
+        table.value(col.get(i));
+      }
+    }
+    return table;
+  }
+
+  public KnoxShellTable sort(String colName) {
+    KnoxShellTable table = new KnoxShellTable();
+
+    String value;
+    List<String> col = values(colName);
+    List<RowIndex> index = new ArrayList<RowIndex>();
+    for (int i = 0; i < col.size(); i++) {
+      value = col.get(i);
+      index.add(new RowIndex(value, i));
+    }
+    Collections.sort(index);
+    table.headers = new ArrayList<String>(headers);
+    for (RowIndex i : index) {
+      table.rows.add(new ArrayList<String>(this.rows.get(i.index)));
+    }
+    return table;
+  }
+
+  private static class RowIndex implements Comparable<RowIndex> {
+    String value;
+    int index;
+
+    public RowIndex(String value, int index) {
+      this.value = value;
+      this.index = index;
+    }
+
+    @Override
+    public int compareTo(RowIndex other) {
+      return (this.value.compareTo(other.value));
+    }
+  }
+
+  @Override
+  public String toString() {
+    return new KnoxShellTableRenderer(this).toString();
+  }
+
+  public String toJSON() {
+    return JsonUtils.renderAsJsonString(this);
+  }
+
+  public String toCSV() {
+    return new KnoxShellTableRenderer(this).toCSV();
+  }
+
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableBuilder.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableBuilder.java
new file mode 100644
index 0000000..5dcc5af
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableBuilder.java
@@ -0,0 +1,44 @@
+/*
+ * 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.knox.gateway.shell.table;
+
+
+public class KnoxShellTableBuilder {
+  protected String title;
+
+  public KnoxShellTableBuilder title(String title) {
+    this.title = title;
+    return this;
+  }
+
+  public CSVKnoxShellTableBuilder csv() {
+    return new CSVKnoxShellTableBuilder();
+  }
+
+  public JSONKnoxShellTableBuilder json() {
+    return new JSONKnoxShellTableBuilder();
+  }
+
+  public JoinKnoxShellTableBuilder join() {
+    return new JoinKnoxShellTableBuilder();
+  }
+
+  public JDBCKnoxShellTableBuilder jdbc() {
+    return new JDBCKnoxShellTableBuilder();
+  }
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableCell.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableCell.java
new file mode 100644
index 0000000..d2e885d
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableCell.java
@@ -0,0 +1,61 @@
+/*
+ * 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.knox.gateway.shell.table;
+
+import java.util.List;
+
+class KnoxShellTableCell {
+  int rowIndex;
+  int colIndex;
+  String header;
+  String value;
+
+  KnoxShellTableCell(List<String> headers, List<List<String>> rows, int colIndex, int rowIndex) {
+    this.rowIndex = rowIndex;
+    this.colIndex = colIndex;
+    if (!headers.isEmpty()) {
+      this.header = headers.get(colIndex);
+    }
+    if (!rows.isEmpty()) {
+      this.value = rows.get(rowIndex).get(colIndex);
+    }
+  }
+
+  KnoxShellTableCell(List<String> headers, List<List<String>> rows, String name, int rowIndex) {
+    this.rowIndex = rowIndex;
+    if (!headers.isEmpty()) {
+      this.header = name;
+      this.colIndex = headers.indexOf(name);
+    }
+    if (!rows.isEmpty()) {
+      this.value = rows.get(rowIndex).get(colIndex);
+    }
+  }
+
+  KnoxShellTableCell value(String value) {
+    this.value = value;
+    return this;
+  }
+
+  KnoxShellTableCell header(String name) {
+    this.header = name;
+    return this;
+  }
+
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilter.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilter.java
new file mode 100644
index 0000000..3a462cc
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilter.java
@@ -0,0 +1,58 @@
+/*
+ * 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.knox.gateway.shell.table;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class KnoxShellTableFilter {
+
+  private KnoxShellTable tableToFilter;
+  private int index;
+
+  public KnoxShellTableFilter table(KnoxShellTable table) {
+    this.tableToFilter = table;
+    return this;
+  }
+
+  public KnoxShellTableFilter name(String name) {
+    index = tableToFilter == null ? -1 : tableToFilter.headers.indexOf(name);
+    return this;
+  }
+
+  public KnoxShellTableFilter index(int index) {
+    this.index = index;
+    return this;
+  }
+
+  public KnoxShellTable regex(String regex) {
+    final Pattern pattern = Pattern.compile(regex);
+    final KnoxShellTable filteredTable = new KnoxShellTable();
+    filteredTable.headers.addAll(tableToFilter.headers);
+    for (List<String> row : tableToFilter.rows) {
+      if (pattern.matcher(row.get(index)).matches()) {
+        filteredTable.row();
+        row.forEach(value -> {
+          filteredTable.value(value);
+        });
+      }
+    }
+    return filteredTable;
+  }
+
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableRenderer.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableRenderer.java
new file mode 100644
index 0000000..dd7295e
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableRenderer.java
@@ -0,0 +1,164 @@
+/*
+ * 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.knox.gateway.shell.table;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+
+class KnoxShellTableRenderer {
+
+  private static int CELL_PAD_SIZE = 2;
+  private static char CELL_CORNER_CHAR = '+';
+  private static char CELL_WALL_CHAR = '|';
+  private static char CELL_DASH_CHAR = '-';
+
+  private final KnoxShellTable tableToRender;
+
+  KnoxShellTableRenderer(KnoxShellTable tableToRender) {
+    this.tableToRender = tableToRender;
+  }
+
+  String toCSV() {
+    final StringBuilder csv = new StringBuilder();
+    String header;
+    for (int i = 0; i < tableToRender.headers.size(); i++) {
+      header = tableToRender.headers.get(i);
+      csv.append(header);
+      if (i < tableToRender.headers.size() - 1) {
+        csv.append(',');
+      } else {
+        csv.append('\n');
+      }
+    }
+    for (List<String> row : tableToRender.rows) {
+      for (int ii = 0; ii < row.size(); ii++) {
+        csv.append(row.get(ii));
+        if (ii < row.size() - 1) {
+          csv.append(',');
+        } else {
+          csv.append('\n');
+        }
+      }
+    }
+
+    return csv.toString();
+  }
+
+  @Override
+  public String toString() {
+    if (!tableToRender.headers.isEmpty() && !tableToRender.rows.isEmpty() && tableToRender.headers.size() != tableToRender.rows.get(0).size()) {
+      throw new IllegalStateException("Number of columns and headers must be the same.");
+    }
+    StringBuilder sb = new StringBuilder();
+    Map<Integer, Integer> widthMap = getWidthMap();
+
+    if (StringUtils.isNoneBlank(tableToRender.title)) {
+      sb.append(tableToRender.title);
+      newLine(sb, 1);
+    }
+    int colCount = 0;
+    if (!tableToRender.rows.isEmpty()) {
+      colCount = tableToRender.rows.get(0).size();
+    }
+    if (!tableToRender.headers.isEmpty()) {
+      colCount = tableToRender.headers.size();
+      createBorder(sb, colCount, widthMap);
+      newLine(sb, 1);
+
+      sb.append(CELL_WALL_CHAR);
+      for (int i = 0; i < colCount; i++) {
+        sb.append(centerString(widthMap.get(i) + 4, tableToRender.headers.get(i))).append(CELL_WALL_CHAR);
+      }
+      newLine(sb, 1);
+    }
+    createBorder(sb, colCount, widthMap);
+
+    for (List<String> row : tableToRender.rows) {
+      newLine(sb, 1);
+      sb.append(CELL_WALL_CHAR);
+      for (int i = 0; i < row.size(); i++) {
+        sb.append(centerString(widthMap.get(i) + 4, row.get(i))).append(CELL_WALL_CHAR);
+      }
+    }
+
+    newLine(sb, 1);
+    createBorder(sb, colCount, widthMap);
+    newLine(sb, 1);
+
+    return sb.toString();
+  }
+
+  private void newLine(StringBuilder sb, int count) {
+    for (int i = 0; i < count; i++) {
+      sb.append('\n');
+    }
+  }
+
+  private String centerString(int width, String s) {
+    s = ensureEvenLength(s);
+    return String.format(Locale.ROOT, "%-" + width + "s", String.format(Locale.ROOT, "%" + (s.length() + (width - s.length()) / 2) + "s", s));
+  }
+
+  private String ensureEvenLength(String s) {
+    if (s.length() % 2 != 0) {
+      s = s + " ";
+    }
+    return s;
+  }
+
+  private void createBorder(StringBuilder sb, int headerCount, Map<Integer, Integer> widthMap) {
+    for (int i = 0; i < headerCount; i++) {
+      if (i == 0) {
+        sb.append(CELL_CORNER_CHAR);
+      }
+
+      for (int j = 0; j < widthMap.get(i) + CELL_PAD_SIZE * 2; j++) {
+        sb.append(CELL_DASH_CHAR);
+      }
+      sb.append(CELL_CORNER_CHAR);
+    }
+  }
+
+  private Map<Integer, Integer> getWidthMap() {
+    Map<Integer, Integer> map = new HashMap<>();
+    String cellValue = null;
+    String headerValue = null;
+
+    // set max's to header sizes for each col
+    for (int i = 0; i < tableToRender.headers.size(); i++) {
+      headerValue = ensureEvenLength(tableToRender.headers.get(i));
+      map.put(i, headerValue.length());
+    }
+    // if there are any cell values longer than the header length set max to longest
+    // cell value length
+    for (List<String> row : tableToRender.rows) {
+      for (int i = 0; i < row.size(); i++) {
+        cellValue = ensureEvenLength(row.get(i));
+        if (map.get(i) == null || cellValue.length() > map.get(i)) {
+          map.put(i, cellValue.length());
+        }
+      }
+    }
+    return map;
+  }
+
+}
diff --git a/gateway-shell/src/test/java/org/apache/knox/gateway/shell/KnoxShellTableTest.java b/gateway-shell/src/test/java/org/apache/knox/gateway/shell/table/KnoxShellTableTest.java
similarity index 95%
rename from gateway-shell/src/test/java/org/apache/knox/gateway/shell/KnoxShellTableTest.java
rename to gateway-shell/src/test/java/org/apache/knox/gateway/shell/table/KnoxShellTableTest.java
index a2c7e0f..766a0ad 100644
--- a/gateway-shell/src/test/java/org/apache/knox/gateway/shell/KnoxShellTableTest.java
+++ b/gateway-shell/src/test/java/org/apache/knox/gateway/shell/table/KnoxShellTableTest.java
@@ -15,7 +15,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.knox.gateway.shell;
+package org.apache.knox.gateway.shell.table;
 
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
@@ -35,7 +35,8 @@ import java.sql.ResultSetMetaData;
 import java.sql.Statement;
 
 import org.apache.commons.io.FileUtils;
-import org.apache.knox.gateway.shell.KnoxShellTable.KnoxShellTableCell;
+import org.apache.knox.gateway.shell.table.KnoxShellTable;
+import org.apache.knox.gateway.shell.table.KnoxShellTableCell;
 import org.easymock.IAnswer;
 import org.junit.Test;
 
@@ -179,7 +180,7 @@ public class KnoxShellTableTest {
 
     String json = table.toJSON();
 
-    KnoxShellTable table2 = KnoxShellTable.builder().json().string(json);
+    KnoxShellTable table2 = KnoxShellTable.builder().json().fromJson(json);
     assertEquals(table.toString(), table2.toString());
   }
 
@@ -207,13 +208,13 @@ public class KnoxShellTableTest {
     table.row().value("789").value("012").value("844444444");
 
     KnoxShellTableCell cell = table.cell(1, 1);
-    assertEquals(cell.header(), "Column B");
-    assertEquals(cell.value(), "012");
+    assertEquals(cell.header, "Column B");
+    assertEquals(cell.value, "012");
     cell.header("Column Beeee");
     cell.value("234");
     table.apply(cell);
-    assertEquals(table.cell(1, 1).value(), "234");
-    assertEquals(table.cell(1, 1).header(), "Column Beeee");
+    assertEquals(table.cell(1, 1).value, "234");
+    assertEquals(table.cell(1, 1).header, "Column Beeee");
   }
 
   @Test
@@ -251,15 +252,15 @@ public class KnoxShellTableTest {
 
     assertEquals(joined.getRows().size(), 1);
     assertEquals(joined.getTitle(), "Joined Table");
-    assertEquals(joined.cell(0, 0).value(), "123");
+    assertEquals(joined.cell(0, 0).value, "123");
     String json = joined.toJSON();
 
-    KnoxShellTable zombie = KnoxShellTable.builder().json().string(json);
+    KnoxShellTable zombie = KnoxShellTable.builder().json().fromJson(json);
     zombie.title("Zombie Table");
 
     assertEquals(zombie.getRows().size(), 1);
     assertEquals(zombie.getTitle(), "Zombie Table");
-    assertEquals(zombie.cell(0, 0).value(), "123");
+    assertEquals(zombie.cell(0, 0).value, "123");
     KnoxShellTable joined2 = KnoxShellTable.builder().join().title("Joined Table 2").left(table).right(table2).on(1, 3);
     assertEquals(1, joined2.getRows().size());
   }
@@ -288,7 +289,6 @@ public class KnoxShellTableTest {
         return false;
       }
     }).times(2);
-    expect(resultSet.isClosed()).andReturn(true);
     expect(resultSet.getString("BOOK_ID")).andReturn("1").times(1);
     expect(resultSet.getString("TITLE")).andReturn("Apache Knox: The Definitive Guide").times(1);
     expect(metadata.getTableName(1)).andReturn("BOOK");


[knox] 02/02: KNOX-2022 - KnoxShellTable contains Comparables instead of Strings

Posted by kr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

krisden pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git

commit 5c2c7a727d8e88e47ce1d14cb803c9a9f4473c29
Author: Sandor Molnar <sm...@apache.org>
AuthorDate: Fri Sep 27 18:25:37 2019 +0200

    KNOX-2022 - KnoxShellTable contains Comparables instead of Strings
    
    Closes #157
    
    Signed-off-by: Kevin Risden <kr...@apache.org>
---
 .../shell/table/JDBCKnoxShellTableBuilder.java     |  9 +--
 .../shell/table/JSONKnoxShellTableBuilder.java     |  8 ++-
 .../shell/table/JoinKnoxShellTableBuilder.java     | 22 +++---
 .../knox/gateway/shell/table/KnoxShellTable.java   | 41 ++++++------
 .../gateway/shell/table/KnoxShellTableCell.java    | 23 +++++--
 .../gateway/shell/table/KnoxShellTableFilter.java  | 31 ++++++++-
 .../shell/table/KnoxShellTableFilterException.java | 26 ++++++++
 .../shell/table/KnoxShellTableRenderer.java        | 10 +--
 .../shell/table/KnoxShellTableRowDeserializer.java | 76 +++++++++++++++++++++
 .../shell/table/KnoxShellTableFilterTest.java      | 78 ++++++++++++++++++++++
 .../gateway/shell/table/KnoxShellTableTest.java    |  7 +-
 11 files changed, 280 insertions(+), 51 deletions(-)

diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JDBCKnoxShellTableBuilder.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JDBCKnoxShellTableBuilder.java
index d8abaf1..d958c5d 100644
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JDBCKnoxShellTableBuilder.java
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JDBCKnoxShellTableBuilder.java
@@ -24,6 +24,7 @@ import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.util.Locale;
 
 public class JDBCKnoxShellTableBuilder extends KnoxShellTableBuilder {
 
@@ -53,13 +54,13 @@ public class JDBCKnoxShellTableBuilder extends KnoxShellTableBuilder {
     try {
       Class.forName(driver).newInstance();
     } catch (ClassNotFoundException e) {
-      System.out.println(String.format("Unable to load the JDBC driver %s. Check your CLASSPATH.", driver));
+      System.out.println(String.format(Locale.US, "Unable to load the JDBC driver %s. Check your CLASSPATH.", driver));
       throw e;
     } catch (InstantiationException e) {
-      System.out.println(String.format("Unable to instantiate the JDBC driver %s", driver));
+      System.out.println(String.format(Locale.US, "Unable to instantiate the JDBC driver %s", driver));
       throw e;
     } catch (IllegalAccessException e) {
-      System.out.println(String.format("Not allowed to access the JDBC driver %s", driver));
+      System.out.println(String.format(Locale.US, "Not allowed to access the JDBC driver %s", driver));
       throw e;
     }
   }
@@ -85,7 +86,7 @@ public class JDBCKnoxShellTableBuilder extends KnoxShellTableBuilder {
         while (result.next()) {
           table.row();
           for (int i = 1; i < colcount + 1; i++) {
-            table.value(result.getString(metadata.getColumnName(i)));
+            table.value(result.getObject(metadata.getColumnName(i), Comparable.class));
           }
         }
       } finally {
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JSONKnoxShellTableBuilder.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JSONKnoxShellTableBuilder.java
index 1be5512..b2a67cf 100644
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JSONKnoxShellTableBuilder.java
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JSONKnoxShellTableBuilder.java
@@ -26,11 +26,17 @@ import org.apache.commons.io.FileUtils;
 import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
 
 public class JSONKnoxShellTableBuilder extends KnoxShellTableBuilder {
 
   public KnoxShellTable fromJson(String json) throws IOException {
-    final KnoxShellTable table = new ObjectMapper(new JsonFactory()).readValue(json, new TypeReference<KnoxShellTable>() {
+    final ObjectMapper mapper = new ObjectMapper(new JsonFactory());
+    final SimpleModule module = new SimpleModule();
+    module.addDeserializer(KnoxShellTable.class, new KnoxShellTableRowDeserializer());
+    mapper.registerModule(module);
+
+    final KnoxShellTable table = mapper.readValue(json, new TypeReference<KnoxShellTable>() {
     });
     if (title != null) {
       table.title(title);
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JoinKnoxShellTableBuilder.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JoinKnoxShellTableBuilder.java
index bc504dd..fc74a1d 100644
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JoinKnoxShellTableBuilder.java
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JoinKnoxShellTableBuilder.java
@@ -21,7 +21,7 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
-public class JoinKnoxShellTableBuilder extends KnoxShellTableBuilder {
+class JoinKnoxShellTableBuilder extends KnoxShellTableBuilder {
 
   private KnoxShellTable left;
   private KnoxShellTable right;
@@ -42,23 +42,29 @@ public class JoinKnoxShellTableBuilder extends KnoxShellTableBuilder {
     return this;
   }
 
-  public KnoxShellTable on(int leftIndex, int rightIndex) {
+  KnoxShellTable on(String columnName) {
+    final int leftIndex = left.headers.indexOf(columnName);
+    final int rightIndex = right.headers.indexOf(columnName);
+    return on(leftIndex, rightIndex);
+  }
+
+  KnoxShellTable on(int leftIndex, int rightIndex) {
     final KnoxShellTable joinedTable = new KnoxShellTable();
     if (title != null) {
       joinedTable.title(title);
     }
 
     joinedTable.headers.addAll(new ArrayList<String>(left.headers));
-    for (List<String> row : left.rows) {
-      joinedTable.rows.add(new ArrayList<String>(row));
+    for (List<Comparable<? extends Object>> row : left.rows) {
+      joinedTable.rows.add(new ArrayList<Comparable<? extends Object>>(row));
     }
-    ArrayList<String> row;
-    String leftKey;
+    List<Comparable<? extends Object>> row;
+    Comparable<? extends Object> leftKey;
     int matchedIndex;
 
     joinedTable.headers.addAll(new ArrayList<String>(right.headers));
-    for (Iterator<List<String>> it = joinedTable.rows.iterator(); it.hasNext();) {
-      row = (ArrayList<String>) it.next();
+    for (Iterator<List<Comparable<? extends Object>>> it = joinedTable.rows.iterator(); it.hasNext();) {
+      row = (List<Comparable<? extends Object>>) it.next();
       leftKey = row.get(leftIndex);
       if (leftKey != null) {
         matchedIndex = right.values(rightIndex).indexOf(leftKey);
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTable.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTable.java
index cf40a09..1fea1f9 100644
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTable.java
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTable.java
@@ -23,6 +23,7 @@ import java.util.List;
 
 import org.apache.knox.gateway.util.JsonUtils;
 
+
 /**
  * Simple table representation and text based rendering of a table via
  * toString(). Headers are optional but when used must have the same count as
@@ -31,7 +32,7 @@ import org.apache.knox.gateway.util.JsonUtils;
 public class KnoxShellTable {
 
   List<String> headers = new ArrayList<String>();
-  List<List<String>> rows = new ArrayList<List<String>>();
+  List<List<Comparable<? extends Object>>> rows = new ArrayList<List<Comparable<? extends Object>>>();
   String title;
 
   public KnoxShellTable title(String title) {
@@ -45,35 +46,35 @@ public class KnoxShellTable {
   }
 
   public KnoxShellTable row() {
-    rows.add(new ArrayList<String>());
+    rows.add(new ArrayList<Comparable<? extends Object>>());
     return this;
   }
 
-  public KnoxShellTable value(String value) {
+  public KnoxShellTable value(Comparable<? extends Object> value) {
     final int index = rows.isEmpty() ? 0 : rows.size() - 1;
-    final List<String> row = rows.get(index);
+    final List<Comparable<? extends Object>> row = rows.get(index);
     row.add(value);
     return this;
   }
 
-  public KnoxShellTableCell cell(int colIndex, int rowIndex) {
+  public KnoxShellTableCell<? extends Comparable<? extends Object>> cell(int colIndex, int rowIndex) {
     return new KnoxShellTableCell(headers, rows, colIndex, rowIndex);
   }
 
-  public List<String> values(int colIndex) {
-    ArrayList<String> col = new ArrayList<String>();
+  public List<Comparable<? extends Object>> values(int colIndex) {
+    List<Comparable<? extends Object>> col = new ArrayList<Comparable<? extends Object>>();
     rows.forEach(row -> col.add(row.get(colIndex)));
     return col;
   }
 
-  public List<String> values(String colName) {
+  public List<Comparable<? extends Object>> values(String colName) {
     int colIndex = headers.indexOf(colName);
-    ArrayList<String> col = new ArrayList<String>();
+    List<Comparable<? extends Object>> col = new ArrayList<Comparable<? extends Object>>();
     rows.forEach(row -> col.add(row.get(colIndex)));
     return col;
   }
 
-  public KnoxShellTable apply(KnoxShellTableCell cell) {
+  public KnoxShellTable apply(KnoxShellTableCell<? extends Comparable<? extends Object>> cell) {
     if (!headers.isEmpty()) {
       headers.set(cell.colIndex, cell.header);
     }
@@ -87,7 +88,7 @@ public class KnoxShellTable {
     return headers == null || headers.isEmpty() ? null : headers;
   }
 
-  public List<List<String>> getRows() {
+  public List<List<Comparable<? extends Object>>> getRows() {
     return rows;
   }
 
@@ -105,15 +106,15 @@ public class KnoxShellTable {
 
   public KnoxShellTable select(String cols) {
     KnoxShellTable table = new KnoxShellTable();
-    List<List<String>> columns = new ArrayList<List<String>>();
+    List<List<Comparable<? extends Object>>> columns = new ArrayList<List<Comparable<? extends Object>>>();
     String[] colnames = cols.split(",");
     for (String colName : colnames) {
       table.header(colName);
-      columns.add((ArrayList<String>) values(headers.indexOf(colName)));
+      columns.add(values(headers.indexOf(colName)));
     }
     for (int i = 0; i < rows.size(); i++) {
       table.row();
-      for (List<String> col : columns) {
+      for (List<Comparable<? extends Object>> col : columns) {
         table.value(col.get(i));
       }
     }
@@ -123,8 +124,8 @@ public class KnoxShellTable {
   public KnoxShellTable sort(String colName) {
     KnoxShellTable table = new KnoxShellTable();
 
-    String value;
-    List<String> col = values(colName);
+    Comparable<? extends Object> value;
+    List<Comparable<? extends Object>> col = values(colName);
     List<RowIndex> index = new ArrayList<RowIndex>();
     for (int i = 0; i < col.size(); i++) {
       value = col.get(i);
@@ -133,23 +134,23 @@ public class KnoxShellTable {
     Collections.sort(index);
     table.headers = new ArrayList<String>(headers);
     for (RowIndex i : index) {
-      table.rows.add(new ArrayList<String>(this.rows.get(i.index)));
+      table.rows.add(new ArrayList<Comparable<? extends Object>>(this.rows.get(i.index)));
     }
     return table;
   }
 
   private static class RowIndex implements Comparable<RowIndex> {
-    String value;
+    Comparable value;
     int index;
 
-    public RowIndex(String value, int index) {
+    RowIndex(Comparable<? extends Object> value, int index) {
       this.value = value;
       this.index = index;
     }
 
     @Override
     public int compareTo(RowIndex other) {
-      return (this.value.compareTo(other.value));
+      return this.value.compareTo(other.value);
     }
   }
 
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableCell.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableCell.java
index d2e885d..1fc0e40 100644
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableCell.java
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableCell.java
@@ -15,18 +15,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.knox.gateway.shell.table;
 
 import java.util.List;
 
-class KnoxShellTableCell {
+class KnoxShellTableCell<T extends Comparable<T>> implements Comparable<KnoxShellTableCell<T>>{
   int rowIndex;
   int colIndex;
   String header;
-  String value;
+  T value;
 
-  KnoxShellTableCell(List<String> headers, List<List<String>> rows, int colIndex, int rowIndex) {
+  KnoxShellTableCell(List<String> headers, List<List<T>> rows, int colIndex, int rowIndex) {
     this.rowIndex = rowIndex;
     this.colIndex = colIndex;
     if (!headers.isEmpty()) {
@@ -37,7 +36,7 @@ class KnoxShellTableCell {
     }
   }
 
-  KnoxShellTableCell(List<String> headers, List<List<String>> rows, String name, int rowIndex) {
+  KnoxShellTableCell(List<String> headers, List<List<T>> rows, String name, int rowIndex) {
     this.rowIndex = rowIndex;
     if (!headers.isEmpty()) {
       this.header = name;
@@ -48,14 +47,24 @@ class KnoxShellTableCell {
     }
   }
 
-  KnoxShellTableCell value(String value) {
+  KnoxShellTableCell<T> value(T value) {
     this.value = value;
     return this;
   }
 
-  KnoxShellTableCell header(String name) {
+  KnoxShellTableCell<T> header(String name) {
     this.header = name;
     return this;
   }
 
+  @Override
+  public String toString() {
+    return value.toString();
+  }
+
+  @Override
+  public int compareTo(KnoxShellTableCell<T> other) {
+    return value.compareTo(other.value);
+  }
+
 }
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilter.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilter.java
index 3a462cc..0ca5903 100644
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilter.java
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilter.java
@@ -18,6 +18,7 @@
 package org.apache.knox.gateway.shell.table;
 
 import java.util.List;
+import java.util.function.Predicate;
 import java.util.regex.Pattern;
 
 public class KnoxShellTableFilter {
@@ -40,12 +41,13 @@ public class KnoxShellTableFilter {
     return this;
   }
 
+  //TODO: this method can be eliminated when the 'equalTo' method is implemented: regex(regex) = equalTo(anyString)
   public KnoxShellTable regex(String regex) {
     final Pattern pattern = Pattern.compile(regex);
     final KnoxShellTable filteredTable = new KnoxShellTable();
     filteredTable.headers.addAll(tableToFilter.headers);
-    for (List<String> row : tableToFilter.rows) {
-      if (pattern.matcher(row.get(index)).matches()) {
+    for (List<Comparable<?>> row : tableToFilter.rows) {
+      if (pattern.matcher(row.get(index).toString()).matches()) {
         filteredTable.row();
         row.forEach(value -> {
           filteredTable.value(value);
@@ -55,4 +57,29 @@ public class KnoxShellTableFilter {
     return filteredTable;
   }
 
+  @SuppressWarnings("rawtypes")
+  private KnoxShellTable filter(Predicate<Comparable> p) throws KnoxShellTableFilterException {
+    try {
+    final KnoxShellTable filteredTable = new KnoxShellTable();
+    filteredTable.headers.addAll(tableToFilter.headers);
+    for (List<Comparable<? extends Object>> row : tableToFilter.rows) {
+      if (p.test(row.get(index))) {
+        filteredTable.row(); // Adds a new empty row to filtered table
+        // Add each value to the row
+        row.forEach(value -> {
+          filteredTable.value(value);
+        });
+      }
+    }
+    return filteredTable;
+    } catch (Exception e) {
+      throw new KnoxShellTableFilterException(e);
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  public KnoxShellTable greaterThan(Comparable<? extends Object> comparable) throws KnoxShellTableFilterException {
+    return filter(s -> s.compareTo(comparable) > 0);
+  }
+
 }
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilterException.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilterException.java
new file mode 100644
index 0000000..1966b8a
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilterException.java
@@ -0,0 +1,26 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.knox.gateway.shell.table;
+
+@SuppressWarnings("serial")
+class KnoxShellTableFilterException extends Exception {
+
+  KnoxShellTableFilterException(Throwable e) {
+    super("Error while filtering KnoxShellTable: " + e.getMessage());
+  }
+
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableRenderer.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableRenderer.java
index dd7295e..b38cac8 100644
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableRenderer.java
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableRenderer.java
@@ -49,7 +49,7 @@ class KnoxShellTableRenderer {
         csv.append('\n');
       }
     }
-    for (List<String> row : tableToRender.rows) {
+    for (List<Comparable<? extends Object>> row : tableToRender.rows) {
       for (int ii = 0; ii < row.size(); ii++) {
         csv.append(row.get(ii));
         if (ii < row.size() - 1) {
@@ -92,11 +92,11 @@ class KnoxShellTableRenderer {
     }
     createBorder(sb, colCount, widthMap);
 
-    for (List<String> row : tableToRender.rows) {
+    for (List<Comparable<? extends Object>> row : tableToRender.rows) {
       newLine(sb, 1);
       sb.append(CELL_WALL_CHAR);
       for (int i = 0; i < row.size(); i++) {
-        sb.append(centerString(widthMap.get(i) + 4, row.get(i))).append(CELL_WALL_CHAR);
+        sb.append(centerString(widthMap.get(i) + 4, row.get(i).toString())).append(CELL_WALL_CHAR);
       }
     }
 
@@ -150,9 +150,9 @@ class KnoxShellTableRenderer {
     }
     // if there are any cell values longer than the header length set max to longest
     // cell value length
-    for (List<String> row : tableToRender.rows) {
+    for (List<Comparable<? extends Object>> row : tableToRender.rows) {
       for (int i = 0; i < row.size(); i++) {
-        cellValue = ensureEvenLength(row.get(i));
+        cellValue = ensureEvenLength(row.get(i).toString());
         if (map.get(i) == null || cellValue.length() > map.get(i)) {
           map.put(i, cellValue.length());
         }
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableRowDeserializer.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableRowDeserializer.java
new file mode 100644
index 0000000..cd372d5
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/KnoxShellTableRowDeserializer.java
@@ -0,0 +1,76 @@
+/*
+ * 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.knox.gateway.shell.table;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.TreeNode;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+/**
+ * A custom @JsonDeserializer in order to be able to deserialize a previously
+ * serialzed @KnoxShellTable. It is requiored because Jackson is not capable of
+ * constructing instance of `java.lang.Comparable` (which we have int he table
+ * cells)
+ *
+ */
+@SuppressWarnings("serial")
+public class KnoxShellTableRowDeserializer extends StdDeserializer<KnoxShellTable> {
+
+  KnoxShellTableRowDeserializer() {
+    this(KnoxShellTable.class);
+  }
+
+  protected KnoxShellTableRowDeserializer(Class<?> vc) {
+    super(vc);
+  }
+
+  @Override
+  public KnoxShellTable deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
+    final TreeNode jsonContent = parser.readValueAsTree();
+    final KnoxShellTable table = new KnoxShellTable();
+    if (jsonContent.get("title").size() != 0) {
+      table.title(trimJSONQuotes(jsonContent.get("title").toString()));
+    }
+
+    final TreeNode headers = jsonContent.get("headers");
+    for (int i = 0; i < headers.size(); i++) {
+      table.header(trimJSONQuotes(headers.get(i).toString()));
+    }
+    final TreeNode rows = jsonContent.get("rows");
+    for (int i = 0; i < rows.size(); i++) {
+      TreeNode row = rows.get(i);
+      table.row();
+      for (int j = 0; j < row.size(); j++) {
+        table.value(trimJSONQuotes(row.get(j).toString()));
+      }
+    }
+    return table;
+  }
+
+  /*
+   * When serializing an object as JSON all elements within the table receive a surrounding quote pair (e.g. the cell contains myValue -> the JSON serialized string will be "myValue")
+   */
+  private String trimJSONQuotes(String toBeTrimmed) {
+    return toBeTrimmed.replaceAll("\"", "");
+  }
+
+}
diff --git a/gateway-shell/src/test/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilterTest.java b/gateway-shell/src/test/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilterTest.java
new file mode 100644
index 0000000..a30b22f
--- /dev/null
+++ b/gateway-shell/src/test/java/org/apache/knox/gateway/shell/table/KnoxShellTableFilterTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.knox.gateway.shell.table;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class KnoxShellTableFilterTest {
+  private static final DateFormat TEST_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
+
+  private static final KnoxShellTable TABLE = new KnoxShellTable();
+
+  @BeforeClass
+  public static void init() throws ParseException {
+    TABLE.header("Column Integer").header("Column Double").header("Column String").header("Column Date");
+    synchronized (TEST_DATE_FORMAT) { //sync is needed due to https://pmd.github.io/latest/pmd_rules_java_multithreading.html#unsynchronizedstaticformatter
+      TABLE.row().value(10).value(20d).value("text30").value(TEST_DATE_FORMAT.parse("2010-01-01"));
+      TABLE.row().value(30).value(40d).value("text50").value(TEST_DATE_FORMAT.parse("2012-01-01"));
+    }
+  }
+
+  @Test
+  public void testRegexFilter() throws KnoxShellTableFilterException {
+    final KnoxShellTable filtered = TABLE.filter().name("Column Integer").regex("30");
+    assertEquals(filtered.getRows().size(), 1);
+  }
+
+  @Test
+  public void testGreaterThanFilter() throws KnoxShellTableFilterException, ParseException {
+    assertEquals(TABLE.filter().name("Column Integer").greaterThan(5).getRows().size(), 2);
+    assertEquals(TABLE.filter().name("Column Integer").greaterThan(25).getRows().size(), 1);
+
+    assertEquals(TABLE.filter().name("Column Double").greaterThan(10d).getRows().size(), 2);
+    assertEquals(TABLE.filter().name("Column Double").greaterThan(30d).getRows().size(), 1);
+
+    assertEquals(TABLE.filter().name("Column String").greaterThan("text20").getRows().size(), 2);
+    assertEquals(TABLE.filter().name("Column String").greaterThan("text30").getRows().size(), 1);
+
+    synchronized (TEST_DATE_FORMAT) {
+      assertEquals(TABLE.filter().name("Column Date").greaterThan(TEST_DATE_FORMAT.parse("2009-12-31")).getRows().size(), 2);
+      assertEquals(TABLE.filter().name("Column Date").greaterThan(TEST_DATE_FORMAT.parse("2011-01-01")).getRows().size(), 1);
+    }
+  }
+
+  @Test
+  public void testIncomparableColumns() {
+    boolean failed = false;
+    try {
+      TABLE.filter().name("Column Double").greaterThan("20");
+    } catch (KnoxShellTableFilterException e) {
+      failed = true;
+    }
+    assertTrue(failed);
+  }
+}
diff --git a/gateway-shell/src/test/java/org/apache/knox/gateway/shell/table/KnoxShellTableTest.java b/gateway-shell/src/test/java/org/apache/knox/gateway/shell/table/KnoxShellTableTest.java
index 766a0ad..3141afe 100644
--- a/gateway-shell/src/test/java/org/apache/knox/gateway/shell/table/KnoxShellTableTest.java
+++ b/gateway-shell/src/test/java/org/apache/knox/gateway/shell/table/KnoxShellTableTest.java
@@ -35,8 +35,6 @@ import java.sql.ResultSetMetaData;
 import java.sql.Statement;
 
 import org.apache.commons.io.FileUtils;
-import org.apache.knox.gateway.shell.table.KnoxShellTable;
-import org.apache.knox.gateway.shell.table.KnoxShellTableCell;
 import org.easymock.IAnswer;
 import org.junit.Test;
 
@@ -199,6 +197,7 @@ public class KnoxShellTableTest {
   }
 
   @Test
+  @SuppressWarnings({ "rawtypes", "unchecked" })
   public void testCells() throws IOException {
     KnoxShellTable table = new KnoxShellTable();
 
@@ -289,8 +288,8 @@ public class KnoxShellTableTest {
         return false;
       }
     }).times(2);
-    expect(resultSet.getString("BOOK_ID")).andReturn("1").times(1);
-    expect(resultSet.getString("TITLE")).andReturn("Apache Knox: The Definitive Guide").times(1);
+    expect(resultSet.getObject("BOOK_ID", Comparable.class)).andReturn("1").times(1);
+    expect(resultSet.getObject("TITLE", Comparable.class)).andReturn("Apache Knox: The Definitive Guide").times(1);
     expect(metadata.getTableName(1)).andReturn("BOOK");
     expect(metadata.getColumnCount()).andReturn(2);
     expect(metadata.getColumnName(1)).andReturn("BOOK_ID").anyTimes();