Coder Social home page Coder Social logo

davidsusu / tree-printer Goto Github PK

View Code? Open in Web Editor NEW
37.0 1.0 7.0 437 KB

Java library for visualizing tree structures in the command line

License: Apache License 2.0

Java 99.94% Shell 0.06%
java tree-viewer command-line unicode-art ascii-art tree library

tree-printer's Introduction

Tree Printer πŸ”Έ now with colors (ANSI support)

Simple Java library for visualizing tree structures in the command line.

natural foods
 β”œβ”€ fruits
 β”‚  β”œβ”€ apple
 β”‚  β”œβ”€ banana
 β”‚  β”œβ”€ mango
 β”‚  β”œβ”€ lorem and
 β”‚  β”‚  ipsum
 β”‚  β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚  β”‚ β”‚            citroideae            β”‚
 β”‚  β”œβ”€β”‚    β”Œβ”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”΄β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚
 β”‚  β”‚ β”‚ orange lemon grapefruit mandarin β”‚
 β”‚  β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
 β”‚  β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚  β”‚ β”‚         berries                β”‚
 β”‚  β”‚ β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”           β”‚
 β”‚  └─│   grape          other         β”‚
 β”‚    β”‚  β”Œβ”€β”€β”΄β”€β”       β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”     β”‚
 β”‚    β”‚ red white strawberry raspberry β”‚
 β”‚    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
 β”œβ”€ vegetables
 β”‚  β”œβ”€ tomato
 β”‚  β”œβ”€ carrot
 β”‚  β”‚ A──────────B
 β”‚  └─│ broccoli β”‚
 β”‚    D──────────C
 └─ seeds
    β”œβ”€ walnut
    └─ peanut

Migration from 2.x to 3.x

Now coloring and other ANSI escapes are supported. See below for more information.

The following breaking changes was made:

  • TreeNode.content() returns with ConsoleText (instead of String)

Migration from 1.x to 2.x

The following breaking changes was made:

  • Package/class structure was reorganized
  • Method naming convention was changed (e. g. getChildren() β†’ children())
  • Most of the telescoping constructors was removed (use builders instead)
  • Decorator inheritance was simplified, forceInherit option was removed

Furthermore, many little changes, extensions and fixes was added.

Using in projects

This library is open source, and available under the Apache License V2.

The library is compatible with java versions 1.8+. See the 1.x branch for using the legacy version (compatible with 1.6+).

Built packages are available from the Maven Central Repository.

In gradle projects:

dependencies {
    implementation "hu.webarticum:tree-printer:${treePrinterVersion}"
}

In maven projects:

<dependency>
  <groupId>hu.webarticum</groupId>
  <artifactId>tree-printer</artifactId>
  <version>${treePrinterVersion}</version>
</dependency>

Constructing trees

A tree is a hierarchical structure built from nodes. Any tree is given by its root node.

Nodes implement the TreeNode interface. It contains default implementations for the majority of its methods, you must implement only the content() and children() methods.

You can use SimpleTreeNode out-of-the-box as a default string-based implementation:

SimpleTreeNode rootNode = new SimpleTreeNode("I'm the root!");
rootNode.addChild(new SimpleTreeNode("I'm a child..."));
rootNode.addChild(new SimpleTreeNode("I'm an other child..."));

However, you are free to implement your custom nodes.

Printing a tree

There are multiple built-in implementations of the TreePrinter interface for printing tree structures via the print() method. This method accepts a TreeNode (the root node of the printed hierarchy), and, optionally, an Appendable object, to where the output will be flushed (by default, output will be printed to System.out).

Alternatively, you can get the visualization as String via stringify(). In some cases this is inefficient (especially when you print large data with ListingTreePrinter).

It is very easy to visualize the above structure:

new ListingTreePrinter().print(rootNode);

And the result:

I'm the root!
 β”œβ”€I'm a child...
 └─I'm an other child...

Or use a TraditionalTreePrinter:

new TraditionalTreePrinter().print(rootNode);

Which results:

           I'm the root!
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”
       β”‚                 β”‚
I'm a child... I'm an other child...

For more available printers see the hu.webarticum.treeprinter.printer.* packages.

Of course, TreePrinter implementations have many options for controlling the output. You can change the lining characters, the aligning, and so on.

Most classes have builders and some basic constructors for easy change of settings. For example, if we want to align everything to left:

