Standalone library for printing Java object lists (arrays, iterables) as a Markdown, CSV or HTML formatted table.
Utils compatible with Java 1.8.
implementation 'io.github.mjfryc:mjaron-etudes-java:0.3.0'
Table.render(persons, Person.class).run();
name | surname | birthday | address | contact |
---|---|---|---|---|
Sally | Fox | Mon Mar 25 00:00:00 CET 3872 | London | [email protected] |
Jay | Elephant | Mon Mar 20 00:00:00 CET 3820 | Paris | [email protected] |
Bella | Tran | Sun Apr 12 00:00:00 CEST 3863 | China | [email protected] |
Let's assume following Person class:
package pl.mjaron.etudes.sample;
import java.util.Date;
@SuppressWarnings("unused")
public class Person {
private final String name;
private final String surname;
private final Date birthday;
private final String address;
private final String contact;
public Person(String name, String surname, Date birthday, String address, String contact) {
this.name = name;
this.surname = surname;
this.birthday = birthday;
this.address = address;
this.contact = contact;
}
public String getName() {
return name;
}
public String getSurname() {
return surname;
}
public Date getBirthday() {
return birthday;
}
public String getAddress() {
return address;
}
public String getContact() {
return contact;
}
public static Person[] getSampleData() {
return new Person[]{
new Person("Sally", "Fox", new Date(1972, 2, 25), "London", "[email protected]"),
new Person("Jay", "Elephant", new Date(1920, 2, 20), "Paris", "[email protected]"),
new Person("Bella", "Tran", new Date(1963, 3, 12), "China", "[email protected]")
};
}
}
Then following code will render the table:
package pl.mjaron.etudes.sample;
import pl.mjaron.etudes.Table;
import pl.mjaron.etudes.table.VerticalAlign;
public class PersonVerboseSample {
public static void run() {
// Verbose options demo
Table.render(Person.getSampleData(), Person.class)
// By default, the Markdown table format without escaper is used.
// Calling .markdown() causes using Markdown renderer and Markdown escaper.
.markdown() // Use Markdown renderer and escaper.
// or
// .csv() // Use CSV renderer and CSV escaper.
// or
// .html() // Use HTML renderer and HTML escaper.
// Skip escaping the special characters.
.withEscaper(null)
// Optionally force align / do not align column widths.
.withAlignedColumnWidths()
// or
//.withoutAlignedColumnWidths()
// or
//.withEqualColumnWidths()
// Optionally use the custom cell delimiter.
// ',' is the default cell delimiter.
.withCellDelimiter(',')
// How the lines will be separated.
.withLineBreakCRLF()
// or '\n'
// .withLineBreakLF()
// or '\r'
// .withLineBreakCR()
// How align the text (Left, Right or Center)
.withAlign(VerticalAlign.Left)
// or
// .withAlign(VerticalAlign.Right)
// or
// .withAlign(VerticalAlign.Center)
// or
// .withAlign(null) // Use the default align.
// Where to save the table.
.toFile("build/sample.csv")
// By default, the System.out is used, which can be specified as:
// .to(System.out)
// or: .to(Stream|PrintStream|Appendable|File|StringBuilder out)
// Run the render operation.
.run()
// or
// .runToString() // to create the String with whole table.
;
}
}
Following example shows the Markdown customization options.
package pl.mjaron.etudes.sample;
import pl.mjaron.etudes.Table;
import pl.mjaron.etudes.table.VerticalAlign;
import static pl.mjaron.etudes.table.RenderContext.col;
public class MarkdownCustomizationSample {
public static void customizeAlignment() {
Table.render(Person.getSampleData(), Person.class).markdown().withAlign(VerticalAlign.Right).withAlign(1, VerticalAlign.Left).run();
}
public static void customizeColumnsOrder() {
Table.render(Person.getSampleData(), Person.class).markdown().withColumns(
// @formatter:off Preferences > Editor > Code Style > Formatter Control
col("contact", "CONTACT")
.col("address")
.col("surname").as("SURNAME")
// @formatter:on
).run();
}
}
There is possibility to set the all columns alignment and particular columns alignment. Column indexes are counted from 0.
name | surname | birthday | address | contact |
---|---|---|---|---|
Sally | Fox | Mon Mar 25 00:00:00 CET 3872 | London | [email protected] |
Jay | Elephant | Mon Mar 20 00:00:00 CET 3820 | Paris | [email protected] |
Bella | Tran | Sun Apr 12 00:00:00 CEST 3863 | China | [email protected] |
There is possibility to filter the columns, customize column names and order.
CONTACT | address | SURNAME |
---|---|---|
[email protected] | London | Fox |
[email protected] | Paris | Elephant |
[email protected] | China | Tran |
Below sample CSV snippet. Custom value separator may be set. Columns may be aligned but it is not recommended when importing by spreadsheet programs.
package pl.mjaron.etudes.sample;
import pl.mjaron.etudes.Table;
import static pl.mjaron.etudes.table.RenderContext.col;
public class CsvCustomizationSample {
public static void run() {
Table.render(Person.getSampleData(), Person.class).csv()
.withCellDelimiter(" , ") // Allows changing the cell separator; ',' is by default.
// Column alignment used here only to make it more human-readable.
// Column alignment is not recommended for CSV due to importing it later by spreadsheet applications.
// Additional spaces for alignment may break the cell content.
// E.g. Use "Trim spaces" option when importing with LibreOffice Calc.
.withAlignedColumnWidths()
// Let's select the rendered columns.
.withColumns(
// @formatter:off Preferences > Editor > Code Style > Formatter Control
col("contact", "CONTACT")
.col("address")
.col("surname").as("SURNAME")
// @formatter:on
).run();
}
}
CONTACT , address , SURNAME
[email protected] , London , Fox
[email protected] , Paris , Elephant
[email protected] , China , Tran
Basic HTML table may be rendered. When the align is specified, it is written as inline style attribute.
There is possibility to set the table id and class HTML attributes, so it can be customized with CSS.
package pl.mjaron.etudes.sample;
import pl.mjaron.etudes.Table;
import pl.mjaron.etudes.table.VerticalAlign;
import static pl.mjaron.etudes.table.Html.tableId;
import static pl.mjaron.etudes.table.RenderContext.col;
public class HtmlCustomizationSample {
public static void run() {
// @formatter:off Preferences > Editor > Code Style > Formatter Control
Table.render(Person.getSampleData(), Person.class)
.html(tableId("my-table-id").tableClass("my-table-class"))
.withAlign(VerticalAlign.Center)
.withAlign(1, VerticalAlign.Right)
// Let's select the rendered columns.
.withColumns(
col("contact", "CONTACT")
.col("address")
.col("surname").as("SURNAME")
).run();
// @formatter:on
}
}
<table id="my-table-id" class="my-table-class">
<tr>
<th style="text-align: center;">CONTACT</th>
<th style="text-align: right;">address</th>
<th style="text-align: center;">SURNAME</th>
</tr>
<tr>
<td style="text-align: center;">[email protected]</td>
<td style="text-align: right;">London</td>
<td style="text-align: center;">Fox</td>
</tr>
<tr>
<td style="text-align: center;">[email protected]</td>
<td style="text-align: right;">Paris</td>
<td style="text-align: center;">Elephant</td>
</tr>
<tr>
<td style="text-align: center;">[email protected]</td>
<td style="text-align: right;">China</td>
<td style="text-align: center;">Tran</td>
</tr>
</table>
The table is generated in the following sequence:
sequenceDiagram
User->>+Rendering operation: render
Rendering operation ->> Rendering context: isComputeColumnWidths()? [null | true | false]
opt computeColumnWidths == null
Rendering operation ->> ITableWriter: get default value of computing column widths [true | false]
Rendering operation ->> Rendering context: Update compute column widths value
end
opt computeColumnWidths == true
Rendering operation ->> TableColumnsWidthDetector: compute column widths
end
Rendering operation ->> Rendering context: getCellDelimiter()
Rendering operation ->> ITableWriter: getDefaultDelimiter()
opt rendering context hasn't cell delimiter but ITableWriter provides default delimiter
Rendering operation ->> Rendering context: Update the cell delimiter from ITableWriter
end
Rendering operation ->> IEscaper: beginTable(renderingContext)
Rendering operation ->> ITableWriter: beginTable(renderingContext)
opt ITableSource.hasHeaders() == true
Rendering operation ->> ITableWriter: beginHeader()
loop For each headerCell
Rendering operation ->> IEscaper: escape(headerCell)
Rendering operation ->> ITableWriter: writeCell(escapedHeaderCell)
end
Rendering operation ->> ITableWriter: endHeader()
end
loop For each row
Rendering operation ->> ITableWriter: beginRow()
loop For each cell
Rendering operation ->> IEscaper: escape(cell)
Rendering operation ->> ITableWriter: writeCell(escapedCell)
end
Rendering operation ->> ITableWriter: endRow()
end
Rendering operation ->> ITableWriter: endTable(renderingContext)
Rendering operation-->>-User: rendered table
{topSpeed=35.24, legsCount=4, lazy=true, name=John}
class Sample {
void test() {
Cat cat = new Cat();
Map<String, Object> values = Obj.getFieldValues(cat);
System.out.println(values);
}
}
class Sample {
void test() {
int[] a = new int[]{1, 2, 3};
int[] b = new int[]{6, 7, 5};
int[] c = Arr.add(a, b);
assertArrayEquals(c, new int[]{1, 2, 3, 6, 7, 5});
}
}
class Sample {
void test() {
Pair<Integer, String> pair = new Pair<>(5, "C");
assertEquals(5, pair.getKey());
assertEquals("C", pair.getValue());
}
}
class Sample {
void test() {
assertEquals("txt", Path.extension("/my/path/to/file.txt"));
assertEquals("", Path.extension("/my/path/to/file"));
assertEquals("", Path.extension("/my/path/to/file."));
}
}
class Sample {
void test() {
assertEquals(2, Str.charsCount("commit", 'm'));
assertEquals("String", Str.capitalize("string"));
assertEquals(" 3", Str.padLeft("3", 3));
assertEquals("3 ", Str.padRight("3", 3));
}
}
class Sample {
void test() {
Timer t = new Timer();
doLongOperation();
System.out.println("Passed time: " + t.getMillis() + " milliseconds.");
}
}
https://search.maven.org/artifact/io.github.mjfryc/mjaron-etudes-java/0.3.0/jar
implementation 'io.github.mjfryc:mjaron-etudes-java:0.3.0'
- Download the latest release
- From here
- To
[gradle's root directory]/libs/
- E.g:
my-project/libs/mjaron-etudes-java-0.3.0.jar
- In any Gradle subproject which needs this library, put following content:
implementation files(project.rootDir.absolutePath + '/libs/mjaron-etudes-java-0.3.0.jar')
- Now import package and use it, e.g:
import pl.mjaron.etudes.*;