Mercurial > hg > release > thermostat-1.6
changeset 1960:7e9e2f81b86c
Add sorting ability to TableRenderer
For backport compatibility purposes, new functionality is added in
SortedTableRenderer instead
Reviewed-by: omajid, jkang, jerboaa
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-July/014712.html
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-June/019718.html
PR3040
line wrap: on
line diff
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/AgentListFormatter.java Thu May 12 11:17:00 2016 -0400 +++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/AgentListFormatter.java Tue Jul 21 16:10:45 2015 -0400 @@ -40,7 +40,7 @@ import java.text.DateFormat; import java.util.Date; -import com.redhat.thermostat.common.cli.TableRenderer; +import com.redhat.thermostat.common.cli.SortedTableRenderer; import com.redhat.thermostat.shared.locale.Translate; import com.redhat.thermostat.storage.model.AgentInformation; @@ -55,10 +55,10 @@ private static final String START_TIME = translator.localize(LocaleResources.START_TIME).getContents(); private static final String STOP_TIME = translator.localize(LocaleResources.STOP_TIME).getContents(); - private final TableRenderer tableRenderer = new TableRenderer(NUM_COLUMNS); + private final SortedTableRenderer tableRenderer = new SortedTableRenderer(NUM_COLUMNS); void addHeader() { - printLine(AGENT_ID, CONFIG_LISTEN_ADDRESS, START_TIME, STOP_TIME); + tableRenderer.printHeader(AGENT_ID, CONFIG_LISTEN_ADDRESS, START_TIME, STOP_TIME); } void addAgent(AgentInformation info) {
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMListFormatter.java Thu May 12 11:17:00 2016 -0400 +++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMListFormatter.java Tue Jul 21 16:10:45 2015 -0400 @@ -38,7 +38,7 @@ import java.io.PrintStream; -import com.redhat.thermostat.common.cli.TableRenderer; +import com.redhat.thermostat.common.cli.SortedTableRenderer; import com.redhat.thermostat.shared.locale.Translate; import com.redhat.thermostat.storage.core.VmRef; import com.redhat.thermostat.storage.model.AgentInformation; @@ -60,14 +60,19 @@ private static final String STATUS_EXITED = translator.localize(LocaleResources.VM_STATUS_EXITED).getContents(); private static final String STATUS_UNKNOWN = translator.localize(LocaleResources.VM_STATUS_UNKNOWN).getContents(); - private final TableRenderer tableRenderer = new TableRenderer(NUM_COLUMNS); + private final SortedTableRenderer tableRenderer = new SortedTableRenderer(NUM_COLUMNS); + + VMListFormatter() { + tableRenderer.sortByColumn(0); + tableRenderer.sortByColumn(2); + } void addVM(VmRef vm, AgentInformation agentInfo, VmInfo info) { printVM(vm, agentInfo, info); } void addHeader() { - printLine(HOST_ID, HOST, VM_ID, VM_PID, VM_STATUS, VM_NAME); + tableRenderer.printHeader(HOST_ID, HOST, VM_ID, VM_PID, VM_STATUS, VM_NAME); } void format(PrintStream out) {
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java Thu May 12 11:17:00 2016 -0400 +++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java Tue Jul 21 16:10:45 2015 -0400 @@ -50,6 +50,7 @@ import com.redhat.thermostat.client.cli.VMStatPrintDelegate; import com.redhat.thermostat.common.OrderedComparator; +import com.redhat.thermostat.common.cli.SortedTableRenderer; import com.redhat.thermostat.common.cli.TableRenderer; import com.redhat.thermostat.shared.locale.Translate; import com.redhat.thermostat.storage.core.VmRef; @@ -67,7 +68,7 @@ private List<VMStatPrintDelegate> delegates; private PrintStream out; private TimeStampedPojoCorrelator correlator; - private TableRenderer table; + private SortedTableRenderer table; private int numCols; private Map<VMStatPrintDelegate, DelegateInfo> delegateInfo; @@ -148,7 +149,7 @@ private void printStats(List<List<? extends TimeStampedPojo>> allStats, List<String> headers) { correlate(allStats); numCols = headers.size(); - table = new TableRenderer(numCols); + table = new SortedTableRenderer(numCols); printHeaders(table, headers); printUpdatedStatsImpl(); } @@ -193,8 +194,8 @@ table.printLine(line); } - private void printHeaders(TableRenderer table, List<String> headers) { - table.printLine(headers.toArray(new String[headers.size()])); + private void printHeaders(SortedTableRenderer table, List<String> headers) { + table.printHeader(headers.toArray(new String[headers.size()])); } private void correlate(List<List<? extends TimeStampedPojo>> allStats) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/main/java/com/redhat/thermostat/common/cli/SortedTableRenderer.java Tue Jul 21 16:10:45 2015 -0400 @@ -0,0 +1,101 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.common.cli; + +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class SortedTableRenderer extends TableRenderer { + + private String[] header; + private List<Integer> columnSortingQueue = new ArrayList<>(); + + public SortedTableRenderer(int numColumns, int minWidth) { + super(numColumns, minWidth); + } + + public SortedTableRenderer(int numColumns) { + super(numColumns); + } + + @Override + public void printLine(String... line) { + checkLine(line); + super.printLine(line); + } + + public void printHeader(String... line) { + checkLine(line); + this.header = line; + } + + @Override + public void render(PrintStream out) { + if (lastPrintedLine == -1 && header != null) { + renderLine(out, header); + } + sortLines(); + super.render(out); + } + + private void sortLines() { + Collections.sort(lines, new Comparator<String[]>() { + @Override + public int compare(final String[] lines1, final String[] lines2) { + int comparison = 0; + for (Integer column : columnSortingQueue) { + comparison = lines1[column].compareTo(lines2[column]); + if (comparison != 0) { + break; + } + } + return comparison; + } + }); + } + + public void sortByColumn(int column) { + if (column < numColumns) { + columnSortingQueue.add(column); + } else { + throw new IllegalArgumentException("Invalid number of columns: " + column + ", expected: " + numColumns); + } + } +}
--- a/common/core/src/main/java/com/redhat/thermostat/common/cli/TableRenderer.java Thu May 12 11:17:00 2016 -0400 +++ b/common/core/src/main/java/com/redhat/thermostat/common/cli/TableRenderer.java Tue Jul 21 16:10:45 2015 -0400 @@ -43,12 +43,12 @@ public class TableRenderer { - private List<String[]> lines; - private int[] maxColumnWidths; - private int lastPrintedLine = -1; + protected List<String[]> lines; + protected int[] maxColumnWidths; + protected int lastPrintedLine = -1; - private int numColumns; - private int minWidth; + protected int numColumns; + protected int minWidth; public TableRenderer(int numColumns) { this(numColumns, 1); @@ -62,10 +62,14 @@ } public void printLine(String... line) { + checkLine(line); + lines.add(line); + } + + protected void checkLine(String... line) { if (line.length != numColumns) { throw new IllegalArgumentException("Invalid number of columns: " + line.length + ", expected: " + numColumns); } - lines.add(line); for (int i = 0; i < numColumns; i++) { maxColumnWidths[i] = Math.max(Math.max(maxColumnWidths[i], line[i].length()), minWidth); } @@ -84,7 +88,7 @@ } } - private void renderLine(PrintStream out, String[] line) { + protected void renderLine(PrintStream out, String[] line) { for (int i = 0; i < numColumns; i++) { out.print(line[i]); padOrNewline(out, line, i); @@ -92,7 +96,7 @@ } } - private void padOrNewline(PrintStream out, String[] line, int i) { + protected void padOrNewline(PrintStream out, String[] line, int i) { if (i < numColumns - 1) { int pad = maxColumnWidths[i] - line[i].length() + 1; fillSpaces(out, pad); @@ -101,7 +105,7 @@ } } - private void fillSpaces(PrintStream out, int pad) { + protected void fillSpaces(PrintStream out, int pad) { for (int i = 0; i < pad; i++) { out.print(" "); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/test/java/com/redhat/thermostat/common/cli/SortedTableRendererTest.java Tue Jul 21 16:10:45 2015 -0400 @@ -0,0 +1,208 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.common.cli; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; + +import static org.junit.Assert.assertEquals; + +public class SortedTableRendererTest { + + private SortedTableRenderer tableRenderer; + private ByteArrayOutputStream out; + + @Before + public void setUp() { + tableRenderer = new SortedTableRenderer(3); + out = new ByteArrayOutputStream(); + } + + @After + public void tearDown() { + out = null; + tableRenderer = null; + } + + @Test + public void testSingleLine() { + tableRenderer.printLine("hello", "fluff", "world"); + tableRenderer.render(out); + assertEquals("hello fluff world\n", new String(out.toByteArray())); + } + + @Test + public void testMultiLine() { + tableRenderer.printLine("hello", "fluff", "world"); + tableRenderer.printLine("looooooong", "f1", "foobar"); + tableRenderer.printLine("f2", "shoooooooooooort", "poo"); + tableRenderer.render(out); + assertEquals("hello fluff world\n" + + "looooooong f1 foobar\n" + + "f2 shoooooooooooort poo\n", new String(out.toByteArray())); + } + + @Test + public void testMultiLineContinuous() { + tableRenderer.printHeader("TITLE", "TIME", "PLACE"); + tableRenderer.printLine("hello", "fluff", "world"); + tableRenderer.printLine("looooooong", "f1", "foobar"); + tableRenderer.printLine("f2", "shoooooooooooort", "poo"); + tableRenderer.render(out); + assertEquals("TITLE TIME PLACE\n" + + "hello fluff world\n" + + "looooooong f1 foobar\n" + + "f2 shoooooooooooort poo\n", new String(out.toByteArray())); + tableRenderer.printLine("f3", "foobar", "poo"); + tableRenderer.render(out); + assertEquals("TITLE TIME PLACE\n" + + "hello fluff world\n" + + "looooooong f1 foobar\n" + + "f2 shoooooooooooort poo\n" + + "f3 foobar poo\n", new String(out.toByteArray())); + } + + @Test + public void testMultiLineSorting() { + tableRenderer.sortByColumn(0); + tableRenderer.sortByColumn(2); + tableRenderer.sortByColumn(1); + tableRenderer.printLine("animal", "brown", "bear"); + tableRenderer.printLine("animal", "black", "bear"); + tableRenderer.printLine("animal", "grey", "rhino"); + tableRenderer.printLine("animal", "aqua-green", "turtle"); + tableRenderer.printLine("animal", "polar", "bear"); + tableRenderer.printLine("animal", "green", "alligator"); + tableRenderer.printLine("animal", "white", "rabbit"); + tableRenderer.printLine("animal", "brown", "alligator"); + tableRenderer.printLine("animal", "brown", "rabbit"); + tableRenderer.printLine("animal", "brown", "rat"); + tableRenderer.printLine("bird", "red", "parrot"); + tableRenderer.printLine("bird", "green", "parrot"); + tableRenderer.printLine("bird", "blue", "parrot"); + tableRenderer.printLine("bird", "red", "ostrich"); + tableRenderer.printLine("bird", "yellow", "rooster"); + tableRenderer.printLine("bird", "tuxedo", "penguin"); + tableRenderer.printLine("fish", "yellow", "trout"); + tableRenderer.printLine("fish", "rainbow", "trout"); + tableRenderer.printLine("fish", "golden", "trout"); + tableRenderer.printLine("fish", "tiger", "trout"); + + tableRenderer.render(out); + assertEquals("animal brown alligator\n" + + "animal green alligator\n" + + "animal black bear\n" + + "animal brown bear\n" + + "animal polar bear\n" + + "animal brown rabbit\n" + + "animal white rabbit\n" + + "animal brown rat\n" + + "animal grey rhino\n" + + "animal aqua-green turtle\n" + + "bird red ostrich\n" + + "bird blue parrot\n" + + "bird green parrot\n" + + "bird red parrot\n" + + "bird tuxedo penguin\n" + + "bird yellow rooster\n" + + "fish golden trout\n" + + "fish rainbow trout\n" + + "fish tiger trout\n" + + "fish yellow trout\n", new String(out.toByteArray())); + } + + @Test + public void testHeader() { + tableRenderer.printHeader("HEADER", "TITLE", "SUBTITLE"); + tableRenderer.render(out); + assertEquals("HEADER TITLE SUBTITLE\n", new String(out.toByteArray())); + } + + @Test + public void testHeaderWithLine() { + tableRenderer.printHeader("HEADER", "TITLE", "SUBTITLE"); + tableRenderer.printLine("hello", "foo", "subtitle"); + tableRenderer.render(out); + assertEquals("HEADER TITLE SUBTITLE\n" + + "hello foo subtitle\n", new String(out.toByteArray())); + } + + @Test + public void testHeaderNotSet() { + tableRenderer.printLine("No", "Header", "Present"); + tableRenderer.render(out); + assertEquals("No Header Present\n", new String(out.toByteArray())); + } + + @Test(expected=IllegalArgumentException.class) + public void testMultiLineSortingWithColumnsOutOfRange() { + tableRenderer.sortByColumn(3); + tableRenderer.sortByColumn(7); + tableRenderer.sortByColumn(1); + tableRenderer.printLine("animal", "brown", "bear"); + tableRenderer.printLine("animal", "black", "bear"); + tableRenderer.printLine("animal", "grey", "rhino"); + tableRenderer.printLine("animal", "aqua-green", "turtle"); + tableRenderer.printLine("animal", "polar", "bear"); + tableRenderer.printLine("animal", "green", "alligator"); + tableRenderer.printLine("animal", "white", "rabbit"); + tableRenderer.printLine("animal", "brown", "alligator"); + tableRenderer.printLine("animal", "brown", "rabbit"); + tableRenderer.printLine("animal", "brown", "rat"); + tableRenderer.printLine("bird", "red", "parrot"); + tableRenderer.printLine("bird", "green", "parrot"); + tableRenderer.printLine("bird", "blue", "parrot"); + tableRenderer.printLine("bird", "red", "ostrich"); + tableRenderer.printLine("bird", "yellow", "rooster"); + tableRenderer.printLine("bird", "tuxedo", "penguin"); + tableRenderer.printLine("fish", "yellow", "trout"); + tableRenderer.printLine("fish", "rainbow", "trout"); + tableRenderer.printLine("fish", "golden", "trout"); + tableRenderer.printLine("fish", "tiger", "trout"); + + tableRenderer.render(out); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidLine() { + tableRenderer.printLine("hello", "fluff", "world", "boom"); + } + +}
--- a/vm-heap-analysis/command/src/main/java/com/redhat/thermostat/vm/heap/analysis/command/internal/FindObjectsCommand.java Thu May 12 11:17:00 2016 -0400 +++ b/vm-heap-analysis/command/src/main/java/com/redhat/thermostat/vm/heap/analysis/command/internal/FindObjectsCommand.java Tue Jul 21 16:10:45 2015 -0400 @@ -39,6 +39,7 @@ import java.util.Collection; import java.util.List; +import com.redhat.thermostat.common.cli.SortedTableRenderer; import com.redhat.thermostat.vm.heap.analysis.common.HeapDump; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; @@ -47,7 +48,6 @@ import com.redhat.thermostat.common.cli.AbstractCommand; import com.redhat.thermostat.common.cli.CommandContext; import com.redhat.thermostat.common.cli.CommandException; -import com.redhat.thermostat.common.cli.TableRenderer; import com.redhat.thermostat.shared.locale.Translate; import com.redhat.thermostat.vm.heap.analysis.command.locale.LocaleResources; import com.redhat.thermostat.vm.heap.analysis.common.HeapDAO; @@ -97,8 +97,8 @@ int limit = parseLimit(limitArg); Collection<String> results = heapDump.searchObjects(searchTerm, limit); - TableRenderer table = new TableRenderer(2); - table.printLine(HEADER_OBJECT_ID, HEADER_TYPE); + SortedTableRenderer table = new SortedTableRenderer(2); + table.printHeader(HEADER_OBJECT_ID, HEADER_TYPE); for (String objectId : results) { JavaHeapObject obj = heapDump.findObject(objectId); String id = obj.getIdString();
--- a/vm-heap-analysis/command/src/main/java/com/redhat/thermostat/vm/heap/analysis/command/internal/ListHeapDumpsCommand.java Thu May 12 11:17:00 2016 -0400 +++ b/vm-heap-analysis/command/src/main/java/com/redhat/thermostat/vm/heap/analysis/command/internal/ListHeapDumpsCommand.java Tue Jul 21 16:10:45 2015 -0400 @@ -40,6 +40,7 @@ import java.util.Collection; import java.util.Date; +import com.redhat.thermostat.common.cli.SortedTableRenderer; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; @@ -84,9 +85,9 @@ public void run(CommandContext ctx) throws CommandException { HostVMArguments args = new HostVMArguments(ctx.getArguments(), false, false); - TableRenderer renderer = new TableRenderer(4); + SortedTableRenderer renderer = new SortedTableRenderer(4); - renderer.printLine(COLUMN_NAMES); + renderer.printHeader(COLUMN_NAMES); ServiceReference hostDAORef = context.getServiceReference(HostInfoDAO.class.getName()); requireNonNull(hostDAORef, translator.localize(LocaleResources.HOST_SERVICE_UNAVAILABLE));
--- a/vm-heap-analysis/command/src/main/java/com/redhat/thermostat/vm/heap/analysis/command/internal/ShowHeapHistogramCommand.java Thu May 12 11:17:00 2016 -0400 +++ b/vm-heap-analysis/command/src/main/java/com/redhat/thermostat/vm/heap/analysis/command/internal/ShowHeapHistogramCommand.java Tue Jul 21 16:10:45 2015 -0400 @@ -38,6 +38,7 @@ import java.io.PrintStream; +import com.redhat.thermostat.common.cli.SortedTableRenderer; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; @@ -46,7 +47,6 @@ import com.redhat.thermostat.common.cli.Arguments; import com.redhat.thermostat.common.cli.CommandContext; import com.redhat.thermostat.common.cli.CommandException; -import com.redhat.thermostat.common.cli.TableRenderer; import com.redhat.thermostat.shared.locale.Translate; import com.redhat.thermostat.vm.heap.analysis.command.locale.LocaleResources; import com.redhat.thermostat.vm.heap.analysis.common.HeapDAO; @@ -100,8 +100,8 @@ } private void printHeapHistogram(ObjectHistogram histogram, PrintStream out) { - TableRenderer table = new TableRenderer(3); - table.printLine(translator.localize(LocaleResources.TABLE_CLASS_NAME).getContents(), + SortedTableRenderer table = new SortedTableRenderer(3); + table.printHeader(translator.localize(LocaleResources.TABLE_CLASS_NAME).getContents(), translator.localize(LocaleResources.TABLE_NUMBER_INSTANCES).getContents(), translator.localize(LocaleResources.TABLE_TOTAL_SIZE).getContents()); for (HistogramRecord rec : histogram.getHistogram()) {
--- a/vm-profiler/client-cli/src/main/java/com/redhat/thermostat/vm/profiler/client/cli/internal/ProfileResultFormatter.java Thu May 12 11:17:00 2016 -0400 +++ b/vm-profiler/client-cli/src/main/java/com/redhat/thermostat/vm/profiler/client/cli/internal/ProfileResultFormatter.java Tue Jul 21 16:10:45 2015 -0400 @@ -38,7 +38,7 @@ import java.io.OutputStream; -import com.redhat.thermostat.common.cli.TableRenderer; +import com.redhat.thermostat.common.cli.SortedTableRenderer; import com.redhat.thermostat.shared.locale.Translate; import com.redhat.thermostat.vm.profiler.client.core.ProfilingResult.MethodInfo; @@ -52,10 +52,10 @@ private static final String HEADER_TIME = translator.localize(LocaleResources.METHOD_PROFILE_HEADER_TIME).getContents(); private static final String HEADER_NAME = translator.localize(LocaleResources.METHOD_PROFILE_HEADER_NAME).getContents(); - private TableRenderer renderer = new TableRenderer(NUM_COLUMNS); + private SortedTableRenderer renderer = new SortedTableRenderer(NUM_COLUMNS); public void addHeader() { - printLine(HEADER_PERCENTAGE, HEADER_TIME, HEADER_NAME); + renderer.printHeader(HEADER_PERCENTAGE, HEADER_TIME, HEADER_NAME); } public void addMethodInfo(MethodInfo methodInfo) {