new TraditionalTreePrinter(
        DefaultAligner.builder()
                .align(DefaultAligner.Alignment.LEFT)
        .build(),
        TraditionalTreePrinter.DEFAULT_LINER
).print(rootNode);

Result:

I'm the root!
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              β”‚
I'm a child... I'm an other child...

Using decorators

You can easily write node decorators by extending AbstractTreeNodeDecorator. There are built-in implementations for adding padding, border, shadow and other basic decorations.

In the previous example the child nodes are confused, because only a single space separates them. It will be much cleaner if we added a border:

new TraditionalTreePrinter().print(new BorderTreeNodeDecorator(rootNode));

Result:

            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚I'm the root!β”‚
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚                   β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚I'm a child...β”‚ β”‚I'm an other child...β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Of course, you can compose as many decorators as you want:

TreeNode decoratedTreeNode = new ShadowTreeNodeDecorator(
        BorderTreeNodeDecorator.builder()
                .wideUnicode()
                .buildFor(
                        new PadTreeNodeDecorator(rootNode, new Insets(0, 2))));

new TraditionalTreePrinter().print(decoratedTreeNode);

Result:

Decorated tree example

Decorators inherit by default, but you can change this behavior.

Placeholders

You can put placeholder nodes into the tree. These nodes are hidden by default in the general printers. Placeholders are useful in n-ary trees, where missing nodes matter.

boolean displayPlaceholders = true;

SimpleTreeNode alignedTree = new SimpleTreeNode("ROOT");
alignedTree.addChild(new PlaceholderNode());
alignedTree.addChild(new SimpleTreeNode("RIGHT"));

new TraditionalTreePrinter(displayPlaceholders).print(
        PadTreeNodeDecorator.builder()
                .horizontalPad(3)
                .buildFor(new BorderTreeNodeDecorator(alignedTree)));
       β”Œβ”€β”€β”€β”€β”
       β”‚ROOTβ”‚
       β””β”€β”€β”€β”€β”˜
   β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”
   β”‚         β”‚
          β”Œβ”€β”€β”€β”€β”€β”
          β”‚RIGHTβ”‚
          β””β”€β”€β”€β”€β”€β”˜

Any node whose isPlaceholder() method returns true is considered a placeholder. hu.webarticum.treeprinter.PlaceholderNode is a built-in placeholder, it's empty and undecorable too.

ASCII vs Unicode mode

Built-in objects that print lines or borders have a built-in set of characters, both for ASCII and Unicode mode. Affected classes have a constructor/builder parameter useUnicode. You can globally change the default mode with UnicodeMode.setUnicodeAsDefault(). (Initial global default is Unicode.)

The first example with ASCII rendering:

ListingTreePrinter.builder().ascii().build().print(rootNode);

Result:

I'm the root!
 |-I'm a child...
 '-I'm an other child...

Coloring and other ANSI formatters

Coloring and other ANSI escapes are supported with the following features:

  • ANSI formatting support was added to built-in decorators and printers (e. g. colored lining)
  • ConsoleText and its implementations was added for better handling of texts
  • AnsiFormat and its basic constant instances was added, it's composable
  • global AnsiMode added (similar to UnicodeMode), ANSI can be disabled globally
  • width of custom ANSI formatted texts is handled properly

Interface ConsoleText represents textual content intended for display in the command line. ConsoleText.of(String) (or new PlainConsoleText(String)) represents some plain text (without formatting). ConsoleText.ofAnsi(String) (or new AnsiConsoleText(String)) holds text possibly with format escapes. Both normalizes newlines and unicode codepoints. Both clears non-printable characters, expect that the latter keeps ANSI format escapes.

It's not necessary to write ANSI escapes by hand. The AnsiFormat class represents a formatting, and can be applied to any ConsoleText. Also, it's composable. Let's see a simple example:

ConsoleText.of("Hello ANSI").format(AnsiFormat.GREEN);

This will make the text "Hello ANSI", in green.

Here is a little bit more complex use case:

ConsoleText.of("Hello ").concat(ConsoleText.of("ANSI").format(AnsiFormat.GREEN)).format(AnsiFormat.BOLD);

Now only the word "ANSI" is green, but the whole text is in bold.

We can create a colorful tree:

SimpleTreeNode rootNode = new SimpleTreeNode(ConsoleText.of("Root").format(
        AnsiFormat.UNDERLINE.compose(AnsiFormat.BOLD).compose(AnsiFormat.RED)));

SimpleTreeNode childNode1 = new SimpleTreeNode(
        ConsoleText.of("Child 1").format(AnsiFormat.GREEN.compose(AnsiFormat.BOLD)));
