I have a command line with different parameters (strings and an int value).
The problem is that i have both spaces and = characters in this input string, which Java recognizes as separators.Now i wonder how to parse this into my program.
I look for a possibility to make it as simple as possible. The parameter values must also be passed to various subroutines. So I'm looking for a way to easily access the parameters to pass them to subroutines and at the same time control that each command line contains these parameters (and that they actually contain valid value).
Maybe someone could give me a hint how to do this the most "correct" way.
Something like this:
public class Demo {
public static void main(String[] args) {
Map<String, String> cliParams = new HashMap<>();
// Full set of required parameters
cliParams.put("t", null);
cliParams.put("vo", null);
cliParams.put("q", null);
for (String arg : args) {
String[] nameAndValue = arg.split("=", 2);
if (nameAndValue.length != 2)
throw new IllegalArgumentException("Invalid parameter syntax: " + arg);
String name = nameAndValue[0];
String value = nameAndValue[1];
if (cliParams.replace(name, value) != null)
throw new IllegalArgumentException("Parameter given more than once: " + arg);
}
cliParams.forEach((k, v) -> {
if (v == null)
throw new IllegalStateException("Required parameter missing: " + k);
});
int q = Integer.parseInt(cliParams.get("q"));
}
}
Related
I have some java code I'm attempting to translate into vb.net. It uses 'predicates', a feature I did not know existed until now, but which exists in vb.net too.
So I read documentation on it, but none of it explains the java code I have, specifically, if you have a variable 'p' declared as a predicate, what does p.arity do, and what does p.arg(0) and p.arg(1) return?
My general impression was that a predicate takes a set of objects and returns a subset that meets a particular criteria, (for instance, given the set of all basketball players, return only those that are over 6 feet tall). But the following code doesn't seem to have anything to do with that:
// st is assumed to be a list of ECHO predicates
//
private void parseInput(StreamTokenizer st) throws IOException
{
while (st.nextToken() != StreamTokenizer.TT_EOF) {
st.pushBack();
Predicate p = new Predicate(st);
parsePredicate(p);
} // while
}
// PRE: p is non-null
// POST: performs the additions to the ECHO graph required by the
// ECHO predicate; if p is not a legal ECHO predicate, an
// IOException is thrown
//
private void parsePredicate(Predicate p) throws IOException
{
Assert.notNull(p);
String name = p.name();
if (name.equalsIgnoreCase(explainPred)) {
parseExplain(p);
} else if (name.equalsIgnoreCase(dataPred)) {
parseData(p);
} else if (name.equalsIgnoreCase(contradictPred)) {
parseContradict(p);
} else if (name.equalsIgnoreCase(analogyPred)) {
parseAnalogy(p);
} else if (name.equalsIgnoreCase(propositionPred)) {
parseProposition(p);
} else {
throw new IOException("ECHO: "+name+" is not a legal predicate name. "+
"Must be one of "+explainPred+", "+dataPred+", "+
contradictPred+", or "+analogyPred);
} // if
}
//
// PRE: p's name is explainPred
// POST: adds the given explanation predicate to ECHO, adding all necessary
// links; throws an exception if p is not a legal explains predicate
//
private void parseExplain(Predicate p) throws IOException
{
//msg("in parseExplain...");
int arity = p.arity();
float weight;
List propList;
String explainee;
if (arity == 2) {
//msg("arity == 2");
propList = (List)p.arg(0);
explainee = (String)p.arg(1);
weight = (float)explainWeight(propList.length());
} else if (arity == 3) {
//msg("arity == 3");
propList = (List)p.arg(0);
explainee = (String)p.arg(1);
float strength = Float.valueOf((String)p.arg(2)).floatValue();
weight = (float)(strength*explainWeight(propList.length()));
} else {
throw new IOException("ECHO: an explains predicate must have 2 or 3 "+
"arguments: "+p);
} // if
In this case I assume the following:
arity is the amount of parameters the function takes. You can see that after checking if arity is equals to 2, it reads two arguments (arg(0) and arg(1)). So arg is just the method to get the parameter/argument at the given index.
Then when arity is 3, you can get three args; 0, 1, 2
One of a method in my API, doesn't take Java.lang.Object type as an argument but it takes all the sub types of it as an argument (java.lang.Integer, java.lang.String etc).
Now, I want to store the DataType in a list by doing:
List<Object> listObjects = new ArrayList<Object>();
if(listObjects.get(0) instanceof Integer){
//then do
listDataTypesForCast.add(Integer);
}
so that, I can cast like this:
myMethod((listDataTypesForCast.get(0)"returning Java.lang.Object datatype"))
But, I don't know how to declare my List: listDataTypesForCast, so that I can use it for Casting. Please let me know if you know the answer?
PS: I'm using Apache POI library and setCellValue() method there can't have java.lang.Object as a DataType in arguments, and I can't check the DataType at the time of inserting the value in cell because it is in a loop and it will add too much of boiler plate code.
If you want to call one of many overloads of a single-argument method, but don't know the argument type until run-time, you can do it using reflection.
Here is a helper method for doing that:
private static void call(Object obj, String methodName, Object arg) {
Class<?> argClass = arg.getClass();
// Try simple approach
Method methodToCall;
try {
methodToCall = obj.getClass().getMethod(methodName, argClass);
} catch (#SuppressWarnings("unused") NoSuchMethodException unused) {
methodToCall = null;
}
// Search for method, if simple approach didn't work
if (methodToCall == null) {
List<Method> candidates = new ArrayList<>();
for (Method method : obj.getClass().getMethods()) { // Note: Public methods only
if (method.getParameterCount() == 1 && method.getName().equals(methodName)) {
Parameter parameter = method.getParameters()[0];
if (parameter.getType().isAssignableFrom(argClass))
candidates.add(method);
}
}
if (candidates.isEmpty()) {
throw new NoSuchMethodError(obj.getClass().getName() + '.' +
methodName + '(' + argClass.getName() + ')');
}
if (candidates.size() > 1) {
// Implement extended overload resolution logic, if needed
throw new NoSuchMethodError("Multiple candidates found for parameter type " +
argClass.getName() + ": " + candidates);
}
methodToCall = candidates.get(0);
}
// Call method
try {
methodToCall.invoke(obj, arg);
} catch (IllegalAccessException e) {
throw new IllegalAccessError(e.getMessage());
} catch (InvocationTargetException e) {
throw new RuntimeException("Checked exception: " + e.getCause(), e);
}
}
Test
public static void main(String[] args) {
Test obj = new Test();
for (Object arg : Arrays.asList("Foo", 42, 42L, 42f, 42d))
call(obj, "myMethod", arg);
}
public void myMethod(String s) {
System.out.println("String: " + s);
}
public void myMethod(Number s) {
System.out.println("Number: " + s);
}
public void myMethod(Long s) {
System.out.println("Long: " + s);
}
Output
String: Foo
Number: 42
Long: 42
Number: 42.0
Number: 42.0
However, my problem can be solved using Reflection (as suggested by Andreas) but, in this particular case I decided to not use reflection extensively because I've a lot of data to write to excel cells and therefore, I used the following approach:
Find out the data type of each data (of type Object) using instanceof, for the first row cells and then saving the result to Enum. Because rest of the rows share the same datatype across the columns.
Now, looping through the Enum and setting the cell values and data type.
I believe instaceof is also using reflection internally but, I just have to use it once.
I don't know if this is possible in Java but I was wondering if it is possible to use an object in Java to return multiple values without using a class.
Normally when I want to do this in Java I would use the following
public class myScript {
public static void main(String[] args) {
// initialize object class
cl_Object lo_Object = new cl_Object(0, null);
// populate object with data
lo_Object = lo_Object.create(1, "test01");
System.out.println(lo_Object.cl_idno + " - " + lo_Object.cl_desc);
//
// code to utilize data here
//
// populate object with different data
lo_Object = lo_Object.create(2, "test02");
System.out.println(lo_Object.cl_idno + " - " + lo_Object.cl_desc);
//
// code to utilize data here
//
}
}
// the way I would like to use (even though it's terrible)
class cl_Object {
int cl_idno = 0;
String cl_desc = null;
String cl_var01 = null;
String cl_var02 = null;
public cl_Object(int lv_idno, String lv_desc) {
cl_idno = lv_idno;
cl_desc = lv_desc;
cl_var01 = "var 01";
cl_var02 = "var 02";
}
public cl_Object create(int lv_idno, String lv_desc) {
cl_Object lo_Object = new cl_Object(lv_idno, lv_desc);
return lo_Object;
}
}
// the way I don't really like using because they get terribly long
class Example {
int idno = 0;
String desc = null;
String var01 = null;
String var02 = null;
public void set(int idno, String desc) {
this.idno = idno;
this.desc = desc;
var01 = "var 01";
var02 = "var 02";
}
public int idno() {
return idno;
}
public String desc() {
return desc;
}
public String var01() {
return var01;
}
public String var02() {
return var02;
}
}
Which seems like a lot of work considering in Javascript (I know they are different) I can achieve the same effect just doing
var lo_Object = f_Object();
console.log(lo_Object["idno"] + " - " + lo_Object[desc]);
function f_Object() {
var lo_Object = {};
lo_Object = {};
lo_Object["idno"] = 1;
lo_Object["desc"] = "test01";
return lo_Object;
}
NOTE
I know the naming convention is wrong but it is intentional because I have an informix-4gl program that runs with this program so the coding standards are from the company I work for
The best way to do this is to use HashMap<String, Object>
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<String, Object> person =
new HashMap<String, Object>();
// add elements dynamically
person.put("name", "Lem");
person.put("age", 46);
person.put("gender", 'M');
// prints the name value
System.out.println(person.get("name"));
// asures that age element is of integer type before
// printing
System.out.println((int)person.get("age"));
// prints the gender value
System.out.println(person.get("gender"));
// prints the person object {gender=M, name=Lem, age=46}
System.out.println(person);
}
}
The advantage of doing this is that you can add elements as you go.
The downside of this is that you will lose type safety like in the case of the age. Making sure that age is always an integer has a cost. So to avoid this cost just use a class.
No, there is no such a feature, you have to type out the full type name(class name).
Or use may use val :
https://projectlombok.org/features/val.html
Also, if you use IntelliJ IDEA
try this plugin :
https://bitbucket.org/balpha/varsity/wiki/Home
I am not sure if it's possible with Java. Class is the primitive structure to generate Object. We need a Class to generate object. So, for the above code, i don't think there is a solution.
Java methods only allow one return value. If you want to return multiple objects/values consider returning one of the collections. Map, List, Queue, etc.
The one you choose will depend on your needs. For example, if you want to store your values as key-value pairs use a Map. If you just want to store values sequentially, use a list.
An example with a list:
list<Object> myList = new ArrayList<Object>();
myList.add("Some value");
return myList;
As a side note, your method create is redundant. You should use getters and setters to populate the object, or populate it through the constructor.
cl_Object lo_Object = new cl_Object(1, "test01");
The way you have it set up right now, you're creating one object to create another of the same type that has the values you want.
Your naming convention is also wrong. Please refer to Java standard naming convention:
http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-135099.html#367
I am trying to make a plugin that adds "variables" into commands. You use /set (variablename) (value) to set a value and then you can use any command with var:(varname) (For example you could do /set foo bar and then do "/say var:foo" and it would say "bar" in chat) For some reason my
else if(Arrays.toString(args).contains("var:")) {
is either never executing or always returning false. Why is this, and how can I fix it?
Main plugin class:
public class main extends JavaPlugin implements Listener {
List<String> vars = new ArrayList<String>();
public void onEnable()
{
getLogger();
getServer().getPluginManager().registerEvents(this, this);
Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN + "Variables Enabled!");
}
public void onDisable()
{
Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN + "Variables Disabled!");
}
#Override
public boolean onCommand(CommandSender sender, Command command,
String label, String[] args) {
if(command.getName().equalsIgnoreCase("set")) {
vars.add(args[0] + ":" + args[1]);
sender.sendMessage(ChatColor.RED + "Variable " + args[0] + " added with the value " + args[1]);
}else if(Arrays.toString(args).contains("var:")) { //Line problem is on
int size = args.length;
for (int i=0; i<size; i++)
{
if(args[i].contains("var:")) {
String[] parts = args[i].split(":");
for (String temp : vars) {
String[] varname = temp.split(":");
if(varname[1].equals(parts[1])) {
args[i] = varname[2];
}
}
}
}
}
return super.onCommand(sender, command, label, args);
}
}
EDIT: The way I know it is a problem with my else if is that if I add
sender.sendMessage("test"); right under the elseif I never get the message "test" even when I have var: in my args.
EDIT 2: I've figured out one part of it. For some reason whenever I do something like /say or /broadcast the onCommand doesn't get fired...
It seems that your problem is not with the else if but with the innermost if. You compare the variable name of your command (foo) with the previously stored value (bar). Try instead:
if(varname[0].equals(parts[1])) {
args[i] = varname[1];
}
Of course, it would be even better to use more appropriate collection types. For example vars might be a Map<String, String>.
I solved my own problem! The problem is that onCommand only fires when taking commands from your plugin. I had to use a playercommandpreprocessevent for this.
The onCommand() method won't enable you to filter all the command requests being sent by a player, it only lets you implement the commands you listed in your plugin.yml. I would instead use the PlayerCommandPreprocessEvent which does allow you to "catch" all the commands a player tries to execute before they are handled by a specific plugin and change the command as well as the subsequent arguments.
I would also use a HashMap instead of a List that will map the names and values of the String variables to each other. Here's some example code I tested that should work:
//Somewhere else
private HashMap<String, String> variableMap = new HashMap<String, String>();
public boolean onCommand(CommandSender sender, Command command, String alias, String[] args) {
if (command.getName().equalsIgnoreCase("set")) {
if (args.length == 2) { //With this approach, the first argument is the name, the second the value
variableMap.put(args[0], args[1]);
sender.sendMessage(ChatColor.GREEN + "Variable " + args[0] + " now has value " + args[1] + ".");
}
}
return false;
}
#EventHandler
public void onCommand(PlayerCommandPreprocessEvent event) {
String[] args = event.getMessage().split(" "); //Get the message and split it to get the arguments
String newMessage = args[0]; //In this example I preserve the original command, so I'm not changing the first argument
for (int i = 1; i < args.length; i++) {
if (args[i].toLowerCase().startsWith("var:")) { //If it starts with "var:", case insensitive...
String[] parts = args[i].split(":");
if (parts.length == 2 && variableMap.containsKey(parts[1])) { //If it has two parts, and the variable map contains the name of that variable
newMessage += " " + variableMap.get(parts[1]); //Add the variable name and not the original argument
continue;
}
}
newMessage += " " + args[i]; //If the argument is not a variable or it is wrongly formatted, add the original argument to the combined string
}
event.setMessage(newMessage); //Set the new command message or contents
}
How do I recreate a method invocation? when all I've got is the list of methods, obtained by getDeclaredMethods(), and converted into a HashMap<String,Method> and a list of its parameters' Classes, obtained by getParameterTypes().
Suppose I get a string from the user and I want to invoke it:
"print(3,"Hello World!",true,2.4f)"
And the method print(int,String,boolean,float) is part of the getMethods() array. I'm having trouble to figure out how do I compose the invocation. So far this is what I got:
private static final Pattern functionCall = Pattern.compile(String.format("^%s\\(%s?\\)$", "(\\w+)", "(.*)"));
if( (m = functionCall.matcher(line)).find() ) {
String function = m.group(1); // in this example = "print"
String arguments = m.group(2); // in this example = "3,\\"Hello World!\\",true,2.4f"
if( methods.containsKey(function) ) {
Method method = methods.get(function);
Class<?>[] paramsExpected = method.getParameterTypes();
String [] paramsActual = arguments.split(",");
if( paramsExpected.length != paramsActual.length ) {
throw new IllegalArgumentException(function + ": bad number of arguments");
}
for( Class<?> param: paramsExpected) {
???????
}
method.invoke(context, ??????);
To be perfectly clear, I don't know in advance what string the user will input, I have to check it against the available methods and their parameters, and if I find it, then I have to invoke it with the parameters supplied by the user.
This what you need to do. One option is to use the ConverterUtils.convert method of BeanUtils to convert string to object of specific type. This will work for built in types.
Object[] args = new Object[paramsExpected.length];
int i = 0;
for( Class<?> param: paramsExpected) {
args[i] = convertStringToType(paramsActual[i], param);
i = i +1;
}
method.invoke(context, args);
Object convertStringToType(String input, Class<?> type) {
return ConverterUtils.convert(input,type);
}