TUI Spreadsheet in Java

This is a complete tiny spreadsheet that runs in a terminal.

Read the mental model first

Download source file

Quick Run (from repo root)

1cd Site/static/code/examples/java
2javac TuiSpreadsheet.java
3java TuiSpreadsheet

It supports:

  • setting and reading cells (A1, B3, etc.)
  • simple formulas like =A1+B1
  • showing all stored cells
  • saving and loading from a file

Code (Single Public Class)

  1import java.io.IOException;
  2import java.nio.file.Files;
  3import java.nio.file.Path;
  4import java.util.HashMap;
  5import java.util.Map;
  6import java.util.Scanner;
  7
  8public class TuiSpreadsheet {
  9    public static void main(String[] args) {
 10        Scanner scanner = new Scanner(System.in);
 11        Sheet sheet = new Sheet();
 12
 13        System.out.println("Commands: set, get, show, save, load, quit");
 14        System.out.println("Example set: set A1 42");
 15        System.out.println("Formula: set C1 =A1+B1");
 16
 17        while (true) {
 18            System.out.print("> ");
 19            String line = scanner.nextLine().trim();
 20            if (line.isEmpty()) {
 21                continue;
 22            }
 23
 24            String[] parts = line.split("\\s+", 3);
 25            String command = parts[0].toLowerCase();
 26
 27            switch (command) {
 28                case "set":
 29                    if (parts.length < 3) {
 30                        System.out.println("Use: set CELL VALUE");
 31                        break;
 32                    }
 33                    sheet.set(parts[1], parts[2]);
 34                    break;
 35                case "get":
 36                    if (parts.length < 2) {
 37                        System.out.println("Use: get CELL");
 38                        break;
 39                    }
 40                    System.out.println(parts[1].toUpperCase() + " = " + sheet.value(parts[1]));
 41                    break;
 42                case "show":
 43                    sheet.show();
 44                    break;
 45                case "save":
 46                    if (parts.length < 2) {
 47                        System.out.println("Use: save FILE");
 48                        break;
 49                    }
 50                    sheet.save(Path.of(parts[1]));
 51                    break;
 52                case "load":
 53                    if (parts.length < 2) {
 54                        System.out.println("Use: load FILE");
 55                        break;
 56                    }
 57                    sheet.load(Path.of(parts[1]));
 58                    break;
 59                case "quit":
 60                    return;
 61                default:
 62                    System.out.println("Use: set, get, show, save, load, quit");
 63            }
 64        }
 65    }
 66
 67    private static class Sheet {
 68        private final Map<String, String> cells = new HashMap<>();
 69
 70        private void set(String cell, String raw) {
 71            String key = normalizeCell(cell);
 72            if (key == null) {
 73                System.out.println("Cell must look like A1, B2, C10...");
 74                return;
 75            }
 76            cells.put(key, raw.trim());
 77            System.out.println("Stored " + key + ".");
 78        }
 79
 80        private String value(String cell) {
 81            String key = normalizeCell(cell);
 82            if (key == null) {
 83                return "#INVALID_CELL";
 84            }
 85            String raw = cells.getOrDefault(key, "");
 86            if (!raw.startsWith("=")) {
 87                return raw;
 88            }
 89            return evaluate(raw.substring(1));
 90        }
 91
 92        private String evaluate(String expr) {
 93            String clean = expr.replace(" ", "");
 94            int plusIndex = clean.indexOf('+');
 95            if (plusIndex < 0) {
 96                return "#BAD_FORMULA";
 97            }
 98            String left = clean.substring(0, plusIndex);
 99            String right = clean.substring(plusIndex + 1);
100            Double a = numberValue(left);
101            Double b = numberValue(right);
102            if (a == null || b == null) {
103                return "#VALUE";
104            }
105            return String.valueOf(a + b);
106        }
107
108        private Double numberValue(String token) {
109            try {
110                return Double.parseDouble(token);
111            } catch (NumberFormatException ignore) {
112                String fromCell = value(token);
113                try {
114                    return Double.parseDouble(fromCell);
115                } catch (NumberFormatException e) {
116                    return null;
117                }
118            }
119        }
120
121        private void show() {
122            if (cells.isEmpty()) {
123                System.out.println("(sheet is empty)");
124                return;
125            }
126            System.out.println("Cell | Raw | Value");
127            System.out.println("------------------------");
128            cells.keySet().stream().sorted().forEach(cell -> {
129                String raw = cells.get(cell);
130                String val = value(cell);
131                System.out.println(cell + " | " + raw + " | " + val);
132            });
133        }
134
135        private void save(Path file) {
136            StringBuilder builder = new StringBuilder();
137            cells.keySet().stream().sorted().forEach(cell -> {
138                builder.append(cell)
139                        .append("\t")
140                        .append(cells.get(cell))
141                        .append("\n");
142            });
143            try {
144                Files.writeString(file, builder.toString());
145                System.out.println("Saved " + cells.size() + " cell(s) to " + file + ".");
146            } catch (IOException e) {
147                System.out.println("Could not save file.");
148            }
149        }
150
151        private void load(Path file) {
152            if (!Files.exists(file)) {
153                System.out.println("File not found.");
154                return;
155            }
156            try {
157                cells.clear();
158                for (String line : Files.readAllLines(file)) {
159                    String[] parts = line.split("\\t", 2);
160                    if (parts.length == 2) {
161                        cells.put(parts[0], parts[1]);
162                    }
163                }
164                System.out.println("Loaded " + cells.size() + " cell(s).");
165            } catch (IOException e) {
166                System.out.println("Could not load file.");
167            }
168        }
169
170        private String normalizeCell(String raw) {
171            String cell = raw.trim().toUpperCase();
172            if (cell.matches("[A-Z]+[1-9][0-9]*")) {
173                return cell;
174            }
175            return null;
176        }
177    }
178}

Run It

1javac TuiSpreadsheet.java
2java TuiSpreadsheet

Why this is beginner-friendly

  • One public class, one nested class.
  • Formula handling is intentionally tiny and readable.
  • You get immediate feedback on every command.