rootNode.addChild(new BorderTreeNodeDecorator(childNode1, AnsiFormat.RED));

SimpleTreeNode childNode2 = new SimpleTreeNode("Child 2");
rootNode.addChild(childNode2);

SimpleTreeNode grandChildNode22 = new SimpleTreeNode(
        ConsoleText.of("Grandchild ")
                .concat(ConsoleText.of("2-2").format(AnsiFormat.CYAN)).breakLine()
                .concat(ConsoleText.of("Line 2").format(AnsiFormat.RED)).breakLine()
                .concat(ConsoleText.of("Line line 3").format(AnsiFormat.MAGENTA))
                .format(AnsiFormat.BOLD));
childNode2.addChild(new ShadowTreeNodeDecorator(
        JustifyTreeNodeDecorator.builder()
                .minimumHeight(5).minimumWidth(20)
                .verticalAlign(VerticalAlign.MIDDLE).horizontalAlign(HorizontalAlign.CENTER)
                .background('~').backgroundFormat(AnsiFormat.CYAN)
                .buildFor(grandChildNode22), AnsiFormat.BLUE));

The built-in printers and decorators are formattable too. Let's print the above nodes connected with some blue lines:

new TraditionalTreePrinter(AnsiFormat.BLUE).print(rootNode);

Depending on the terminal, the result will be something like this:

ANSI formatted tree example

For more examples see the AnsiExamplesMain demo. Study the API to find more possibilities.

Future plans

There are various ideas in the backlog that are planned to be implemented in the next versions, such as:

  • More TreePrinter implementations
  • More flexible alignment settings
  • Improved TraditionalTreePrinter
  • and more

tree-printer's People

Contributors

andrewliles avatar davidsusu avatar morningman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

tree-printer's Issues

Compress branches

Take a look at https://github.com/billvanyo/tree_printer.

It is capable of using as much space as possible - child branches can overflow under sibling parent branches, which makes the tree look a lot more compact. Something like that would be nice to have

Another nice feature would be to compress row spacing to compress rows:

                    1                
                    └┐               <-- one row for branches instead of 2 
                     20              
               β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”         
               15          24        
           β”Œβ”€β”€β”€β”΄β”€β”€β”€β”   β”Œβ”€β”€β”€β”΄β”€β”€β”€β”     
           10      19  21      27    
        β”Œβ”€β”€β”΄β”€β”€β”   β”Œβ”˜   └┐    β”Œβ”€β”΄β”€β”   
        7     12  16    22   26  29  
      β”Œβ”€β”΄β”€β” β”Œβ”€β”΄β”€β” └┐    └┐  β”Œβ”˜ β”Œβ”€β”΄β”€β” 
      5   8 11  14 18    23 25 28  30
     β”Œβ”΄β”  └┐   β”Œβ”˜ β”Œβ”˜                 
     4 6   9   13 17                 
    β”Œβ”˜                               
    2                                
    └┐                               
     3                 

ALSO when I pulled and debugged your lib, I saw there are a lot of writes and changes to the LineBuffer, as well as methods that return encoded values (like array of 2 elements encoding properties like position and width β€” maybe use records or classes with meaningful fields for them?). Maybe it would be possible to avoid changes like that if you had a dynamic list elements per row (like pad, branch, node, branch, pad) which is rewritten in some kind of two-pass bottom-up + top-down fashion?

tree mis-aligned

I print a simple tree but found that some branch is misaligned. i tried both ascii and unicode as per the following snapshots

image
image

Avoid Root Node, support `ListingTreePrinter().stringify(Node ...)` for multiple nodes (without one root)

Example: AST of SQL Text with multiple statements:

SQL Text
 β”œβ”€Statements: statement.select.PlainSelect
 β”‚  β”œβ”€selectItems: statement.select.SelectItem
 β”‚  β”‚  └─AllColumns: *
 β”‚  └─Table: dual
 └─Statements: statement.select.PlainSelect
    β”œβ”€selectItems: statement.select.SelectItem
    β”‚  └─AllColumns: *
    └─Table: dual

Would be nice to optionally avoid the Root Node SQL Text and print it out as:

Statements: statement.select.PlainSelect
 β”œβ”€selectItems: statement.select.SelectItem
 β”‚  └─AllColumns: *
 └─Table: dual

Statements: statement.select.PlainSelect
 β”œβ”€selectItems: statement.select.SelectItem
 β”‚  └─AllColumns: *
 └─Table: dual

