12 changed files with 788 additions and 20 deletions
@ -0,0 +1,309 @@ |
|||||||
|
package net.sopod.soim.client.cmd.console; |
||||||
|
|
||||||
|
import com.google.common.base.*; |
||||||
|
import com.google.common.collect.Iterables; |
||||||
|
import net.sopod.soim.client.cmd.console.handlers.ConsoleBoxPasswordHandler; |
||||||
|
|
||||||
|
import java.awt.*; |
||||||
|
import java.awt.image.BufferedImage; |
||||||
|
import java.io.PrintStream; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
/** |
||||||
|
* represents a console display box in the system console (or any PrintStream). |
||||||
|
* used to display friendly technical information |
||||||
|
* @author Willie Scholtz |
||||||
|
*/ |
||||||
|
public final class ConsoleBox { |
||||||
|
|
||||||
|
/** |
||||||
|
* the character to use in box corners |
||||||
|
*/ |
||||||
|
private String boxChar = " + "; |
||||||
|
|
||||||
|
/** |
||||||
|
* the character to use to pad strings with |
||||||
|
*/ |
||||||
|
private char padChar = ' '; |
||||||
|
|
||||||
|
/** |
||||||
|
* the character used for the box's right and left sides |
||||||
|
*/ |
||||||
|
private String endChar = " | "; |
||||||
|
|
||||||
|
/** |
||||||
|
* the character used for the box's top, title and bottom sides |
||||||
|
*/ |
||||||
|
private String borderChar = "-"; |
||||||
|
|
||||||
|
/** |
||||||
|
* the character used for representing black in a color |
||||||
|
*/ |
||||||
|
private String blackChar = " "; |
||||||
|
|
||||||
|
/** |
||||||
|
* the character used for representing white in a color |
||||||
|
*/ |
||||||
|
private String whiteChar = "#"; |
||||||
|
|
||||||
|
/** |
||||||
|
* the character used for representing aliasing of fonts. |
||||||
|
* actually other colors, but since our image is only black and white |
||||||
|
* it will represent a shade, which in this case is aliasing |
||||||
|
*/ |
||||||
|
private String aliasChar = " "; |
||||||
|
|
||||||
|
/** |
||||||
|
* the key value separator for names and values |
||||||
|
*/ |
||||||
|
private String keyValueSeparator = " : "; |
||||||
|
|
||||||
|
private final List<ConsoleBoxKeyHandler> handlers; |
||||||
|
private final StringBuilder builder; |
||||||
|
private boolean content; |
||||||
|
private final int width; |
||||||
|
|
||||||
|
/** |
||||||
|
* creates a new instance of a ConsoleBox |
||||||
|
* @param boxWidth the width of the box (in character count) |
||||||
|
* @param title the initial title of the box, leave blank for none |
||||||
|
*/ |
||||||
|
public ConsoleBox(int boxWidth, String title) { |
||||||
|
this.width = boxWidth; |
||||||
|
this.builder = new StringBuilder(); |
||||||
|
this.handlers = new ArrayList<ConsoleBoxKeyHandler>(); |
||||||
|
|
||||||
|
// add default password handler
|
||||||
|
this.handlers.add(new ConsoleBoxPasswordHandler()); |
||||||
|
|
||||||
|
if (!Strings.isNullOrEmpty(title)) { |
||||||
|
this.title(title); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* creates a new instance of a ConsoleBox with no title |
||||||
|
* @param boxWidth the width of the box (in character count) |
||||||
|
*/ |
||||||
|
public ConsoleBox(int boxWidth) { |
||||||
|
this(boxWidth, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* pads a string from both sides |
||||||
|
* @param string the string to pad |
||||||
|
* @param pad the padding character |
||||||
|
* @param length the length to pad |
||||||
|
* @return the padded string |
||||||
|
*/ |
||||||
|
private String padBoth(String string, String pad, int length) { |
||||||
|
int right = (length - string.length()) / 2 + string.length(); |
||||||
|
String result = Strings.padEnd(string, right, pad.toCharArray()[0]); |
||||||
|
return Strings.padStart(result, length, pad.toCharArray()[0]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* configures the box characters, note that sensible defaults are already set. |
||||||
|
* @param cornerChar the character to use in box corners |
||||||
|
* @param padChar the character to use to pad strings with |
||||||
|
* @param sideChar the character used for the box's right and left sides |
||||||
|
* @param borderChar the character used for the box's top, title and bottom sides |
||||||
|
* @return the current instance |
||||||
|
*/ |
||||||
|
public ConsoleBox setBoxCharacters(String cornerChar, char padChar, String sideChar, String borderChar) { |
||||||
|
this.boxChar = Preconditions.checkNotNull(cornerChar); |
||||||
|
this.padChar = Preconditions.checkNotNull(padChar); |
||||||
|
this.endChar = Preconditions.checkNotNull(sideChar); |
||||||
|
this.borderChar = Preconditions.checkNotNull(borderChar); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* configures the box's ASCII characters, note that sensible defaults are already set. |
||||||
|
* @param blackChar the character used for representing black in a color |
||||||
|
* @param whiteChar the character used for representing white in a color |
||||||
|
* @param aliasChar the character used for representing aliasing of fonts. |
||||||
|
* actually other colors, but since our image is only black and white |
||||||
|
* it will represent a shade, which in this case is aliasing. |
||||||
|
* @return the current instance |
||||||
|
*/ |
||||||
|
public ConsoleBox setAsciiCharacters(String blackChar, String whiteChar, String aliasChar) { |
||||||
|
this.blackChar = Preconditions.checkNotNull(blackChar); |
||||||
|
this.whiteChar = Preconditions.checkNotNull(whiteChar); |
||||||
|
this.aliasChar = Preconditions.checkNotNull(aliasChar); |
||||||
|
|
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* configures the box's key-value separator. |
||||||
|
* @param keyValueSeparator the character used for representing black in a color |
||||||
|
* actually other colors, but since our image is only black and white |
||||||
|
* it will represent a shade, which in this case is aliasing. |
||||||
|
* @return the current instance |
||||||
|
*/ |
||||||
|
public ConsoleBox setKeyValueSeparator(String keyValueSeparator) { |
||||||
|
this.keyValueSeparator = Preconditions.checkNotNull(keyValueSeparator); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* adds a new key-value handler for this ConsoleBox |
||||||
|
* @param handler the handler to use |
||||||
|
* @return the current instance |
||||||
|
*/ |
||||||
|
public ConsoleBox handler(ConsoleBoxKeyHandler handler) { |
||||||
|
this.handlers.add(handler); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* builds and writes this box to the specified output stream |
||||||
|
* @param output |
||||||
|
*/ |
||||||
|
public void build(PrintStream output) { |
||||||
|
this.title(""); |
||||||
|
output.println(this.builder.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* adds a title section to the console box |
||||||
|
* @param title the title to use |
||||||
|
* @return the current box |
||||||
|
*/ |
||||||
|
public ConsoleBox title(String title) { |
||||||
|
this.builder.append("\n").append(this.boxChar).append(padBoth(title, |
||||||
|
this.borderChar, this.width)).append(this.boxChar); |
||||||
|
|
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* adds an empty line section to the console box |
||||||
|
* @return the current box |
||||||
|
*/ |
||||||
|
public ConsoleBox empty() { |
||||||
|
this.builder.append("\n").append(this.boxChar).append( |
||||||
|
padBoth("", " ", this.width)).append(this.boxChar); |
||||||
|
|
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* generates and writes the specified text as an ASCII image into this box |
||||||
|
* @param text the text to write as ASCII |
||||||
|
* @param invert should the ASCII colors be inverted? |
||||||
|
* @return the current box |
||||||
|
*/ |
||||||
|
public ConsoleBox ascii(String text, boolean invert) { |
||||||
|
final BufferedImage image = new BufferedImage(this.width, |
||||||
|
32, BufferedImage.TYPE_INT_RGB); |
||||||
|
|
||||||
|
final Graphics graphics = image.getGraphics(); |
||||||
|
final Graphics2D g2d = (Graphics2D) graphics; |
||||||
|
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, |
||||||
|
RenderingHints.VALUE_TEXT_ANTIALIAS_ON); |
||||||
|
|
||||||
|
Font textFont = new Font("Dialog", Font.BOLD, 22); |
||||||
|
FontMetrics textMetrics = g2d.getFontMetrics(textFont); |
||||||
|
g2d.setFont(textFont); |
||||||
|
|
||||||
|
int tX = (image.getWidth() / 2) - (textMetrics.stringWidth(text) / 2); |
||||||
|
int tY = (image.getHeight() / 2) + (textMetrics.getHeight() / 2) - 5; |
||||||
|
|
||||||
|
g2d.drawString(text, tX, tY); |
||||||
|
g2d.drawRenderedImage(image, null); |
||||||
|
g2d.dispose(); |
||||||
|
|
||||||
|
final int iHeight = image.getHeight(); |
||||||
|
final int iWidth = image.getWidth(); |
||||||
|
|
||||||
|
final String bChar = invert ? this.whiteChar : this.blackChar; |
||||||
|
final String wChar = invert ? this.blackChar : this.whiteChar; |
||||||
|
|
||||||
|
for (int y = 0; y < iHeight; y++) { |
||||||
|
final StringBuilder sb = new StringBuilder(); |
||||||
|
for (int x = 0; x < iWidth; x++) { |
||||||
|
final int rgbColor = image.getRGB(x, y); |
||||||
|
sb.append(rgbColor == -16777216 ? bChar : rgbColor == -1 ? wChar : aliasChar); |
||||||
|
} |
||||||
|
|
||||||
|
if (sb.toString().trim().isEmpty()) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
this.builder.append("\n").append(this.endChar) |
||||||
|
.append(sb).append(this.endChar); |
||||||
|
} |
||||||
|
|
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* adds a informational line to the console box, |
||||||
|
* automatically splitting large values |
||||||
|
* @param key the name of the value to display |
||||||
|
* @param value the value of this line |
||||||
|
* @return the current box |
||||||
|
*/ |
||||||
|
public ConsoleBox line(String key, String value) { |
||||||
|
key = Strings.isNullOrEmpty(key) ? "null" : key; |
||||||
|
value = Strings.isNullOrEmpty(value) ? "" : value; |
||||||
|
|
||||||
|
// get the key length
|
||||||
|
final int kL = key.length(); |
||||||
|
final int kSl = this.keyValueSeparator.length(); |
||||||
|
|
||||||
|
// calculate remaining box space for the value
|
||||||
|
final int ths = (this.width - kL - kSl); |
||||||
|
Preconditions.checkState(ths > -1, "key[" + key + "] is to long " |
||||||
|
+ "for box with a " + width + " width!"); |
||||||
|
|
||||||
|
// \n | the_key_length_in_spaces
|
||||||
|
final String joinOn = ("\n" + this.endChar + Strings.padEnd("", |
||||||
|
kL + kSl, this.padChar)); |
||||||
|
|
||||||
|
// get key handlers and modify if neccessary
|
||||||
|
for (ConsoleBoxKeyHandler handler : this.handlers) { |
||||||
|
if (handler.shouldHandle(key)) { |
||||||
|
value = handler.handleValue(key, value); |
||||||
|
// don't break, possibilitty of multiple handlers
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// if a key handler returns null, a key should be skipped
|
||||||
|
if (value != null) { |
||||||
|
// split the string on either length or new lines
|
||||||
|
Iterable<String> splitted = Splitter.on(Pattern |
||||||
|
.compile("(?<=\\G.{" + ths + "})|\\n")).split(value); |
||||||
|
|
||||||
|
// add the value + end characters (multiple lines)
|
||||||
|
String formatted = Joiner.on(joinOn).join( |
||||||
|
Iterables.transform(splitted, new Function<String, String>() { |
||||||
|
@Override |
||||||
|
public String apply(String input) { |
||||||
|
return Strings.padEnd(input, ths, ' ') + endChar; |
||||||
|
} |
||||||
|
})); |
||||||
|
|
||||||
|
// write completed line to builder
|
||||||
|
this.builder.append("\n").append(this.endChar).append(key) |
||||||
|
.append(this.keyValueSeparator).append(formatted); |
||||||
|
|
||||||
|
this.content = true; |
||||||
|
} |
||||||
|
|
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return true if {@link #line(java.lang.String, java.lang.String)} |
||||||
|
* has been called at least once |
||||||
|
*/ |
||||||
|
public boolean hasContent() { |
||||||
|
return content; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,24 @@ |
|||||||
|
package net.sopod.soim.client.cmd.console; |
||||||
|
|
||||||
|
/** |
||||||
|
* interface for defining handlers for certain console box keys |
||||||
|
* @author Willie Scholtz |
||||||
|
*/ |
||||||
|
public interface ConsoleBoxKeyHandler { |
||||||
|
|
||||||
|
/** |
||||||
|
* determines if the specified key should be handled by this key handler |
||||||
|
* @param key the key to check |
||||||
|
* @return true if this key should be handled by this key handler |
||||||
|
*/ |
||||||
|
public boolean shouldHandle(final String key); |
||||||
|
|
||||||
|
/** |
||||||
|
* if this handler is set to handle the specified key it, will execute this method to handle it's value |
||||||
|
* @param key the key being handled |
||||||
|
* @param value the original value to handle, note that console box automatically handles new lines and long lines |
||||||
|
* @return the formatted value for ConsoleBox, or null if the whole line should be skipped |
||||||
|
*/ |
||||||
|
public String handleValue(final String key, final String value); |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,41 @@ |
|||||||
|
package net.sopod.soim.client.cmd.console.handlers; |
||||||
|
|
||||||
|
import net.sopod.soim.client.cmd.console.ConsoleBoxKeyHandler; |
||||||
|
|
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
/** |
||||||
|
* ensures no passwords are present in the ConsoleBox |
||||||
|
* @author Willie Scholtz |
||||||
|
*/ |
||||||
|
public class ConsoleBoxPasswordHandler implements ConsoleBoxKeyHandler { |
||||||
|
/** |
||||||
|
* pattern defining which attributes should be blurred |
||||||
|
*/ |
||||||
|
private static final Pattern IGNORE_ATT_PATT = Pattern |
||||||
|
.compile("pass(word)?", Pattern.CASE_INSENSITIVE); |
||||||
|
|
||||||
|
/** |
||||||
|
* replacement value for sensitive data |
||||||
|
*/ |
||||||
|
private static final String SAFE_REPLACEMENT = "*****"; |
||||||
|
|
||||||
|
/** |
||||||
|
* determines if a certain parameter is safe to transmit over the wire |
||||||
|
* @param key the name of the parameter that will be sent |
||||||
|
* @return true if the parameter may be sent |
||||||
|
*/ |
||||||
|
private static boolean safeToTransmit(final String key) { |
||||||
|
return !IGNORE_ATT_PATT.matcher(key).find(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean shouldHandle(String key) { |
||||||
|
return !safeToTransmit(key); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String handleValue(String key, String value) { |
||||||
|
return SAFE_REPLACEMENT; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,72 @@ |
|||||||
|
|
||||||
|
package net.sopod.soim.client.cmd.console.handlers; |
||||||
|
|
||||||
|
import com.google.common.base.Function; |
||||||
|
import com.google.common.base.Joiner; |
||||||
|
import com.google.common.base.Splitter; |
||||||
|
import com.google.common.collect.Iterables; |
||||||
|
import net.sopod.soim.client.cmd.console.ConsoleBoxKeyHandler; |
||||||
|
|
||||||
|
/** |
||||||
|
* ensures that JVM system properties are handled correctly by consolebox |
||||||
|
* @author Willie Scholtz |
||||||
|
*/ |
||||||
|
public class JvmKeyHandler implements ConsoleBoxKeyHandler { |
||||||
|
|
||||||
|
private static final boolean WINDOWS = System.getProperty("os.name").indexOf("Windows") > -1; |
||||||
|
|
||||||
|
/** |
||||||
|
* keys that should be converted to new lines |
||||||
|
*/ |
||||||
|
private static final String[] CLASSPATHS = new String[] { |
||||||
|
"java.library.path", "java.class.path", "sun.boot.class.path" |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* keys that should be skipped |
||||||
|
*/ |
||||||
|
private static final String[] SKIPS = new String[] { |
||||||
|
"line.seperator" |
||||||
|
}; |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean shouldHandle(String key) { |
||||||
|
for (String string : CLASSPATHS) { |
||||||
|
if (string.equals(key)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (String string : SKIPS) { |
||||||
|
if (string.equals(key)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String handleValue(String key, String value) { |
||||||
|
for (String string : CLASSPATHS) { |
||||||
|
if (string.equals(key)) { |
||||||
|
value = Joiner.on("\n").join(Iterables.transform(Splitter.on(WINDOWS ? ";" : ":").split(value), |
||||||
|
new Function<String, String>() { |
||||||
|
int item = 0; |
||||||
|
@Override |
||||||
|
public String apply(String input) { |
||||||
|
return "[" + ++item + "] " + input; |
||||||
|
} |
||||||
|
})); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (String string : SKIPS) { |
||||||
|
if (string.equals(key)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return value; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,241 @@ |
|||||||
|
package net.sopod.soim.client.prettyconsole; |
||||||
|
|
||||||
|
import com.google.common.base.Charsets; |
||||||
|
import junit.framework.Test; |
||||||
|
import junit.framework.TestCase; |
||||||
|
import junit.framework.TestSuite; |
||||||
|
import net.sopod.soim.client.cmd.console.ConsoleBox; |
||||||
|
import net.sopod.soim.client.cmd.console.ConsoleBoxKeyHandler; |
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream; |
||||||
|
import java.io.PrintStream; |
||||||
|
import java.io.UnsupportedEncodingException; |
||||||
|
import java.util.logging.Level; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
/** |
||||||
|
* Unit test for testing console box |
||||||
|
*/ |
||||||
|
public class ConsoleBoxTest extends TestCase { |
||||||
|
|
||||||
|
/** |
||||||
|
* Create the test case |
||||||
|
* @param testName name of the test case |
||||||
|
*/ |
||||||
|
public ConsoleBoxTest(String testName) { |
||||||
|
super(testName); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return the suite of tests being tested |
||||||
|
*/ |
||||||
|
public static Test suite() { |
||||||
|
return new TestSuite(ConsoleBoxTest.class); |
||||||
|
} |
||||||
|
|
||||||
|
private ConsoleBox basicBox(int width) { |
||||||
|
ConsoleBox box = new ConsoleBox(width); |
||||||
|
box.title("TEST"); |
||||||
|
return box; |
||||||
|
} |
||||||
|
|
||||||
|
private String getBox(ConsoleBox box) { |
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
||||||
|
box.build(new PrintStream(baos)); |
||||||
|
|
||||||
|
String actual = null; |
||||||
|
try { |
||||||
|
actual = baos.toString(Charsets.UTF_8.name()); |
||||||
|
} catch (UnsupportedEncodingException ex) { |
||||||
|
Logger.getLogger(ConsoleBoxTest.class.getName()).log( |
||||||
|
Level.SEVERE, "failed to get UTF-8", ex); |
||||||
|
} |
||||||
|
|
||||||
|
System.out.println(actual); |
||||||
|
return actual; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @throws java.io.UnsupportedEncodingException |
||||||
|
*/ |
||||||
|
public void testCreation() throws UnsupportedEncodingException { |
||||||
|
ConsoleBox box = this.basicBox(10); |
||||||
|
box.line("a", "1"); |
||||||
|
|
||||||
|
final String actual = this.getBox(box); |
||||||
|
final String expected = "\n" + |
||||||
|
" + ---TEST--- + \n" + |
||||||
|
" | a : 1 | \n" + |
||||||
|
" + ---------- + \r\n"; |
||||||
|
|
||||||
|
assertEquals("basic box build", expected, actual); |
||||||
|
} |
||||||
|
|
||||||
|
public void testOverflow() throws UnsupportedEncodingException { |
||||||
|
ConsoleBox box = this.basicBox(10); |
||||||
|
box.line("a", "123456789987654321"); |
||||||
|
|
||||||
|
final String actual = this.getBox(box); |
||||||
|
final String expected = "\n" + |
||||||
|
" + ---TEST--- + \n" + |
||||||
|
" | a : 123456 | \n" + |
||||||
|
" | 789987 | \n" + |
||||||
|
" | 654321 | \n" + |
||||||
|
" | | \n" + |
||||||
|
" + ---------- + \r\n"; |
||||||
|
|
||||||
|
assertEquals("overflow in content", expected, actual); |
||||||
|
} |
||||||
|
|
||||||
|
public void testNewLineOverflow() throws UnsupportedEncodingException { |
||||||
|
ConsoleBox box = this.basicBox(10); |
||||||
|
box.line("a", "1\n2\n3\n4\n5\n6\n7\n8\n9\n12345679abcdefg"); |
||||||
|
|
||||||
|
final String actual = this.getBox(box); |
||||||
|
final String expected = "\n" + |
||||||
|
" + ---TEST--- + \n" + |
||||||
|
" | a : 1 | \n" + |
||||||
|
" | 2 | \n" + |
||||||
|
" | 3 | \n" + |
||||||
|
" | 4 | \n" + |
||||||
|
" | 5 | \n" + |
||||||
|
" | 6 | \n" + |
||||||
|
" | 7 | \n" + |
||||||
|
" | 8 | \n" + |
||||||
|
" | 9 | \n" + |
||||||
|
" | 123456 | \n" + |
||||||
|
" | 79abcd | \n" + |
||||||
|
" | efg | \n" + |
||||||
|
" + ---------- + \r\n"; |
||||||
|
|
||||||
|
assertEquals("overflow in content with new lines", expected, actual); |
||||||
|
} |
||||||
|
|
||||||
|
public void testDefaultPasswordHandling() throws UnsupportedEncodingException { |
||||||
|
ConsoleBox box = this.basicBox(20); |
||||||
|
box.line("password", "supercoolpassword"); |
||||||
|
box.line("pass", "mysecret"); |
||||||
|
|
||||||
|
final String actual = this.getBox(box); |
||||||
|
final String expected = "\n" + |
||||||
|
" + --------TEST-------- + \n" + |
||||||
|
" | password : ***** | \n" + |
||||||
|
" | pass : ***** | \n" + |
||||||
|
" + -------------------- + \r\n"; |
||||||
|
|
||||||
|
|
||||||
|
assertEquals("default password handling", expected, actual); |
||||||
|
} |
||||||
|
|
||||||
|
public void testLinesAndTitles() throws UnsupportedEncodingException { |
||||||
|
ConsoleBox box = this.basicBox(25); |
||||||
|
box.line("keyOne", "valueOne"); |
||||||
|
box.line("keyTwo", "valueTwo"); |
||||||
|
box.title("TITLE"); |
||||||
|
box.line("keyThree", "valueThree"); |
||||||
|
box.line("keyFour", "valueFour"); |
||||||
|
|
||||||
|
final String actual = this.getBox(box); |
||||||
|
final String expected = "\n" + |
||||||
|
" + -----------TEST---------- + \n" + |
||||||
|
" | keyOne : valueOne | \n" + |
||||||
|
" | keyTwo : valueTwo | \n" + |
||||||
|
" + ----------TITLE---------- + \n" + |
||||||
|
" | keyThree : valueThree | \n" + |
||||||
|
" | keyFour : valueFour | \n" + |
||||||
|
" + ------------------------- + \r\n"; |
||||||
|
|
||||||
|
assertEquals("with lines and titles", expected, actual); |
||||||
|
} |
||||||
|
|
||||||
|
public void testAscii() throws UnsupportedEncodingException { |
||||||
|
ConsoleBox box = this.basicBox(60); |
||||||
|
box.line("keyOne", "valueOne"); |
||||||
|
box.line("keyTwo", "valueTwo"); |
||||||
|
box.title("TITLE"); |
||||||
|
box.line("keyThree", "valueThree"); |
||||||
|
box.line("keyFour", "valueFour"); |
||||||
|
box.title(""); |
||||||
|
box.ascii("ASCII", false); |
||||||
|
|
||||||
|
final String actual = this.getBox(box); |
||||||
|
final String expected = "\n" + |
||||||
|
" + ----------------------------TEST---------------------------- + \n" + |
||||||
|
" | keyOne : valueOne | \n" + |
||||||
|
" | keyTwo : valueTwo | \n" + |
||||||
|
" + ----------------------------TITLE--------------------------- + \n" + |
||||||
|
" | keyThree : valueThree | \n" + |
||||||
|
" | keyFour : valueFour | \n" + |
||||||
|
" + ------------------------------------------------------------ + \n" + |
||||||
|
" | ### ### ### | \n" + |
||||||
|
" | #### ######## ####### ### ### | \n" + |
||||||
|
" | ##### ########## ########## ### ### | \n" + |
||||||
|
" | ## ## ## ## ## ## ### ### | \n" + |
||||||
|
" | ## # ## ## ## ### ### | \n" + |
||||||
|
" | ## ## ## ## ### ### | \n" + |
||||||
|
" | ## ## ### ## ### ### | \n" + |
||||||
|
" | ## # ###### ## ### ### | \n" + |
||||||
|
" | ## ## ### ## ### ### | \n" + |
||||||
|
" | ######### ## ## ### ### | \n" + |
||||||
|
" | ########### ## ## ## ### ### | \n" + |
||||||
|
" | ########### ## ## ## ### ### | \n" + |
||||||
|
" | ## ## ### ## ## ## ### ### | \n" + |
||||||
|
" | ## ## ########## ######### ### ### | \n" + |
||||||
|
" | ## ## ######## ####### ### ### | \n" + |
||||||
|
" | ## ## # ### ### | \n" + |
||||||
|
" + ------------------------------------------------------------ + \r\n"; |
||||||
|
|
||||||
|
assertEquals("with ascii text", expected, actual); |
||||||
|
} |
||||||
|
|
||||||
|
public void testCustomHandler() throws UnsupportedEncodingException { |
||||||
|
ConsoleBox box = this.basicBox(25); |
||||||
|
box.handler(new ConsoleBoxKeyHandler() { |
||||||
|
public boolean shouldHandle(String key) { |
||||||
|
return "a".equals(key) || "c".equals(key); |
||||||
|
} |
||||||
|
|
||||||
|
public String handleValue(String key, String value) { |
||||||
|
if ("a".equals(key)) { |
||||||
|
return "a_replace"; |
||||||
|
} |
||||||
|
|
||||||
|
if ("c".equals(key)) { |
||||||
|
return "c_replace"; |
||||||
|
} |
||||||
|
|
||||||
|
return value; |
||||||
|
} |
||||||
|
}); |
||||||
|
box.line("a", "valueOne"); |
||||||
|
box.line("b", "valueTwo"); |
||||||
|
box.line("c", "valueThree"); |
||||||
|
box.line("d", "valueFour"); |
||||||
|
|
||||||
|
final String actual = this.getBox(box); |
||||||
|
final String expected = "\n" + |
||||||
|
" + -----------TEST---------- + \n" + |
||||||
|
" | a : a_replace | \n" + |
||||||
|
" | b : valueTwo | \n" + |
||||||
|
" | c : c_replace | \n" + |
||||||
|
" | d : valueFour | \n" + |
||||||
|
" + ------------------------- + \r\n"; |
||||||
|
|
||||||
|
assertEquals("with a custom handler", expected, actual); |
||||||
|
} |
||||||
|
|
||||||
|
public void testEmpty() throws UnsupportedEncodingException { |
||||||
|
ConsoleBox box = this.basicBox(5); |
||||||
|
box.empty(); |
||||||
|
box.empty(); |
||||||
|
|
||||||
|
final String actual = this.getBox(box); |
||||||
|
final String expected = "\n" + |
||||||
|
" + -TEST + \n" + |
||||||
|
" + + \n" + |
||||||
|
" + + \n" + |
||||||
|
" + ----- + \r\n"; |
||||||
|
|
||||||
|
assertEquals("with empty content", expected, actual); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
package net.sopod.soim.router.api.route; |
||||||
|
|
||||||
|
import org.apache.dubbo.common.URL; |
||||||
|
import org.apache.dubbo.rpc.Invocation; |
||||||
|
import org.apache.dubbo.rpc.Invoker; |
||||||
|
import org.apache.dubbo.rpc.cluster.loadbalance.AbstractLoadBalance; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* ConsistentHashRoute |
||||||
|
* 一致性 hash 服务路由 |
||||||
|
* |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-04-29 14:41 |
||||||
|
*/ |
||||||
|
public class ConsistentHashRoute extends AbstractLoadBalance { |
||||||
|
|
||||||
|
@Override |
||||||
|
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) { |
||||||
|
Invoker<T> invoker = invokers.get(0); |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue