docs: private void processMethod(final Method method) {
      CommandArgument annotation = method.getAnnotation(CommandArgument.class);
      if (annotation != null) {
        final String name = method.getName();
        final Class<?>[] paramTypes = method.getParameterTypes();
        final Annotation[][] paramAnnotations = method.getParameterAnnotations();
        final ArgumentProcessor<Object[]> argProcessor = new ArgumentProcessor<Object[]>();
        final Object[] defaultParams = new Object[paramTypes.length];
        final List<Integer> positionalParams = new ArrayList<Integer>();
  
        String key = annotation.value();
        if (key.equals("")) {
          key = name;
        }
  
        for (int i = 0; i < paramAnnotations.length; i++) {
          Class<?> paramType = paramTypes[i];
          if (paramType == Integer.class || paramType == int.class) {
            defaultParams[i] = 0;
          } else if (paramType == Long.class || paramType == long.class) {
              defaultParams[i] = 0L;
          } else if (paramType == Double.class || paramType == double.class) {
            defaultParams[i] = 0.0;
          } else if (paramType == Float.class || paramType == float.class) {
            defaultParams[i] = (float) 0.0;
          } else if (paramType == String.class) {
            defaultParams[i] = "";
          } else if (paramType == File.class) {
            defaultParams[i] = null;
          } else if (paramType == Boolean.class || paramType == boolean.class) {
            defaultParams[i] = false;
          } else {
            throw new IllegalArgumentException("Invalid type (" + paramType.getCanonicalName() + ") for option parameter: method=" + method.getDeclaringClass().getCanonicalName() + "." + name);
          }
  
          int j;
          for (j = 0; j < paramAnnotations[i].length; j++) {
            if (paramAnnotations[i][j] instanceof OptionArgument) {
              OptionArgument optAnnotation = (OptionArgument) paramAnnotations[i][j];
              String optKey = optAnnotation.value();
              if (optKey.equals("")) {
                throw new IllegalArgumentException("OptionArgument on parameter requires key (method=`" + method.getDeclaringClass().getCanonicalName() + "." + name + "').");
              }
  
              char optShortKey = optAnnotation.shortKey();
              if (optShortKey == '\0') {
                optShortKey = optKey.charAt(0);
              }
  
              final int index = i;
              if (paramType == Integer.class || paramType == int.class) {
                argProcessor.addOption(optKey, optShortKey, new Command<Object[]>() {
                  public void process(Queue<String> argq,
                      Object[] state) {
                    state[index] = Integer.parseInt(argq.remove());
                  }
                });
              } else if (paramType == Long.class || paramType == long.class) {
                argProcessor.addOption(optKey, optShortKey, new Command<Object[]>() {
                  public void process(Queue<String> argq,
                      Object[] state) {
                    state[index] = Long.parseLong(argq.remove());
                  }
                });
              } else if (paramType == Boolean.class || paramType == boolean.class) {
                argProcessor.addOption(optKey, optShortKey, new Command<Object[]>() {
                  public void process(Queue<String> argq,
                      Object[] state) {
                    state[index] = true;
                  }
                });
              } else if (paramType == String.class) {
                argProcessor.addOption(optKey, optShortKey, new Command<Object[]>() {
                  public void process(Queue<String> argq,
                      Object[] state) {
                    state[index] = argq.remove();
                  }
                });
              } else if (paramType == Double.class || paramType == double.class) {
                argProcessor.addOption(optKey, optShortKey, new Command<Object[]>() {
                  public void process(Queue<String> argq,
                      Object[] state) {
                    state[index] = Double.parseDouble(argq.remove());
                  }
                });
              } else if (paramType == Float.class || paramType == float.class) {
                argProcessor.addOption(optKey, optShortKey, new Command<Object[]>() {
                  public void process(Queue<String> argq,
                      Object[] state) {
                    state[index] = Float.parseFloat(argq.remove());
                  }
                });
              } else if (paramType == File.class) {
                argProcessor.addOption(optKey, optShortKey, new Command<Object[]>() {
                  public void process(Queue<String> argq,
                      Object[] state) {
                    state[index] = new File(argq.remove());
                  }
                });
              }
  
              break;
            }
          }
  
          // If there is no OptionArgument annotation, treat it as a
          // positional parameter.
          if (j >= paramAnnotations[i].length) {
            positionalParams.add(i);
          }
        }
  
        // The default command will parse out all the positional parameters
        // from what's left over after all the options have been processed.
        argProcessor.setDefaultCommand(new Command<Object[]>() {
          public void process(Queue<String> argq, Object[] state) {
            for (int index : positionalParams) {
              if (argq.isEmpty()) {
                break;
              }
              Class<?> paramType = paramTypes[index];
              if (paramType == Integer.class || paramType == int.class) {
                state[index] = Integer.parseInt(argq.remove());
              } else if (paramType == Long.class || paramType == long.class) {
                state[index] = Long.parseLong(argq.remove());
              } else if (paramType == Double.class || paramType == double.class) {
                state[index] = Double.parseDouble(argq.remove());
              } else if (paramType == Float.class || paramType == float.class) {
                state[index] = Float.parseFloat(argq.remove());
              } else if (paramType == String.class) {
                state[index] = argq.remove();
              } else if (paramType == File.class) {
                state[index] = new File(argq.remove());
              } else if (paramType == Boolean.class || paramType == boolean.class) {
                state[index] = Boolean.parseBoolean(argq.remove());
              } else {
                throw new UnexpectedException();
              }
            }
          }
        });
  
        // Add the command that processes its arguments and invokes the
        // method.
        addCommand(key, new Command<T>(){
          public void process(Queue<String> argq, T state) {
            Object[] params = defaultParams.clone();
            argProcessor.process(argq, params);
            try {
              method.invoke(state, params);
            } catch (IllegalArgumentException e) {
              throw new UnexpectedException(e);
            } catch (IllegalAccessException e) {
              throw new UnexpectedException(e);
            } catch (InvocationTargetException e) {
              throw new UnexpectedException(e);
            }
          }
        });
      }
    }