CTO News Hubb
Advertisement
  • Home
  • CTO News
  • IT
  • Technology
  • Tech Topics
    • AI
    • QC
    • Robotics
    • Blockchain
  • Contact
No Result
View All Result
  • Home
  • CTO News
  • IT
  • Technology
  • Tech Topics
    • AI
    • QC
    • Robotics
    • Blockchain
  • Contact
No Result
View All Result
CTO News Hubb
No Result
View All Result
Home IT

Interactive Java consoles with JLine and ConsoleUI

June 1, 2023
in IT


The command-line interface (CLI) is the inner world of software development. From the shell, we have direct access to all the operating system’s capabilities, and with that comes the power to compose and orchestrate all aspects of the software. Many tools and frameworks incorporate command lines. Not only that, but the command prompt is the root magic of working with software systems; it’s the home of near unlimited possibilities. 

In this article, we’ll take a tour of building sophisticated interactive command-line interface (CLI) applications and REPLs (read–eval–print loops, or interactive shells) in Java. We’ll set up a basic demo application in Java and use the JLine and ConsoleUI libraries to add the features that we need.

The Java-based REPL

Our demonstration is based on a theoretical application that examines a software project’s working directory and gathers information about the projects there. The application also is able to create new projects in the directory. The example application will start a REPL that accepts two commands, describe and create, which can be tab-completed. The describe command will list the folder hierarchy of the working directory with color coding (using paging if necessary), while create initiates an interactive menu that lets the user choose what kind of project to create—Java, JavaScript, or Python. If it’s a Java application, we’ll allow a multi-select of additional features the user can add (database or REST API) that will let us see a nested menu. 

We’ll just use these features to explore the JLine capabilities, rather than actually implementing them.

The demo application

For this tour, you’ll need a Java JDK and Maven installed. We’ll start by creating a fresh application with a Maven archetype, like what’s shown in Listing 1.

Listing 1. Create a new application with Maven


mvn archetype:generate -DgroupId=com.infoworld -DartifactId=jline3 -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Maven will use these commands to lay out a new project for us. Before we go any further, let’s also add all the dependencies we’ll need, and also set the Java version to 11 (any version from Java 8 forward should work), as I’ve done in Listing 2. This applies to the pom.xml file in the project root (leave the rest of the pom.xml as-is).

Listing 2. Add dependencies and set the Java version



  
    
      org.apache.maven.plugins
      maven-compiler-plugin
      3.8.1
      
        11
        11
      
    
  


  
    junit
    junit
    3.8.1
    test
  
  
    org.jline
    jline
    3.16.0
  
  
    org.fusesource.jansi
    jansi
    2.4.0
  
  
    org.jline
    jline-terminal-jansi
    3.20.0
  

Next, let’s modify the main class in src/main/java/com/infoworld/App.java to start a REPL loop. Modify App.java using the code in Listing 3.

Listing 3. A simple REPL loop


package com.infoworld;
import org.jline.reader.*;
import org.jline.reader.impl.*;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.impl.completer.*;
import org.jline.terminal.TerminalBuilder;
import org.jline.terminal.Terminal;

import java.io.IOException;
import java.util.*;

public class App {

    public static void main(String[] args) throws IOException {
        Terminal terminal = TerminalBuilder.terminal();
        LineReader reader = LineReaderBuilder.builder()
                .terminal(terminal)
                .completer(new StringsCompleter("describe", "create"))
                .build();

        while (true) {
            String line = reader.readLine("> ");
            if (line == null || line.equalsIgnoreCase("exit")) {
                break;
            }
            reader.getHistory().add(line);
            System.out.println("You said: " + line);
        }
    }
}

Listing 3 creates a very simple program that watches for lines of user input and echoes them back. To that, I added a “completer,” which holds the two commands we support, describe and create. That means when the user is typing at the prompt, they can tab to complete these commands. Tabbing twice will offer a menu with the available commands. JLine has made this very easy with the fluent-style .completer(new StringsCompleter("describe", "create")) method call. JLine has several built-in completers, in addition to Strings, and you can also build your own.

