1

I have non-standard question.

Lets say I have class MyClass that I execute from command line with list of arguments

[<key> <value>]

like:

MyClass -key1 arg1 -key2 arg2 -key3 arg3 ... -keyN argN.

therefore my class has followed method that passes input data to fileds:

public class MyClass{

private static String arg1;
private static String arg2;
private static String arg3;
...
private static String argN;

private static void getArgs(String[] args) {
    for(int k=0; k<args.length; k=k+2){
        if(args[k].equals("-key1")){ 
            arg1 = args[k+1];                 
        }
        else if(args[k].equals("-key2")){ 
            arg2 = args[k+1];                 
        }   
        else if(args[k].equals("-key3")){ 
            arg3 = args[k+1];                 
        }

                    ...

        else if(args[k].equals("-keyN")){ 
            argN = args[k+1];                 
        }
    }   
}

I write automation and have about 30-40 MyClass's where each one has 10-30 parameters.

All classes have the same fields and different ones as well (50/50).

I don't like private static void getArgs(String[] args) method implementation, seems waste of time.

I though to write in Notepad++ some script based on Reg-ex and generates java code but before I want to ask if someone knows other technologies/techniques to take care about input parameters.

[EDIT]

I thought to use reflection, something like:

for(int k=0; k<args.length; k=k+2){
 Field field = classInstance.getClass().getDeclaredField(args[k].substring(1));
 field.setAccessible(true);
 field.set(classInstance, args[k]);
}

Thank you,

Maxim Shoustin
  • 77,483
  • 27
  • 203
  • 225
  • If you only need to cover the given example, you might want to create a common ``parse`` method which returns a ``Map`` with keys/values. – qqilihq Aug 11 '13 at 12:36
  • Well, but after I need parse Map to extract values – Maxim Shoustin Aug 11 '13 at 12:43
  • You may need a java getopt alternative. Look [here](http://stackoverflow.com/questions/1524661/the-best-cli-parser-for-java%20here) – maniek Aug 11 '13 at 12:57
  • 1
    your last edit clarifies it a bit more... you'll save coding time using reflection for all your classes but, did you notice that your fields are `static`? I'd rather add a modifier check after getting the `Field` instance (to verify it's static) and then invoke the field as `field.set(null, args[k]);` because, there is no instance of a class when dealing with `static` members. – Alonso Dominguez Aug 11 '13 at 13:01
  • Thanks for comment, yes, I know, I can pay this price to cut off dev. time – Maxim Shoustin Aug 11 '13 at 13:06

4 Answers4

4

Commons CLI already provides a nice solution to this problem. I would suggest using this as it is already well tested and used. No sense reinventing the library.

Jeff Storey
  • 56,312
  • 72
  • 233
  • 406
2

It is convenient to pass run time arguments to the java program when there are few arguments to be provided. But not in scenarios when there is a huge list of params to be passed to the program. I would suggest instead of using command line arguments to provide your info, you should rather use properties file. And to your advantage, Properties file also holds the data in key value pair and it makes more sense for your requirement. Also using the java Properties class to read values from the file is very convenient.

And if you need you can pass the properties file name(with full path) as an argument to your java class.

Juned Ahsan
  • 67,789
  • 12
  • 98
  • 136
  • For sure I can pass `Properties` file but after I need to use `ResourceBundle` and extract all data. This is also additional task. I thought to go to reflection direction. – Maxim Shoustin Aug 11 '13 at 12:46
  • @MaximShoustin Aren't resource bundles saved in the form of properties file, i believe yes. This link should help you for what you need: http://docs.oracle.com/javase/tutorial/i18n/resbundle/propfile.html – Juned Ahsan Aug 11 '13 at 12:51
  • Thanks for link, I have automation core that have based on arguments, totally I have about 300-400 java classes and I can't change infra to handle Properties, anyways thanks – Maxim Shoustin Aug 11 '13 at 12:57
1

In all honesty, I would just parse them and put them in a map. And use a constant key to get the value from the map whenever you need it: private static final String KEY_FIRSTKEY = "firstkey";.

So I would simply write a function parseArgs to put everything in a map. (Perhaps this belongs in an utility class). Here is a quick example (without exception handling)

  private static Map<String, String> parseArgs(String... pArgs)
  {
    if (pArgs == null) return null;

    Map<String, String> map = new HashMap<String, String>();
    String arg;
    String key;
    String value;
    for (int i = 0, iLength = pArgs.length; i < iLength; i += 2)
    {
      arg = pArgs[i];

      // GET KEY
      if (!arg.startsWith("-")) continue;
      key = arg.substring(1);

      // GET VALUE
      if ((i + 1) >= pArgs.length) break;
      value = pArgs[i + 1];

      // PUT IT IN MAP
      map.put(key, value);
    }
    return (map.isEmpty()) ? null : map;
  }

But if you really prefer to put them in private fields. Then I propose to use some Reflection to convert the map to fields afterwards. Here is an example:

  private static void mapStaticFields(Map<String, String> pMap, Class pStaticFieldHolder)
          throws NoSuchFieldException
  {
    if (pMap == null) return;
    for (Map.Entry<String, String> entry : pMap.entrySet())
    {
      try
      {
        Field field = pStaticFieldHolder.getDeclaredField(entry.getKey());
        field.setAccessible(true);
        field.set(field, entry.getValue());
      }
      catch (IllegalAccessException iae)
      {
        // impossible
      }
    }
}

Putting it all together in an example:

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class MainTest
{
  private static String key1;
  private static String key2;
  private static String key3;

  public static void main(String... pArgs)
  {
    try
    {
      Map<String, String> argMap = parseArgs(pArgs);
      mapStaticFields(argMap, MainTest.class);

      System.out.println(key1);
      System.out.println(key2);
      System.out.println(key3);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
}

Invoking this from the commandline as java MainTest -key1 val1 -key2 val2 -key3 val3 will result in the following output:

val1
val2
val3

Enjoy !

bvdb
  • 22,839
  • 10
  • 110
  • 123
0

It should be trivial to build a Map from the argument list:

Map<String, String> map = new HashMap<>();
for (i=0; i < args.length-1; i+=2) {
     map.put(args[i], args[i+1];
}

You can then initialize your arguments like:

String arg1 = map.get("-key1");
...
Ingo
  • 36,037
  • 5
  • 53
  • 100