justify: supports bullet lists
[nikiroo-utils.git] / src / be / nikiroo / utils / main / justify.java
CommitLineData
9e50696e
NR
1package be.nikiroo.utils.main;
2
a5d976cd 3import java.util.AbstractMap;
9e50696e
NR
4import java.util.ArrayList;
5import java.util.List;
a5d976cd 6import java.util.Map.Entry;
9e50696e
NR
7import java.util.Scanner;
8
9import be.nikiroo.utils.StringUtils;
10import be.nikiroo.utils.StringUtils.Alignment;
11
12/**
13 * Text justification (left, right, center, justify).
14 *
15 * @author niki
16 */
17public class justify {
18 /**
19 * Syntax: $0 ([left|right|center|justify]) (max width)
20 * <p>
21 * <ul>
22 * <li>mode: left, right, center or full justification (defaults to left)</li>
23 * <li>max width: the maximum width of a line, or "" for "no maximum"
24 * (defaults to "no maximum")</li>
25 * </ul>
26 *
27 * @param args
28 */
29 public static void main(String[] args) {
30 int width = -1;
31 StringUtils.Alignment align = Alignment.LEFT;
32
33 if (args.length >= 1) {
34 align = Alignment.valueOf(args[0].toUpperCase());
35 }
36 if (args.length >= 2) {
37 width = Integer.parseInt(args[1]);
38 }
39
40 // TODO: move to utils?
a5d976cd
NR
41 // Content <-> Bullet spacing (null = no spacing)
42 List<Entry<String, String>> lines = new ArrayList<Entry<String, String>>();
9e50696e 43 Scanner scan = new Scanner(System.in);
67e9a06e 44 scan.useDelimiter("\r\n|[\r\n]");
9e50696e
NR
45 try {
46 StringBuilder previous = null;
47 StringBuilder tmp = new StringBuilder();
a5d976cd
NR
48 String previousItemBulletSpacing = null;
49 String itemBulletSpacing = null;
9e50696e 50 while (scan.hasNext()) {
a5d976cd
NR
51 boolean previousLineComplete = true;
52
53 String current = scan.next().replace("\t", " ");
54 itemBulletSpacing = getItemSpacing(current);
55 boolean bullet = isItemLine(current);
56 if ((previousItemBulletSpacing == null || itemBulletSpacing
57 .length() <= previousItemBulletSpacing.length())
58 && !bullet) {
59 itemBulletSpacing = null;
60 }
61
62 if (itemBulletSpacing != null) {
63 current = current.trim();
64 if (!current.isEmpty() && bullet) {
65 current = current.substring(1);
9e50696e 66 }
a5d976cd
NR
67 current = current.trim();
68 previousLineComplete = bullet;
69 } else {
70 tmp.setLength(0);
71 for (String word : current.split(" ")) {
72 if (word.isEmpty()) {
73 continue;
74 }
9e50696e 75
a5d976cd
NR
76 if (tmp.length() > 0) {
77 tmp.append(' ');
78 }
79 tmp.append(word.trim());
9e50696e 80 }
a5d976cd
NR
81 current = tmp.toString();
82
83 previousLineComplete = current.isEmpty()
84 || previousItemBulletSpacing != null
85 || (previous != null && isFullLine(previous));
9e50696e 86 }
9e50696e
NR
87
88 if (previous == null) {
89 previous = new StringBuilder();
90 } else {
a5d976cd
NR
91 if (previousLineComplete) {
92 lines.add(new AbstractMap.SimpleEntry<String, String>(
93 previous.toString(), previousItemBulletSpacing));
9e50696e 94 previous.setLength(0);
a5d976cd 95 previousItemBulletSpacing = itemBulletSpacing;
9e50696e
NR
96 } else {
97 previous.append(' ');
98 }
99 }
100
101 previous.append(current);
a5d976cd 102
9e50696e
NR
103 }
104
105 if (previous != null) {
a5d976cd
NR
106 lines.add(new AbstractMap.SimpleEntry<String, String>(previous
107 .toString(), previousItemBulletSpacing));
9e50696e
NR
108 }
109 } finally {
110 scan.close();
111 }
112
a5d976cd
NR
113 for (Entry<String, String> line : lines) {
114 String content = line.getKey();
115 String spacing = line.getValue();
116
117 String bullet = "- ";
118 if (spacing == null) {
119 bullet = "";
120 spacing = "";
121 }
122
123 if (spacing.length() > width + 3) {
124 spacing = "";
125 }
126
127 for (String subline : StringUtils.justifyText(content, width
128 - (spacing.length() + bullet.length()), align)) {
129 System.out.println(spacing + bullet + subline);
130 if (!bullet.isEmpty()) {
131 bullet = " ";
132 }
9e50696e
NR
133 }
134 }
135 }
136
137 static private boolean isFullLine(StringBuilder line) {
138 return line.length() == 0 //
139 || line.charAt(line.length() - 1) == '.'
140 || line.charAt(line.length() - 1) == '"'
a5d976cd
NR
141 || line.charAt(line.length() - 1) == 'ยป';
142 }
143
144 static private boolean isItemLine(String line) {
145 String spacing = getItemSpacing(line);
146 return spacing != null && line.charAt(spacing.length()) == '-';
147 }
148
149 static private String getItemSpacing(String line) {
150 int i;
151 for (i = 0; i < line.length(); i++) {
152 if (line.charAt(i) != ' ') {
153 return line.substring(0, i);
154 }
155 }
156
157 return "";
9e50696e
NR
158 }
159}