Tabulate crash report mod list and add signature information (#4251)
This commit is contained in:
parent
a40df67004
commit
43e24c3eb4
4 changed files with 364 additions and 5 deletions
|
@ -414,6 +414,11 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
|
|||
all.add(asm.getClassName());
|
||||
for (ASMData asm : evt.getASMHarvestedData().getAll(ICrashCallable.class.getName().replace('.', '/')))
|
||||
all.add(asm.getClassName());
|
||||
// Add table classes for mod list tabulation
|
||||
all.add("net/minecraftforge/common/util/TextTable");
|
||||
all.add("net/minecraftforge/common/util/TextTable$Column");
|
||||
all.add("net/minecraftforge/common/util/TextTable$Row");
|
||||
all.add("net/minecraftforge/common/util/TextTable$Alignment");
|
||||
|
||||
all.removeIf(cls -> !cls.startsWith("net/minecraft/") && !cls.startsWith("net/minecraftforge/"));
|
||||
|
||||
|
|
220
src/main/java/net/minecraftforge/common/util/TextTable.java
Normal file
220
src/main/java/net/minecraftforge/common/util/TextTable.java
Normal file
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package net.minecraftforge.common.util;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Utility to format data into a textual (markdown-compliant) table.
|
||||
*/
|
||||
public class TextTable
|
||||
{
|
||||
public static Column column(String header)
|
||||
{
|
||||
return new Column(header);
|
||||
}
|
||||
|
||||
public static Column column(String header, Alignment alignment)
|
||||
{
|
||||
return new Column(header, alignment);
|
||||
}
|
||||
|
||||
private final List<Column> columns;
|
||||
private final List<Row> rows = new ArrayList<>();
|
||||
|
||||
public TextTable(List<Column> columns)
|
||||
{
|
||||
this.columns = columns;
|
||||
}
|
||||
|
||||
public String build(String lineEnding)
|
||||
{
|
||||
StringBuilder destination = new StringBuilder();
|
||||
append(destination, lineEnding);
|
||||
return destination.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the data formatted as a table to the given string builder.
|
||||
* The padding character used for the column alignments is a single space (' '),
|
||||
* the separate between column headers and values is a dash ('-').
|
||||
* Note that you *have* to specify a line ending, '\n' isn't used by default.
|
||||
* <p>
|
||||
* The generated table is compliant with the markdown file format.
|
||||
*
|
||||
* @param destination a string builder to append the table to
|
||||
* @param lineEnding the line ending to use for each row of the table
|
||||
*/
|
||||
public void append(StringBuilder destination, String lineEnding)
|
||||
{
|
||||
List<String> headers = columns.stream().map(c -> c.formatHeader(" ")).collect(Collectors.toList());
|
||||
printRow(destination, headers);
|
||||
destination.append(lineEnding);
|
||||
printSeparators(destination);
|
||||
for (Row row : rows)
|
||||
{
|
||||
destination.append(lineEnding);
|
||||
printRow(destination, row.format(columns, " "));
|
||||
}
|
||||
}
|
||||
|
||||
private void printSeparators(StringBuilder destination)
|
||||
{
|
||||
destination.append('|');
|
||||
for (Column column : columns)
|
||||
{
|
||||
destination.append(column.alignment != Alignment.RIGHT ? ':' : ' ');
|
||||
destination.append(column.getSeparator('-'));
|
||||
destination.append(column.alignment != Alignment.LEFT ? ':' : ' ');
|
||||
destination.append('|');
|
||||
}
|
||||
}
|
||||
|
||||
private void printRow(StringBuilder destination, List<String> values)
|
||||
{
|
||||
destination.append('|');
|
||||
for (String value : values)
|
||||
{
|
||||
destination.append(' ');
|
||||
destination.append(value);
|
||||
destination.append(' ');
|
||||
destination.append('|');
|
||||
}
|
||||
}
|
||||
|
||||
public void add(@Nonnull Object... values)
|
||||
{
|
||||
if (values.length != columns.size())
|
||||
{
|
||||
throw new IllegalArgumentException("Received wrong amount of values for table row, expected " + columns.size() + ", received " + columns.size() + ".");
|
||||
}
|
||||
Row row = new Row();
|
||||
for (int i = 0; i < values.length; i++)
|
||||
{
|
||||
String value = Objects.toString(values[i]);
|
||||
row.values.add(value);
|
||||
columns.get(i).fit(value);
|
||||
}
|
||||
rows.add(row);
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
for (Column column : columns)
|
||||
{
|
||||
column.resetWidth();
|
||||
}
|
||||
rows.clear();
|
||||
}
|
||||
|
||||
public List<Column> getColumns()
|
||||
{
|
||||
return Collections.unmodifiableList(columns);
|
||||
}
|
||||
|
||||
public static class Column
|
||||
{
|
||||
private String header;
|
||||
private int width;
|
||||
private Alignment alignment;
|
||||
|
||||
public Column(String header)
|
||||
{
|
||||
this(header, Alignment.LEFT);
|
||||
}
|
||||
|
||||
public Column(String header, Alignment alignment)
|
||||
{
|
||||
this.header = header;
|
||||
this.width = header.length();
|
||||
this.alignment = alignment;
|
||||
}
|
||||
|
||||
public String formatHeader(String padding)
|
||||
{
|
||||
return format(header, padding);
|
||||
}
|
||||
|
||||
public String format(String value, String padding)
|
||||
{
|
||||
switch (alignment)
|
||||
{
|
||||
case LEFT:
|
||||
return StringUtils.rightPad(value, width, padding);
|
||||
case RIGHT:
|
||||
return StringUtils.leftPad(value, width, padding);
|
||||
default:
|
||||
int length = value.length();
|
||||
int left = (width - length) / 2;
|
||||
int leftWidth = left + length;
|
||||
return StringUtils.rightPad(StringUtils.leftPad(value, leftWidth, padding), width, padding);
|
||||
}
|
||||
}
|
||||
|
||||
public String getSeparator(char character)
|
||||
{
|
||||
return StringUtils.leftPad("", width, character);
|
||||
}
|
||||
|
||||
public void fit(String value)
|
||||
{
|
||||
if (value.length() > width)
|
||||
{
|
||||
width = value.length();
|
||||
}
|
||||
}
|
||||
|
||||
public void resetWidth()
|
||||
{
|
||||
this.width = header.length();
|
||||
}
|
||||
|
||||
public int getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Row
|
||||
{
|
||||
private final ArrayList<String> values = new ArrayList<>();
|
||||
|
||||
public List<String> format(List<Column> columns, String padding)
|
||||
{
|
||||
if (columns.size() != values.size())
|
||||
{
|
||||
throw new IllegalArgumentException("Received wrong amount of columns for table row, expected " + columns.size() + ", received " + columns.size() + ".");
|
||||
}
|
||||
return Streams.zip(values.stream(), columns.stream(), (v, c) -> c.format(v, padding)).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
public enum Alignment
|
||||
{
|
||||
LEFT, CENTER, RIGHT
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ import java.util.Map;
|
|||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraftforge.common.util.TextTable;
|
||||
import net.minecraftforge.fml.common.LoaderState.ModState;
|
||||
import net.minecraftforge.fml.common.ProgressManager.ProgressBar;
|
||||
import net.minecraftforge.fml.common.event.FMLEvent;
|
||||
|
@ -310,14 +311,28 @@ public class LoadController
|
|||
for (ModState state : ModState.values())
|
||||
ret.append(" '").append(state.getMarker()).append("' = ").append(state.toString());
|
||||
|
||||
TextTable table = new TextTable(Lists.newArrayList(
|
||||
TextTable.column("State"),
|
||||
TextTable.column("ID"),
|
||||
TextTable.column("Version"),
|
||||
TextTable.column("Source"),
|
||||
TextTable.column("Signature"))
|
||||
);
|
||||
for (ModContainer mc : loader.getModList())
|
||||
{
|
||||
ret.append("\n\t");
|
||||
for (ModState state : modStates.get(mc.getModId()))
|
||||
ret.append(state.getMarker());
|
||||
|
||||
ret.append("\t").append(mc.getModId()).append("{").append(mc.getVersion()).append("} [").append(mc.getName()).append("] (").append(mc.getSource().getName()).append(") ");
|
||||
table.add(
|
||||
modStates.get(mc.getModId()).stream().map(ModState::getMarker).reduce("", (a, b) -> a + b),
|
||||
mc.getModId(),
|
||||
mc.getVersion(),
|
||||
mc.getSource().getName(),
|
||||
mc.getSigningCertificate() != null ? CertificateHelper.getFingerprint(mc.getSigningCertificate()) : "None"
|
||||
);
|
||||
}
|
||||
|
||||
ret.append("\n");
|
||||
ret.append("\n\t");
|
||||
table.append(ret, "\n\t");
|
||||
ret.append("\n");
|
||||
}
|
||||
|
||||
public List<ModContainer> getActiveModList()
|
||||
|
|
119
src/test/java/net/minecraftforge/test/TextTableTest.java
Normal file
119
src/test/java/net/minecraftforge/test/TextTableTest.java
Normal file
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.test;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraftforge.common.util.TextTable;
|
||||
import net.minecraftforge.common.util.TextTable.Column;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static net.minecraftforge.common.util.TextTable.column;
|
||||
|
||||
public class TextTableTest
|
||||
{
|
||||
private static final String WIDTH_REFERENCE = "StringOfWidth15";
|
||||
private static final int WIDTH_REFERENCE_LENGTH = WIDTH_REFERENCE.length();
|
||||
|
||||
@Test
|
||||
public void testColumnWidthAdjustment()
|
||||
{
|
||||
Column column = column("Column", TextTable.Alignment.LEFT);
|
||||
column.fit(WIDTH_REFERENCE);
|
||||
String paddedHeader = column.formatHeader("-");
|
||||
Assert.assertEquals("Formatted column header didn't have correct length", WIDTH_REFERENCE_LENGTH, paddedHeader.length());
|
||||
Assert.assertEquals("Formatted column header wasn't padded properly", "Column---------", paddedHeader);
|
||||
|
||||
String paddedReference = column.format(WIDTH_REFERENCE, "-");
|
||||
Assert.assertEquals("Formatted width reference didn't have correct length", WIDTH_REFERENCE_LENGTH, paddedReference.length());
|
||||
Assert.assertEquals("Formatted width reference was changed despite defining width", WIDTH_REFERENCE, paddedReference);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeftAlignment()
|
||||
{
|
||||
Column column = column("Left", TextTable.Alignment.LEFT);
|
||||
column.fit(WIDTH_REFERENCE);
|
||||
|
||||
String paddedHeader = column.formatHeader("-");
|
||||
Assert.assertEquals("Left-aligned header should be padded on the right", "Left-----------", paddedHeader);
|
||||
String paddedReference = column.format(WIDTH_REFERENCE, "-");
|
||||
Assert.assertEquals("Left-aligned reference should'nt be padded", WIDTH_REFERENCE, paddedReference);
|
||||
String paddedValue = column.format("Value", "-");
|
||||
Assert.assertEquals("Left-aligned value should be padded on the right", "Value----------", paddedValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCenterAlignment()
|
||||
{
|
||||
Column column = column("Centered", TextTable.Alignment.CENTER);
|
||||
column.fit(WIDTH_REFERENCE);
|
||||
|
||||
String paddedHeader = column.formatHeader("-");
|
||||
Assert.assertEquals("Centered header should be padded equally on both sides", "---Centered----", paddedHeader);
|
||||
String paddedReference = column.format(WIDTH_REFERENCE, "-");
|
||||
Assert.assertEquals("Centered reference should'nt be padded", WIDTH_REFERENCE, paddedReference);
|
||||
String paddedValue = column.format("Value", "-");
|
||||
Assert.assertEquals("Centered value should be padded equally on both sides", "-----Value-----", paddedValue);
|
||||
String paddedOffCenter = column.format("Value1", "-");
|
||||
Assert.assertNotEquals("Center padding should be left-biased", "-----Value1----", paddedOffCenter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRightAlignment()
|
||||
{
|
||||
Column column = column("Right", TextTable.Alignment.RIGHT);
|
||||
column.fit(WIDTH_REFERENCE);
|
||||
|
||||
String paddedHeader = column.formatHeader("-");
|
||||
Assert.assertEquals("Right-aligned header should be padded on the left", "----------Right", paddedHeader);
|
||||
String paddedReference = column.format(WIDTH_REFERENCE, "-");
|
||||
Assert.assertEquals("Right-aligned reference should'nt be padded", WIDTH_REFERENCE, paddedReference);
|
||||
String paddedValue = column.format("Value", "-");
|
||||
Assert.assertEquals("Right-aligned value should be padded on the left", "----------Value", paddedValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMarkdownCompliance()
|
||||
{
|
||||
TextTable table = new TextTable(Lists.newArrayList(
|
||||
column("Left", TextTable.Alignment.LEFT),
|
||||
column("Center", TextTable.Alignment.CENTER),
|
||||
column("Right", TextTable.Alignment.RIGHT)
|
||||
));
|
||||
table.add("Long Value 1", "Value 2", "Value 3");
|
||||
table.add("Value 1", "Long Value 2", "Value 3");
|
||||
table.add("Value 1", "Value 2", "Long Value 3");
|
||||
int[] columnWidths = table.getColumns().stream().mapToInt(Column::getWidth).toArray();
|
||||
Assert.assertArrayEquals("Column widths should adjust for long values", new int[]{12, 12, 12}, columnWidths);
|
||||
|
||||
String[] result = table.build("\n").split("\n");
|
||||
Assert.assertEquals("Header row + separator row + value rows should result in 5 lines", 5, result.length);
|
||||
Assert.assertEquals(
|
||||
"Column headers should be properly formatted",
|
||||
"| Left | Center | Right |",
|
||||
result[0]);
|
||||
Assert.assertEquals(
|
||||
"Header-body separators should contain markdown alignment information",
|
||||
"|:------------ |:------------:| ------------:|",
|
||||
result[1]
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue