/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.openejb.client;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Properties;
/**
* @version $Rev$ $Date$
*/
@SuppressWarnings("UseOfSystemOutOrSystemErr")
public class CommandParser {
private final Map<String, Option> opts = new LinkedHashMap<String, Option>();
public CommandParser() {
init();
}
protected void init() {
}
protected List<String> validate(final Arguments arguments) {
return new ArrayList<String>();
}
protected List<String> usage() {
return new ArrayList<String>();
}
public void category(final String name) {
opts.put("-@" + name, new Category(name));
}
public Option opt(final String _long) {
return opt(new Option(_long));
}
public Option opt(final char _short, final String _long) {
return opt(new Option(_short, _long));
}
private Option opt(final Option o) {
opts.put(o.getName(), o);
if (o.getMini() != null) {
opts.put(o.getMini(), o);
}
return o;
}
private void help() {
final Iterator<String> usage = usage().iterator();
if (usage.hasNext()) {
System.out.println("Usage: " + usage.next());
}
while (usage.hasNext()) {
System.out.println(" or " + usage.next());
}
final List<Option> seen = new ArrayList<Option>();
for (final Option option : opts.values()) {
if (seen.contains(option)) {
continue;
}
seen.add(option);
if (option instanceof Category) {
System.out.println("");
System.out.println(option.getName() + ":");
continue;
}
final StringBuilder s = new StringBuilder();
if (option.getMini() != null) {
s.append(" -");
s.append(option.getMini());
s.append(", ");
} else {
s.append(" ");
}
s.append("--");
s.append(option.getName());
if (option.getType() != null) {
s.append("=");
s.append(option.getType().getSimpleName());
}
if (option.getDescription() != null || option.getValue() != null) {
for (int i = s.length(); i < 29; i++) {
s.append(' ');
}
s.append(' ');
s.append(' ');
if (option.getDescription() != null) {
s.append(option.getDescription());
}
if (option.getValue() != null) {
if (option.getDescription() != null) {
s.append(" ");
}
s.append("Default is ");
s.append(option.getValue());
}
}
System.out.println(s.toString());
}
}
public Arguments parse(final String[] args) throws HelpException, InvalidOptionsException {
final List<String> list = new ArrayList<String>(Arrays.asList(args));
final ListIterator<String> items = list.listIterator();
final Properties props = new Properties();
while (items.hasNext()) {
String arg = items.next();
if (!arg.startsWith("-")) {
break;
}
items.remove();
final boolean longOption = arg.startsWith("--");
arg = arg.replaceFirst("-+", "");
if (longOption) {
final String name;
final String value;
if (arg.equals("help")) {
props.put("help", "");
continue;
} else if (arg.contains("=")) {
name = arg.substring(0, arg.indexOf("="));
value = arg.substring(arg.indexOf("=") + 1);
} else {
name = arg;
final Option option = opts.get(name);
value = value(items, option);
}
final Option option = opts.get(name);
checkOption("--" + name, option);
checkValue("--" + name, value, option);
props.put(option.getName(), value);
} else {
final char[] chars = new char[arg.length()];
arg.getChars(0, chars.length, chars, 0);
for (int i = 0; i < chars.length; i++) {
final char c = chars[i];
final String name = c + "";
final Option option = opts.get(name);
checkOption("-" + name, option);
String value = "";
if (i + 1 >= chars.length) {
// Last char so, next item could be a value
value = value(items, option);
}
checkValue("-" + c, value, option);
props.put(option.getName(), value);
}
}
}
final Arguments arguments = new Arguments(new Options(props), list);
if (arguments.options().has("help")) {
help();
throw new HelpException();
}
final List<String> issues = validate(arguments);
if (issues.size() > 0) {
for (final String issue : issues) {
System.err.println(issue);
}
System.err.println();
help();
throw new InvalidOptionsException();
}
return arguments;
}
private void checkValue(final String optString, final String value, final Option option) {
checkOption(optString, option);
if (option.getType() == null) {
return;
}
if (value == null || value.equals("")) {
System.err.println("Missing param " + optString + " <" + option.getType().getSimpleName() + ">");
System.exit(1);
}
}
private void checkOption(final String invalid, final Option option) {
if (option != null) {
return;
}
System.err.println("Unknown option: " + invalid);
help();
System.exit(1);
}
private String value(final ListIterator<String> items, final Option option) {
if (option.getType() != null && items.hasNext()) {
final String possibleValue = items.next();
if (!possibleValue.startsWith("-")) {
items.remove();
// next item is the value
return possibleValue;
} else {
// next item is an option
items.previous();
}
}
return "";
}
public static final class Arguments {
private final Options options;
private final List<String> args;
public Arguments(final Options options, final List<String> args) {
this.options = options;
this.args = args;
}
public Options options() {
return options;
}
public List<String> get() {
return args;
}
}
public static class Category extends Option {
public Category(final String name) {
super(name);
}
}
public static class Option {
private final String mini;
private final String name;
private String description;
private Class type;
private Object value;
Option(final char mini, final String name) {
this(mini + "", name);
}
Option(final String name) {
this(null, name);
}
Option(final String mini, final String name) {
this.mini = mini;
this.name = name;
}
public Option description(String description) {
if (!description.endsWith(".")) {
description += ".";
}
this.setDescription(description);
return this;
}
public Option type(final Class type) {
this.setType(type);
return this;
}
public Option value(final Object value) {
this.setValue(value);
if (getType() == null) {
this.setType(value.getClass());
}
return this;
}
public String getMini() {
return mini;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public void setDescription(final String description) {
this.description = description;
}
public Class getType() {
return type;
}
public void setType(final Class type) {
this.type = type;
}
public Object getValue() {
return value;
}
public void setValue(final Object value) {
this.value = value;
}
}
public static class HelpException extends Exception {
}
public static class InvalidOptionsException extends Exception {
}
}