Added LightOptionParser, NullOutputStream, bugfixes.

LightOptionParser
-----------------

Lightweight, fairly featureful option parsing mechanism idiomatic to Groovy.
Takes in a map of parameter definitions and returns a map of option values and
arguments.

NullOutputStream
----------------

Ignores all data sent its way.

ParameterizedSocket
-------------------

Added trace and debug logging to the message handling code.

SmartConfig
-----------

Fixed a bug where a property lookup on a non-existent property with no default
given caused a null pointer exception trying to resolve the class of the default
value.
This commit is contained in:
Jonathan Bernard 2012-11-27 08:40:17 -06:00
parent a186d6a09d
commit c887a49f8c
5 changed files with 114 additions and 6 deletions

View File

@ -1,6 +1,6 @@
#Thu, 22 Nov 2012 14:39:26 -0600
#Tue, 27 Nov 2012 08:38:33 -0600
name=jdb-util
version=1.7
version=1.8
lib.local=true
build.number=16
build.number=2

View File

@ -12,6 +12,8 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
public class ParameterizedSocket {
@ -50,6 +52,7 @@ public class ParameterizedSocket {
private Socket socket;
private byte[] buffer;
private Charset charset;
private Logger log = LoggerFactory.getLogger(getClass());
public ParameterizedSocket(Socket socket, int bufferSize) {
this.socket = socket;
@ -58,7 +61,8 @@ public class ParameterizedSocket {
public ParameterizedSocket(String ipAddress, int port, int bufferSize)
throws UnknownHostException, IOException {
this(new Socket(ipAddress, port), bufferSize); }
this(new Socket(ipAddress, port), bufferSize);
log.trace("Opened a socket to {}:{}", ipAddress, port); }
public ParameterizedSocket(Socket socket)
throws UnknownHostException, IOException {
@ -76,6 +80,7 @@ public class ParameterizedSocket {
public void writeMessage(Message message) throws IOException {
if (message == null || message.parts.size() == 0) return;
log.debug("Writing a message: [{}].", message);
byte[] messageBytes = formatMessage(message);
socket.getOutputStream().write(messageBytes); }
@ -83,6 +88,8 @@ public class ParameterizedSocket {
List<String> messageList = new ArrayList<String>();
Map<String, String> namedParameters = new HashMap<String, String>();
log.trace("Reading a message.");
if (socket.getInputStream().read() != START_TOKEN_BYTE) {
byte[] errMsg = formatMessage("ERROR", "Invalid command (expected START_TOKEN).");
socket.getOutputStream().write(errMsg); }
@ -135,7 +142,9 @@ public class ParameterizedSocket {
else namedParameters.put(paramName,
new String(buffer, 0, bufIdx, charset)); }
return new Message(messageList, namedParameters); }
Message message = new Message(messageList, namedParameters);
log.debug("Message read: [{}].", message);
return message; }
protected byte[] formatMessage(String... parts) {
return formatMessage(new Message(parts)); }

View File

@ -0,0 +1,86 @@
/**
* # Light Option Parser
* @author Jonathan Bernard (jdbernard@gmail.com)
*
* LightOptionParser parses command-line style options.
* TODO: complete docs.
*/
package com.jdbernard.util
public class LightOptionParser {
public static def parseOptions(def optionDefinitions, List args) {
def returnOpts = [:]
def foundOpts = [:]
def optionArgIndices = []
/// Find all the options.
args.eachWithIndex { arg, idx ->
if (arg.startsWith('--')) foundOpts[arg.substring(2)] = [idx: idx]
else if (arg.startsWith('-')) foundOpts[arg.substring(1)] = [idx: idx] }
/// Look for option arguments.
foundOpts.each { foundName, optInfo ->
def retVal
/// Find the definition for this option.
def optDef = optionDefinitions.find {
it.key == foundName || it.value.longName == foundName }
def optName = optDef.key
optDef = optDef.value
if (!optDef) throw new IllegalArgumentException(
"Unrecognized option: '${args[optInfo.idx]}.")
/// Remember the option index for later.
optionArgIndices << optInfo.idx
/// If there are no arguments, this is a flag.
if ((optDef.arguments ?: 0) == 0) retVal = true
/// Otherwise, read in the arguments
if (optDef.arguments && optDef.arguments > 0) {
/// Not enough arguments left
if ((optInfo.idx + optDef.arguments) >= args.size()) {
throw new Exception("Option '${args[optInfo.idx]}' " +
"expects ${optDef.arguments} arguments.") }
int firstArgIdx = optInfo.idx + 1
/// Case of only one argument
if (optDef.arguments == 1)
retVal = args[firstArgIdx]
/// Case of multiple arguments
else retVal = args[firstArgIdx..<(firstArgIdx + optDef.arguments)]
/// Remember all the option argument indices for later.
(firstArgIdx..<(firstArgIdx + optDef.arguments)).each {
optionArgIndices << it }}
/// Store the value in the returnOpts map
returnOpts[optName] = retVal
if (optDef.longName) returnOpts[optDef.longName] = retVal }
/// Check that all required options have been found.
optionDefinitions.each { optName, optDef ->
/// If this is a required field
if (optDef.required &&
/// and it has not been found, by either it's short or long name.
!(returnOpts[optName] ||
(optDef.longName && returnOpts[longName])))
throw new Exception("Missing required option: '-${optName}'.") }
/// Remove all the option arguments from the args list and return just
/// the non-option arguments.
optionArgIndices.sort().reverse().each { args.remove(it) }
//optionArgIndices = optionArgIndices.collect { args[it] }
//args.removeAll(optionArgIndices)
returnOpts.args = args
return returnOpts }
}

View File

@ -0,0 +1,13 @@
package com.jdbernard.util;
import java.io.OutputStream;
public class NullOutputStream extends OutputStream {
public NullOutputStream() {}
public void close() { }
public void flush() { }
public void write(int b) { }
public void write(byte[] b) { }
public void write(byte[] b, int offset, int length) { }
}

View File

@ -71,7 +71,7 @@ public class SmartConfig {
log.trace("No known interpretation, casting to type of " +
"default argument.")
val = val.asType(defVal.class)
val = val.asType(defVal?.class ?: String)
}
}