At heart, the REPL is an infinite while loop, which breaks when the user enters exit.

You can test it out by running the Maven exec:java command shown in Listing 4.

Listing 4. Run the program with Maven


mvn clean package exec:java -Dexec.mainClass=com.infoworld.App

You’ll get the carrot prompt, the echo response, and the tab-completion commands.

Handling REPL commands

Now that we have the echo REPL working with auto-complete, let’s actually handle the commands. We’ll do this with typical Java, by comparing the string entered with the commands and calling methods for each. For now, create won’t do anything, but we’ll implement the logic to output the directory hierarchy, as shown in Listing 5.

Listing 5. REPL with the describe command


package com.infoworld;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
import org.jline.reader.LineReader;
import org.jline.reader.impl.completer.StringsCompleter;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.impl.DefaultParser;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;

public class App {
  public static void main(String[] args) throws IOException {
    Terminal terminal = TerminalBuilder.terminal();

    LineReader reader =
        LineReaderBuilder.builder()
            .terminal(terminal)
            .completer(new StringsCompleter("describe", "create"))
            .parser(new DefaultParser())
            .build();

    while (true) {
      String line = reader.readLine("> ");
      if (line == null || line.equalsIgnoreCase("exit")) {
        break;
      }
      reader.getHistory().add(line);

      if (line.equalsIgnoreCase("describe")) {
        Path path = Paths.get(".");
        System.out.println(getDirectoryHierarchy(path));
      } else if (line.equalsIgnoreCase("create")) {
        System.out.println(“TBD”);
      } else {
        System.out.println("Unknown command: " + line);
      }
    }
  }

  public static String getDirectoryHierarchy(Path path) {
    StringBuilder sb = new StringBuilder();
    try (Stream paths = Files.walk(path)) {
      paths.sorted()
          .forEach(
              p -> {
                int depth = path.relativize(p).getNameCount();
                for (int i = 0; i < depth; i++) {
                  sb.append(" ");
                }
    if (p.toFile().isDirectory()) {
                  sb.append("https://www.infoworld.com/"); 
                }
                sb.append(p.getFileName()).append("n");
              });
    } catch (IOException e) {
      e.printStackTrace();
    }
    return sb.toString();
  }
}

Now when you run the application, if you enter the describe command, you’ll see an indent-formatted list of the working directory. The work of building that string up happens in getDirectoryHierarchy(). That method uses normal Java from the java.nio.file package to walk the directory and output each file and directory, indenting a space for each level of directory we go down. This work is mainly done with path.relativize(p).getNameCount(), which says: from my current path (.) give me the relative path to the current one (e.g., ./src/main/java). The getNameCount() just counts the number of names in that path—three, in this case. For each name, we add a space.



Source link

Previous Post

What Every Tech Leader Needs to Know

Next Post

How pairing DORA with additional metrics can help teams achieve peak performance

Next Post

How pairing DORA with additional metrics can help teams achieve peak performance

Automate 2023 recap and the receding horizon problem

Trending News

Quality of new vehicles in US declining on more tech use, study shows

June 23, 2023

OPNsense® a true open source security platform and more

June 27, 2023

Kotlin rises to the Tiobe top 20

September 13, 2023

© CTO News Hubb All rights reserved.

Use of these names, logos, and brands does not imply endorsement unless specified. By using this site, you agree to the Privacy Policy and Terms & Conditions.

Navigate Site

  • Home
  • CTO News
  • IT
  • Technology
  • AI
  • QC
  • Robotics
  • Blockchain
  • Contact

Newsletter Sign Up

No Result
View All Result
  • Home
  • CTO News
  • IT
  • Technology
  • Tech Topics
    • AI
    • QC
    • Robotics
    • Blockchain
  • Contact

© 2021 JNews – Premium WordPress news & magazine theme by Jegtheme.

SUBSCRIBE TO OUR WEEKLY NEWSLETTERS