Skip to content

Commit

Permalink
RenderOptions and Escaper classes introduced.
Browse files Browse the repository at this point in the history
  • Loading branch information
Michal Jaron committed Jan 26, 2023
1 parent afb9c91 commit e93d55d
Show file tree
Hide file tree
Showing 20 changed files with 570 additions and 87 deletions.
51 changes: 47 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# mjaron-etudes-java

Utils compatible with Java 1.8 or higher.
Library for printing Java object lists as Markdown-formatted table.
Utils compatible with Java 1.8.

[![Java CI with Gradle](https://github.com/mjfryc/mjaron-etudes-java/actions/workflows/gradle.yml/badge.svg)](https://github.com/mjfryc/mjaron-etudes-java/actions/workflows/gradle.yml)
[![Publish package to GitHub Packages](https://github.com/mjfryc/mjaron-etudes-java/actions/workflows/gradle-publish.yml/badge.svg)](https://github.com/mjfryc/mjaron-etudes-java/actions/workflows/gradle-publish.yml)
Expand Down Expand Up @@ -29,22 +30,64 @@ implementation 'io.github.mjfryc:mjaron-etudes-java:0.1.11'
* Now import package and use it, e.g:
* `import pl.mjaron.etudes.Obj;`

## Object utils
## Table generation utils

### Printing object list as a Markdown table

```java
Cat cat = sampleCat();
String table = Table.render(Arrays.asList(cat, otherCat()), Cat.class);
System.out.println(table);
```

```
| name | legsCount | lazy | topSpeed |
| ---- | --------- | ----- | -------- |
| John | 4 | true | 35.24 |
| Bob | 5 | false | 75.0 |
```

### Table generation customization

Optionally, Markdown special characters may be escaped:

```java
final Cat cat = sampleCat();
final String table = Obj.asTable(List.of(cat, otherCat()), Cat.class);
String table = Table.render(cats, Cat.class,
RenderOptions.make().withMarkdownEscaper().withColumnsAligned());
System.out.println(table);
```

So now all special characters are escaped by HTML number syntax, there is following raw text:

| name | age |
| ----------------- | --- |
| Tom | 2 |
| _Michael_ | 5 |

Rendered by Markdown:

| name | age |
| ----------------- | --- |
| Tom | 2 |
| _Michael_ | 5 |

Without a `MarkdownEscaper`, cells will be rendered 'as is', which can cause a bad rendering:

```java
String table = Table.render(cats, Cat.class,
RenderOptions.make().withColumnsAligned());
System.out.println(table);
```
| name | age |
| --------- | --- |
| Tom | 2 |
| _Michael_ | 5 |

### Markdown custom escaper - @TODO

@TODO Implement escaper which escapes only when prepended with (eg.) backslash `\`.

## Object utils

### Getting object values

Expand Down
16 changes: 16 additions & 0 deletions src/main/java/pl/mjaron/etudes/Arr.java
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,20 @@ public static boolean[] add(final boolean[] array, final boolean[] other) {
System.arraycopy(other, 0, newArray, array.length, other.length);
return newArray;
}

public static <T> boolean containsReference(final T[] arr, final T elem) {
for (final T entry : arr) {
if (entry == elem) {
return true;
}
}
return false;
}

public static boolean contains(final char[] arr, final char elem) {
for (final char entry : arr)
if (entry == elem)
return true;
return false;
}
}
2 changes: 1 addition & 1 deletion src/main/java/pl/mjaron/etudes/IteratorWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
/**
* Allows getting current iterated element with {@link #curr()} method.
*
* @param <T>
* @param <T> Iterated element type
*/
public class IteratorWrapper<T> implements Iterator<T> {

Expand Down
25 changes: 9 additions & 16 deletions src/main/java/pl/mjaron/etudes/Obj.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static <T> Field[] getFields(final Class<T> tClass) {
return tClass.getDeclaredFields();
}

public static <T> List<String> getFieldNames(final Class<T> tClass, final Field[] fields) {
public static <T> List<String> getFieldNames(final Field[] fields) {
List<String> names = new ArrayList<>(fields.length);
for (final Field entry : fields) {
names.add(entry.getName());
Expand All @@ -45,7 +45,7 @@ public static <T> List<String> getFieldNames(final Class<T> tClass, final Field[

public static <T> List<String> getFieldNames(final Class<T> tClass) {
final Field[] fields = getFields(tClass);
return getFieldNames(tClass, fields);
return getFieldNames(fields);
}

/**
Expand Down Expand Up @@ -85,22 +85,17 @@ public static <T> void visitFieldValues(final T what, final Class<T> tClass, fin
try {
getter = tClass.getMethod("is" + fieldNameCapitalized);
} catch (final NoSuchMethodException e2) {
throw new RuntimeException("Cannot obtain value of field: [" + field.getName() +
"], of type: [" + field.getType() + "]: Field is not public and there is no get"
+ fieldNameCapitalized + "() + nor is" + fieldNameCapitalized + "() method accessible.", e2);
throw new RuntimeException("Cannot obtain value of field: [" + field.getName() + "], of type: [" + field.getType() + "]: Field is not public and there is no get" + fieldNameCapitalized + "() + nor is" + fieldNameCapitalized + "() method accessible.", e2);
}
} else {
throw new RuntimeException("Cannot obtain value of field: [" + field.getName() +
"], of type: [" + field.getType() + "]: Field is not public and there is no get"
+ fieldNameCapitalized + "() method accessible.", e1);
throw new RuntimeException("Cannot obtain value of field: [" + field.getName() + "], of type: [" + field.getType() + "]: Field is not public and there is no get" + fieldNameCapitalized + "() method accessible.", e1);
}
}
try {
final Object getterResult = getter.invoke(what);
visitor.visit(field.getName(), getterResult);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("Cannot obtain value of field: [" + field.getName() +
"], of type: [" + field.getType() + "]: invocation of getter has failed.", e);
throw new RuntimeException("Cannot obtain value of field: [" + field.getName() + "], of type: [" + field.getType() + "]: invocation of getter has failed.", e);
}
}
}
Expand Down Expand Up @@ -149,12 +144,10 @@ public static <T> Map<String, String> getFieldValuesAsStrings(final T what) {
* @param tClass Class of iterated entries.
* @param <T> Type of iterated entries.
* @return Markdown table.
* @deprecated Use {@link Table#render(Iterable, Class)}
* @deprecated Use {@link Table#render(Iterable, Class, RenderOptions)}
*/
public static <T> String asTable(final Iterable<T> iterable, final Class<T> tClass) {
final BeanTableSource<T> tableSource = new BeanTableSource<>(iterable, tClass);
final MarkdownTableWriter tableWriter = new MarkdownTableWriter();
return asTable(tableSource, tableWriter);
return Table.renderWith(new BeanTableSource<>(iterable, tClass), new MarkdownTableWriter(), RenderOptions.MARKDOWN_ESCAPER);
}

/**
Expand All @@ -165,9 +158,9 @@ public static <T> String asTable(final Iterable<T> iterable, final Class<T> tCla
* @param <SourceT> Type of table source.
* @param <WriterT> Type of table destination.
* @return Table written as a String.
* @deprecated Use {@link Table#renderWith(ITableSource, ITableWriter)}
* @deprecated Use {@link Table#renderWith(ITableSource, ITableWriter, RenderOptions)}
*/
public static <SourceT extends ITableSource, WriterT extends ITableWriter> String asTable(final SourceT source, final WriterT writer) {
return Table.renderWith(source, writer);
return Table.renderWith(source, writer, RenderOptions.FIXED_WIDTH);
}
}
3 changes: 2 additions & 1 deletion src/main/java/pl/mjaron/etudes/Path.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ public static int extensionPos(final String path, final char separator) {
* a.b/c --&gt; ""
* </pre>
*
* @param path Given path.
* @param path Given path.
* @param separators Path node separators
* @return Extension without a dot.
*/
public static String extension(final String path, final String separators) {
Expand Down
143 changes: 120 additions & 23 deletions src/main/java/pl/mjaron/etudes/Table.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,67 @@

package pl.mjaron.etudes;

import pl.mjaron.etudes.table.BeanTableSource;
import pl.mjaron.etudes.table.ITableSource;
import pl.mjaron.etudes.table.ITableWriter;
import pl.mjaron.etudes.table.MarkdownTableWriter;
import pl.mjaron.etudes.table.*;

import java.util.Arrays;

/**
* A set of methods used to generate table, e.g, any {@link Iterable} or array may be converted to following
* {@link String}:
*
* <pre>
* | name | legsCount | lazy | topSpeed |
* | ---- | --------- | ---- | -------- |
* | John | 4 | true | 35.24 |
* </pre>
*/
public abstract class Table {

/**
* Read from table source and write to table writer.
*
* @param source Table source.
* @param writer Table destination.
* @param options Rendering options
* @param <SourceT> Type of table source.
* @param <WriterT> Type of table destination.
* @return Table rendered as text
* @since 0.1.12
*/
public static <SourceT extends ITableSource, WriterT extends ITableWriter> String renderWith(final SourceT source, final WriterT writer, final RenderOptions options) {
source.readTo(writer, options);
return writer.getTable();
}

/**
* Creates table from any iterable.
*
* @param iterable Any iterable
* @param tClass Element class
* @param writer Writer implementation instance
* @param options Rendering options
* @param <T> Element type
* @return Table rendered as text
* @since 0.1.12
*/
public static <T> String render(final Iterable<T> iterable, final Class<T> tClass, final ITableWriter writer, final RenderOptions options) {
return renderWith(new BeanTableSource<>(iterable, tClass), writer, options);
}

/**
* Creates table from any iterable.
*
* @param iterable Any iterable
* @param tClass Element class
* @param writer Writer implementation instance
* @param <T> Element type
* @return Table rendered as text
* @since 0.1.12
*/
public static <T> String render(final Iterable<T> iterable, final Class<T> tClass, final ITableWriter writer) {
return render(iterable, tClass, writer, RenderOptions.DEFAULT);
}

/**
* Prints iterable as a table, e.g:
*
Expand All @@ -37,38 +89,83 @@ public abstract class Table {
* | John | 4 | true | 35.24 |
* </pre>
*
* @param iterable Any iterable collection.
* @param tClass Class of iterated entries.
* @param <T> Type of iterated entries.
* @param iterable Any iterable collection
* @param tClass Class of iterated entries
* @param <T> Type of iterated entries
* @param options Rendering options
* @return Markdown table.
* @since 0.1.12
*/
public static <T> String render(final Iterable<T> iterable, final Class<T> tClass, final RenderOptions options) {
return render(iterable, tClass, new MarkdownTableWriter(), options);
}

/**
* Creates table from any iterable.
*
* @param iterable Any iterable
* @param tClass Element class
* @param <T> Element type
* @return Table rendered as text
* @since 0.1.12
*/
public static <T> String render(final Iterable<T> iterable, final Class<T> tClass) {
return renderAs(iterable, tClass, new MarkdownTableWriter());
return render(iterable, tClass, new MarkdownTableWriter(), RenderOptions.DEFAULT);
}

public static <T> String render(final T[] array, final Class<T> tClass) {
return renderAs(array, tClass, new MarkdownTableWriter());
/**
* Creates table from any array.
*
* @param array Any array
* @param tClass Element class
* @param writer Writer implementation instance
* @param options Rendering options
* @param <T> Element type
* @return Table rendered as text
* @since 0.1.12
*/
public static <T> String render(final T[] array, final Class<T> tClass, final ITableWriter writer, final RenderOptions options) {
return render(Arrays.asList(array), tClass, writer, options);
}

public static <T> String renderAs(final Iterable<T> iterable, final Class<T> tClass, final ITableWriter writer) {
return renderWith(new BeanTableSource<>(iterable, tClass), writer);
/**
* Creates table from any array.
*
* @param array Any array
* @param tClass Element class
* @param writer Writer implementation instance
* @param <T> Element type
* @return Table rendered as text
* @since 0.1.12
*/
public static <T> String render(final T[] array, final Class<T> tClass, final ITableWriter writer) {
return render(array, tClass, writer, RenderOptions.DEFAULT);
}

public static <T> String renderAs(final T[] array, final Class<T> tClass, final ITableWriter writer) {
return renderAs(Arrays.asList(array), tClass, writer);
/**
* Creates table from any array.
*
* @param array Any array
* @param tClass Element class
* @param options Rendering options
* @param <T> Element type
* @return Table rendered as text
* @since 0.1.12
*/
public static <T> String render(final T[] array, final Class<T> tClass, final RenderOptions options) {
return render(array, tClass, new MarkdownTableWriter(), options);
}

/**
* Read from table source and write to table writer.
* Creates table from any array.
*
* @param source Table source.
* @param writer Table destination.
* @param <SourceT> Type of table source.
* @param <WriterT> Type of table destination.
* @return Table written as a String.
* @param array Any array
* @param tClass Element class
* @param <T> Element type
* @return Table rendered as text
* @since 0.1.12
*/
public static <SourceT extends ITableSource, WriterT extends ITableWriter> String renderWith(final SourceT source, final WriterT writer) {
source.readTo(writer);
return writer.getTable();
public static <T> String render(final T[] array, final Class<T> tClass) {
return render(array, tClass, new MarkdownTableWriter(), RenderOptions.DEFAULT);
}
}
9 changes: 2 additions & 7 deletions src/main/java/pl/mjaron/etudes/table/BeanTableSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public BeanTableSource(final Iterable<BeanT> values, final Class<BeanT> tClass)
this.tClass = tClass;
this.values = values;
this.tFields = Obj.getFields(this.tClass);
this.headers = Obj.getFieldNames(this.tClass, tFields);
this.headers = Obj.getFieldNames(tFields);
}

@Override
Expand Down Expand Up @@ -110,12 +110,7 @@ public boolean hasNext() {
public Iterable<String> next() {
final BeanT bean = beanIterator.next();
List<String> stringSeries = new ArrayList<>(tFields.length);
Obj.visitFieldValues(bean, this.tClass, this.tFields, new Obj.IFieldVisitor() {
@Override
public void visit(final String name, final Object value) {
stringSeries.add(Str.orEmpty(value));
}
});
Obj.visitFieldValues(bean, this.tClass, this.tFields, (name, value) -> stringSeries.add(Str.orEmpty(value)));
return stringSeries;
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/pl/mjaron/etudes/table/BlankTableWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ public BlankTableWriter(final int[] widths) {
}

@Override
public void beginTable(ITableSource source) {
if (this.widths == null) {
this.widths = TableColumnsWidthDetector.compute(source);
public void beginTable(ITableSource source, int[] widths) {
if (widths != null) {
this.widths = widths;
}
}

Expand Down
Loading

0 comments on commit e93d55d

Please sign in to comment.