/*
* $Id: CommandLineHelper.java 152467 2010-11-24 02:43:02Z jwesterm $
*/
package com.linkedin.databus.util;
/*
*
* Copyright 2013 LinkedIn Corp. All rights reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
/**
* @author Jemiah Westerman<jwesterman@linkedin.com>
* @version $Revision: 152467 $
*/
public class CommandLineHelper
{
public enum ArgumentType {STRING, INTEGER, LONG, FLOAT, DOUBLE, BOOLEAN, DIRECTORY, FILE};
private static class ArgumentInfo
implements Comparable<ArgumentInfo>
{
private final String _name;
private final boolean _required;
private final String _description;
private final ArgumentType _type;
private final Pattern _regexPattern;
public ArgumentInfo(String name, boolean required, String description, ArgumentType type, Pattern regexPattern)
{
_name = name;
_required = required;
_description = description;
_type = type;
_regexPattern = regexPattern;
}
@Override
public int compareTo(ArgumentInfo other)
{
return _name.compareTo(other._name);
}
@Override
public boolean equals(Object o)
{
if (null == o || !(o instanceof ArgumentInfo)) return false;
return _name.equals(((ArgumentInfo)o)._name);
}
@Override
public int hashCode()
{
return _name.hashCode();
}
}
private final Map<String, ArgumentInfo> _argumentInfos = new HashMap<String, ArgumentInfo>();
public void addArgument(String name, boolean required, String description, ArgumentType type)
{
addArgument(name, required, description, type, null);
}
public void addArgument(String name, boolean required, String description, ArgumentType type, String regexPattern)
{
// Argument names should not have the leading dash. We handle that internally and don't want
// confusion from the caller whether or not the Map returned by parseCommandLine has keys with or
// without the dash.
if(name.startsWith("-"))
{
throw new IllegalArgumentException("Argument names should not begin with a leading dash (-).");
}
// If the optional regular expression pattern was specified, then compile the pattern here.
Pattern pattern = null;
if(regexPattern != null)
{
pattern = Pattern.compile(regexPattern);
}
// Create the new ArgumentInfo and add it to the map.
// Note the key in the map has the leading dash.
_argumentInfos.put("-" + name, new ArgumentInfo(name, required, description, type, pattern));
}
/**
* Parse the command line arguments passed in the {@code args} array. Prior to calling this method,
* all legal argument names should be registered with {@code addArgument(...)}.
*/
public Map<String, Object> parseCommandLine(String[] args)
{
Map<String, Object> arguments = new HashMap<String, Object>();
// Should be an even number of arguments in the form -argName argValue
if(args.length % 2 != 0)
{
showUsage("Bad command line");
return null;
}
for(int i=0; i < args.length; i+=2)
{
String argumentName = args[i];
String argumentValue = args[i+1];
// Get the ArgumentInfo for this argumentName. If it is null then it is a bad argument and we abort
ArgumentInfo argumentInfo = _argumentInfos.get(argumentName);
if(argumentInfo == null)
{
showUsage("Bad command line. The argument name does not exist: " + argumentName);
return null;
}
// If the argument has the optional regular expression pattern, make sure the value provided
// matches the pattern
if(argumentInfo._regexPattern != null)
{
if(!argumentInfo._regexPattern.matcher(argumentValue).matches())
{
showUsage("Bad command line. The argument " + argumentName + " must match the pattern " + argumentInfo._regexPattern.toString());
return null;
}
}
// Put the argument value in the return map.
// Do this in a try/catch block so we can capture NumberFormatException etc. if the value is
// not valid for the specified type.
try
{
switch(argumentInfo._type)
{
case BOOLEAN:
arguments.put(argumentInfo._name, Boolean.valueOf(argumentValue));
break;
case DOUBLE:
arguments.put(argumentInfo._name, Double.valueOf(argumentValue));
break;
case FLOAT:
arguments.put(argumentInfo._name, Float.valueOf(argumentValue));
break;
case INTEGER:
arguments.put(argumentInfo._name, Integer.valueOf(argumentValue));
break;
case LONG:
arguments.put(argumentInfo._name, Long.valueOf(argumentValue));
break;
case STRING:
arguments.put(argumentInfo._name, argumentValue);
break;
case FILE:
File file = new File(argumentValue);
if(!file.exists() || file.isDirectory())
{
showUsage("Bad command line. The file " + argumentValue + " does not exist.");
return null;
}
arguments.put(argumentInfo._name, file);
break;
case DIRECTORY:
File dir = new File(argumentValue);
if(!dir.exists() || !dir.isDirectory())
{
showUsage("Bad command line. The directory " + argumentValue + " does not exist.");
return null;
}
arguments.put(argumentInfo._name, dir);
break;
}
}
catch(Exception ex)
{
showUsage("Bad command line. The argument " + argumentName + " had a bad value. It must be of type " + argumentInfo._type);
return null;
}
}
boolean missingRequiredArgs = false;
for(ArgumentInfo argumentInfo : _argumentInfos.values())
{
if(argumentInfo._required && !arguments.containsKey(argumentInfo._name))
{
System.out.println("Missing required argument: " + argumentInfo._name);
missingRequiredArgs = true;
}
}
if(missingRequiredArgs)
{
showUsage();
return null;
}
// Return the map of parsed arguments
return arguments;
}
public void showParsedArguments(Map<String, Object> arguments)
{
showParsedArguments(null, arguments);
}
public void showParsedArguments(String message, Map<String, Object> arguments)
{
// Print the optional message if one was provided, followed by our normal usage header
if(message != null)
{
System.out.println(message);
}
// Print all the arguments that were passed in on the command line
for(Map.Entry<String, Object> entry : arguments.entrySet())
{
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
public void showUsage()
{
showUsage(null);
}
public void showUsage(String message)
{
// Build a sorted list of ArgumentInfos from the _argumentInfos map
List<ArgumentInfo> argumentInfosList = new ArrayList<ArgumentInfo>();
argumentInfosList.addAll(_argumentInfos.values());
Collections.sort(argumentInfosList);
// Print the optional message if one was provided, followed by our normal usage header
if(message != null)
{
System.out.println(message);
}
System.out.println("Arguments: ");
// Print out the required arguments first
for(ArgumentInfo argumentInfo : argumentInfosList)
{
if(argumentInfo._required)
{
System.out.println("\t-" + argumentInfo._name + " (required): " + argumentInfo._description + " (" + argumentInfo._type + ")");
}
}
// Then print out the optional arguments
for(ArgumentInfo argumentInfo : argumentInfosList)
{
if(!argumentInfo._required)
{
System.out.println("\t-" + argumentInfo._name + " (optional): " + argumentInfo._description + " (" + argumentInfo._type + ")");
}
}
}
}