Skip to content

Commit da7f546

Browse files
committed
* initial pretty-console commit
0 parents  commit da7f546

File tree

6 files changed

+661
-0
lines changed

6 files changed

+661
-0
lines changed

pom.xml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<groupId>com.github.epochcoder</groupId>
6+
<artifactId>pretty-console</artifactId>
7+
<version>1.0-SNAPSHOT</version>
8+
<packaging>jar</packaging>
9+
10+
<name>pretty-console</name>
11+
<url>http://maven.apache.org</url>
12+
13+
<properties>
14+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15+
</properties>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>junit</groupId>
20+
<artifactId>junit</artifactId>
21+
<version>3.8.1</version>
22+
<scope>test</scope>
23+
</dependency>
24+
<dependency>
25+
<groupId>com.google.guava</groupId>
26+
<artifactId>guava</artifactId>
27+
<version>15.0</version>
28+
</dependency>
29+
</dependencies>
30+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
package com.github.epochcoder.prettyconsole;
2+
3+
import com.github.epochcoder.prettyconsole.handlers.ConsoleBoxPasswordHandler;
4+
import com.google.common.base.Function;
5+
import com.google.common.base.Joiner;
6+
import com.google.common.base.Preconditions;
7+
import com.google.common.base.Splitter;
8+
import com.google.common.base.Strings;
9+
import com.google.common.collect.Iterables;
10+
import java.awt.Font;
11+
import java.awt.FontMetrics;
12+
import java.awt.Graphics;
13+
import java.awt.Graphics2D;
14+
import java.awt.RenderingHints;
15+
import java.awt.image.BufferedImage;
16+
import java.io.PrintStream;
17+
import java.util.ArrayList;
18+
import java.util.List;
19+
import java.util.regex.Pattern;
20+
21+
/**
22+
* represents a console display box in the system console (or any PrintStream).
23+
* used to display friendly technical information
24+
* @author Willie Scholtz
25+
*/
26+
public final class ConsoleBox {
27+
28+
/**
29+
* the character to use in box corners
30+
*/
31+
private static final String BOX_CHAR = " + ";
32+
33+
/**
34+
* the character to use to pad strings with
35+
*/
36+
private static final char PAD_CHAR = ' ';
37+
38+
/**
39+
* the character used for the box's right and left sides
40+
*/
41+
private static final String END_CHAR = " | ";
42+
43+
/**
44+
* the character used for the box's top, title and bottom sides
45+
*/
46+
private static final String TB_CHAR = "-";
47+
48+
/**
49+
* the character used for representing black in a color
50+
*/
51+
private static final String BLACK_CHAR = " ";
52+
53+
/**
54+
* the character used for representing white in a color
55+
*/
56+
private static final String WHITE_CHAR = "#";
57+
58+
/**
59+
* the character used for representing aliasing of fonts.
60+
* actually other colors, but since our image is only black and white
61+
* it will represent a shade, which in this case is aliasing
62+
*/
63+
private static final String ALIAS_CHAR = " ";
64+
65+
/**
66+
* the key value separator for names and values
67+
*/
68+
private static final String KEY_VALUE_SEP = " : ";
69+
70+
71+
private final List<ConsoleBoxKeyHandler> handlers;
72+
private final StringBuilder builder;
73+
private boolean content;
74+
private final int width;
75+
76+
/**
77+
* creates a new instance of a ConsoleBox
78+
* @param boxWidth the width of the box (in character count)
79+
* @param title the initial title of the box, leave blank for none
80+
*/
81+
public ConsoleBox(int boxWidth, String title) {
82+
this.width = boxWidth;
83+
this.builder = new StringBuilder();
84+
this.handlers = new ArrayList<ConsoleBoxKeyHandler>();
85+
86+
// add default password handler
87+
this.handlers.add(new ConsoleBoxPasswordHandler());
88+
89+
if (!Strings.isNullOrEmpty(title)) {
90+
this.title(title);
91+
}
92+
}
93+
94+
/*
95+
* creates a new instance of a ConsoleBox with no title
96+
* @param boxWidth the width of the box (in character count)
97+
*/
98+
public ConsoleBox(int boxWidth) {
99+
this(boxWidth, null);
100+
}
101+
102+
public ConsoleBox handler(ConsoleBoxKeyHandler handler) {
103+
this.handlers.add(handler);
104+
return this;
105+
}
106+
107+
/**
108+
* builds and writes this box to the specified output stream
109+
* @param output
110+
*/
111+
public void build(PrintStream output) {
112+
this.title("");
113+
output.println(this.builder.toString());
114+
}
115+
116+
private String padBoth(String string, String pad, int length) {
117+
int right = (length - string.length()) / 2 + string.length();
118+
String result = Strings.padEnd(string, right, pad.toCharArray()[0]);
119+
return Strings.padStart(result, length, pad.toCharArray()[0]);
120+
}
121+
122+
/**
123+
* adds a title section to the console box
124+
* @param title the title to use
125+
* @return the current box
126+
*/
127+
public ConsoleBox title(String title) {
128+
this.builder.append("\n" + BOX_CHAR).append(padBoth(title,
129+
TB_CHAR, this.width)).append(BOX_CHAR);
130+
131+
return this;
132+
}
133+
134+
/**
135+
* adds an empty line section to the console box
136+
* @return the current box
137+
*/
138+
public ConsoleBox empty() {
139+
this.builder.append("\n" + BOX_CHAR).append(
140+
padBoth("", " ", this.width)).append(BOX_CHAR);
141+
142+
return this;
143+
}
144+
145+
/**
146+
* generates and writes the specified text as an ASCII image into this box
147+
* @param text the text to write as ASCII
148+
* @param invert should the ASCII colors be inverted?
149+
* @return the current box
150+
*/
151+
public ConsoleBox ascii(String text, boolean invert) {
152+
final BufferedImage image = new BufferedImage(this.width,
153+
32, BufferedImage.TYPE_INT_RGB);
154+
155+
final Graphics graphics = image.getGraphics();
156+
final Graphics2D g2d = (Graphics2D) graphics;
157+
158+
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
159+
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
160+
161+
Font textFont = new Font("Dialog", Font.BOLD, 22);
162+
FontMetrics textMetrics = g2d.getFontMetrics(textFont);
163+
g2d.setFont(textFont);
164+
165+
int tX = (image.getWidth() / 2) - (textMetrics.stringWidth(text) / 2);
166+
int tY = (image.getHeight() / 2) + (textMetrics.getHeight() / 2) - 5;
167+
168+
g2d.drawString(text, tX, tY);
169+
g2d.drawRenderedImage(image, null);
170+
g2d.dispose();
171+
172+
final int iHeight = image.getHeight();
173+
final int iWidth = image.getWidth();
174+
175+
final String bChar = invert ? WHITE_CHAR : BLACK_CHAR;
176+
final String wChar = invert ? BLACK_CHAR : WHITE_CHAR;
177+
178+
for (int y = 0; y < iHeight; y++) {
179+
final StringBuilder sb = new StringBuilder();
180+
for (int x = 0; x < iWidth; x++) {
181+
final int rgbColor = image.getRGB(x, y);
182+
sb.append(rgbColor == -16777216 ? bChar : rgbColor == -1 ? wChar : ALIAS_CHAR);
183+
}
184+
185+
if (sb.toString().trim().isEmpty()) {
186+
continue;
187+
}
188+
189+
this.builder.append("\n" + END_CHAR)
190+
.append(sb).append(END_CHAR);
191+
}
192+
193+
return this;
194+
}
195+
196+
/**
197+
* adds a informational line to the console box,
198+
* automatically splitting large values
199+
* @param key the name of the value to display
200+
* @param value the value of this line
201+
* @return the current box
202+
*/
203+
public ConsoleBox line(String key, String value) {
204+
key = Strings.isNullOrEmpty(key) ? "null" : key;
205+
value = Strings.isNullOrEmpty(value) ? "" : value;
206+
207+
// get the key length
208+
final int kL = key.length();
209+
// calculate remaining box space for the value
210+
final int ths = (this.width - kL - KEY_VALUE_SEP.length());
211+
Preconditions.checkState(ths > -1, "key[" + key + "] is to long "
212+
+ "for box with a " + width + " width!");
213+
214+
// \n | the_key_length_in_spaces
215+
final String joinOn = ("\n" + END_CHAR + Strings.padEnd("",
216+
kL + KEY_VALUE_SEP.length(), PAD_CHAR));
217+
218+
// get key handlers and modify if neccessary
219+
for (ConsoleBoxKeyHandler handler : this.handlers) {
220+
if (handler.shouldHandle(key)) {
221+
value = handler.handleValue(key, value);
222+
// don't break, possibilitty of multiple handlers
223+
}
224+
}
225+
226+
// if a key handler returns null, a key should be skipped
227+
if (value != null) {
228+
// split the string on either length or new lines
229+
Iterable<String> splitted = Splitter.on(Pattern
230+
.compile("(?<=\\G.{" + ths + "})|\\n")).split(value);
231+
232+
// add the value + end characters (multiple lines)
233+
String formatted = Joiner.on(joinOn).join(
234+
Iterables.transform(splitted, new Function<String, String>() {
235+
@Override
236+
public String apply(String input) {
237+
return Strings.padEnd(input, ths, ' ') + END_CHAR;
238+
}
239+
}));
240+
241+
// write completed line to builder
242+
this.builder.append("\n" + END_CHAR).append(key)
243+
.append(KEY_VALUE_SEP).append(formatted);
244+
245+
this.content = true;
246+
}
247+
248+
return this;
249+
}
250+
251+
/**
252+
* @return true if {@link #line(java.lang.String, java.lang.String)}
253+
* has been called at least once
254+
*/
255+
public boolean hasContent() {
256+
return content;
257+
}
258+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.github.epochcoder.prettyconsole;
2+
3+
/**
4+
* interface for defining handlers for certain console box keys
5+
* @author Willie Scholtz
6+
* @version 1.43
7+
*/
8+
public interface ConsoleBoxKeyHandler {
9+
10+
/**
11+
* determines if the specified key should be handled by this key handler
12+
* @param key the key to check
13+
* @return true if this key should be handled by this key handler
14+
*/
15+
public boolean shouldHandle(final String key);
16+
17+
/**
18+
* if this handler is set to handle the specified key it, will execute this method to handle it's value
19+
* @param key the key being handled
20+
* @param value the original value to handle, note that console box automatically handles new lines and long lines
21+
* @return the formatted value for ConsoleBox, or null if the whole line should be skipped
22+
*/
23+
public String handleValue(final String key, final String value);
24+
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.github.epochcoder.prettyconsole.handlers;
2+
3+
import com.github.epochcoder.prettyconsole.ConsoleBoxKeyHandler;
4+
import java.util.regex.Pattern;
5+
6+
/**
7+
* ensures no passwords are present in the ConsoleBox
8+
* @author Willie Scholtz
9+
* @version 1.43
10+
*/
11+
public class ConsoleBoxPasswordHandler implements ConsoleBoxKeyHandler {
12+
/**
13+
* pattern defining which attributes should be blurred
14+
*/
15+
private static final Pattern IGNORE_ATT_PATT = Pattern
16+
.compile("pass(word)?", Pattern.CASE_INSENSITIVE);
17+
18+
/**
19+
* replacement value for sensitive data
20+
*/
21+
private static final String SAFE_REPLACEMENT = "*****";
22+
23+
/**
24+
* determines if a certain parameter is safe to transmit over the wire
25+
* @param key the name of the parameter that will be sent
26+
* @return true if the parameter may be sent
27+
*/
28+
private static boolean safeToTransmit(final String key) {
29+
return !IGNORE_ATT_PATT.matcher(key).find();
30+
}
31+
32+
@Override
33+
public boolean shouldHandle(String key) {
34+
return !safeToTransmit(key);
35+
}
36+
37+
@Override
38+
public String handleValue(String key, String value) {
39+
return SAFE_REPLACEMENT;
40+
}
41+
}

0 commit comments

Comments
 (0)