node creation

hello, may i suggest the availability of node creation with empty values.

Run example code throws NullPointerException

    public static void main(String[] args) throws Exception {
        SimpleTreeNode rootNode = new SimpleTreeNode("I'm the root!");
        rootNode.addChild(new SimpleTreeNode("I'm a child..."));
        rootNode.addChild(new SimpleTreeNode("I'm an other child..."));
        new TraditionalTreePrinter().print(new BorderTreeNodeDecorator(rootNode));
    }

Throws:

Exception in thread "main" java.lang.NullPointerException
	at org.apache.doris.common.treeprinter.TraditionalTreePrinter$DefaultAligner.alignChildren(TraditionalTreePrinter.java:266)
	at org.apache.doris.common.treeprinter.TraditionalTreePrinter.print(TraditionalTreePrinter.java:74)
	at org.apache.doris.common.treeprinter.AbstractTreePrinter.print(AbstractTreePrinter.java:28)
	at cmy.MyTest.main(MyTest.java:43)

The package name is changing to org.apache.doris.common.treeprinter because I need to debug the code in my project.

This is because the AbstractTreeNode class missing equals and hashCode method.
I will submit a PR to fix this.

Children() is hard to implement/overwrite

Greetings!

First of all, thank you a lot for providing the software. It looks exactly like what I need.
(For reference, please have a look at the AST visualization at http://217.160.215.75:8080/jsqlformatter/JSQLFormatter/demo.html -- I will want to do the same but in ASCII).

I have already a plain TreeNode structure (which I use for the Java JTree view) and find it now difficult to deal with your naming conventions:

  1. TreeNode and hu.webarticum.treeprinter.TreeNode collide and make it cumbersome to work with both interfaces within one Method. Maybe a name change into AsciiTreeNode would make things easier.

  2. content() is a matter of taste, maybe you want to direct it to toString() by default

  3. most serious, children() is a challenge because there is no easy way to translate Enumeration<? extends TreeNode> children() into List<hu.webarticum.treeprinter.TreeNode> children() without traversing the whole tree and translate Node by Node.

When implementing both interfaces, children() collides.

So why can't we keep the Java Enumeration<? extends TreeNode> children() and add another method List<hu.webarticum.treeprinter.TreeNode> getChildrenList?

It would make life so much easier, when coming from JTrees and looking for an ASCII output only.
Thanks for considering and all the best!

image

How best to carry contextual information via the Tree into a Listing?

Thank you, David, for this library.

I have used it to produce a tree listed of people's names, here is an example, (illustrated with made-up names) as rows in a spreadsheet. I use the ListingTreePrinter to (a) sequence the relationships (b) create an "ASCII" tree presentation.

image

I have been using v2.x. In order to achieve this, I had to overload the TreeNode.content() method to contain not just the people's names, but also an identifier appended on the end (name was concatenated with @ then the identifier. Then parse the ListingTreePrinter.print() output splitting using the following Regex

(.*)@(.{26}\n

to obtains the line elements and the Identifier.

How could I achieve this in a more elegant way; as there are no protected/public methods, I cannot subclass the ListingTreePrinter? Or would you be happy to receive a PR for an alternate public method, maybe something like:

  List<ListNode> ListingTreePrinter.getListNodes(TreeNode rootNode)

where ListNode contains the listing prefix e.g. " β”‚ β”œβ”€ " and the original TreeNode.

Fails to import tree printer module in JAVA Module based projects

Hi Devs,
While working on one of my projects, I decided to use your library to print out AST models on the console. But I met with a problem !!!

My project uses Java Module based structure (i.e., uses module-info.java file) as well as Gradle Build System. When I tried to import your library it says ...

error: module not found: tree.printer

My module-info.java file looks like this ...

module org.kodedevs.kode {
    requires info.picocli;
    requires org.fusesource.jansi;
    requires com.install4j.runtime;
    requires tree.printer;

    requires transitive java.scripting;

    opens org.kodedevs.kode.cli to info.picocli;

    provides javax.script.ScriptEngineFactory with
            org.kodedevs.kode.jsr223.KodeScriptEngineFactory;
}

If possible, can you add a module-info.java file to your project ...

With Regards,
Edumate696

Will there be a new version?

Hi, after this PR #4 is merged, will a new version be pushed to the maven repository?
Or will you wait for the redesign in the TODO list to be completed before pushing a new version?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.