package net.wasamon.mjlib.util;
import java.util.ArrayList;
import java.util.StringTokenizer;
/**
* 引数の解析を行い、その結果を保持するクラス。<br>
* GetOpt("hvf:", "prefix:", args)などとして利用<br>
* 最初の引数で一文字のオプションを、第二引数ではロングオプションを設定。<br>
* ":"を末尾につけることで、そのオプションは引数をとることができる。<br>
* この引数は空白または"="で識別される。<br>
* また、オプションに"::"をつけると、その後の文字列すべてを、そのオプションの引数として処理する。<br>
* - や -- の次、また、-ではじまらない文字列からを引数として保持する。<br>
*
* @version $Id: GetOpt.java,v 1.3 2004/05/24 05:24:35 miyo Exp $
* @author Takefumi MIYOSHI
*
*/
public class GetOpt {
private String[] args;
private NamedArrayList opts;
/** 引数つきのオプションを保持するのリスト */
private NamedArrayList opt_with_arg;
/** 引数つきではないオプションフラグのリスト */
private NamedArrayList opt_flag;
/** 解析の結果不明だったリスト */
private ArrayList unknown;
private NamedObject opt_with_arg_rest = new NamedObject("");
private boolean result = true;
/**
* コンストラクタ
*
* @param sp
* 解析したい一文字オプションの連続(-vとか)
* @param lps
* 解析したいロングオプションのcommma separate羅列
* @param ptn
* 解析すべき文字列の配列
*/
public GetOpt(String sp, String lps, String ptn[]) {
this(sp, lps, ptn, 0);
}
/**
* コンストラクタ
*
* @param sp
* 解析したい一文字オプションの連続(-vとか)
* @param lps
* 解析したいロングオプションのcommma separate羅列
* @param ptn
* 解析すべき文字列の配列
* @param offset
* 解析すべき文字のオフセット
*/
public GetOpt(String sp, String lps, String ptn[], int offset) {
args = new String[0];
opt_with_arg = new NamedArrayList();
opt_flag = new NamedArrayList();
opts = new NamedArrayList();
unknown = new ArrayList();
StringTokenizer st = new StringTokenizer(lps, ",");
String lp[] = new String[st.countTokens()];
for (int i = 0; i < lp.length; i++) {
lp[i] = st.nextToken();
}
makeOptList(sp, lp);
analyze(ptn, offset);
}
/**
* デバッグ用コンストラクタ
*
*/
public GetOpt(String sp, String lps, String ptn[], boolean flag) {
this(sp, lps, ptn);
String[] o = getAllOpt();
String[] a = getArgs();
for (int i = 0; i < o.length; i++) {
System.out.println("Option " + o[i]);
if (flag(o[i])) {
try {
System.out.println(" Value=" + getValue(o[i]));
} catch (GetOptException e) {
System.out.println(" Value=");
}
}
}
if (a != null) {
for (int i = 0; i < a.length; i++)
System.out.println("Argument " + a[i]);
}
}
/**
* 引数すべてに対し指定したパターンがあるかどうか判定する
*
* @param ptn
* 引数の配列
* @param offset
* 解析する引数のオフセット
*
* @TODO もっといいアルゴリズムに
*/
private void analyze(String[] ptn, int offset) {
int i = offset;
for (i = offset; i < ptn.length; i++) {
if (ptn[i].charAt(0) != '-') {
break;
}
if ((ptn[i].equals("-") == true) || (ptn[i].equals("--") == true)) {
break;
}
if (opt_with_arg_rest.equals(ptn[i].substring(1))) {
String flag = ptn[i].substring(1);
String rest = "";
i += 1;
while (true) {
rest += ptn[i];
if (i == ptn.length - 1) {
break;
} else {
rest += " ";
i += 1;
}
}
opts.add(new AssocPair(flag, rest));
}
if (ptn[i].charAt(0) == '-') {
if (ptn[i].charAt(1) == '-') {
i += analy_longopt(ptn[i].substring(2), ptn, i);
} else {
i += analy_shortopt(ptn[i].substring(1), ptn, i);
}
}
}
args = setArgs(ptn, i);
}
/**
* 与えられたショートオプションとロングオプションから 引数解析のためのリストを生成する
*/
private boolean makeOptList(String sp, String[] lp) {
int i = 0;
while (i < sp.length()) {
if (sp.length() > (i + 1) && sp.charAt(i + 1) == ':') { /* もし文字の後に':'が続いていた場合引数を伴う */
if (sp.length() > (i + 2) && sp.charAt(i + 2) == ':') { /* もう一つ続いていたらラスト */
opt_with_arg_rest = new NamedObject(sp.substring(i, i + 1));
i += 3;
} else {
opt_with_arg.add(new NamedObject(sp.substring(i, i + 1)));
i += 2;
}
} else {
opt_flag.add(new NamedObject(sp.substring(i, i + 1)));
i += 1;
}
}
i = 0;
while (i < lp.length) {
if (lp[i].charAt(lp[i].length() - 1) == ':') { /* 最終の文字が':'なら引数を伴う */
opt_with_arg.add(new NamedObject(lp[i].substring(0,
lp[i].length() - 1)));
} else {
opt_flag.add(new NamedObject(lp[i]));
}
i += 1;
}
return true;
}
/**
* パターンに該当するフラグオプションがるかどうか
*
* @param ptn
* パターン文字列
* @return 該当オプションがあるかどうか
*/
private int analy_shortopt(String ptn, String arg[], int offset) {
int add = 0;
for (int i = 0; i < ptn.length(); i++) {
String flag = ptn.substring(i, i + 1);
if (opt_flag.has(flag)) {
opts.add(new NamedObject(flag));
add += 0;
} else if (opt_with_arg.has(flag)) {
if (arg.length > offset + 1) {
opts.add(new AssocPair(flag, arg[offset + 1]));
add += 1;
} else {
result = false;
add += 0;
}
}
}
return add;
}
/**
* 引数つきオプションの解析 hoge=fefe または hoge fefe をオプション hoge と、その引数 fefe と解析
*
* @param ptn
* ためすパターン
* @param arg
* オプション字列の配列(引数かもしれないから)
* @param offset
* 現在のパタンの配列中のオフセット
* @return 該当するオプションがあったかどうか
*/
private int analy_longopt(String ptn, String arg[], int offset) {
int add = 0;
if (opt_flag.has(ptn)) {
opts.add(new NamedObject(ptn));
add = 0;
} else if (ptn.matches(".*=.*")) { /* hogehoge=*みたいな形 */
int index = ptn.indexOf("=");
String ptn2 = ptn.substring(0, index);
if (opt_with_arg.has(ptn2)) {
opts.add(new AssocPair(ptn2, ptn.substring(index + 1)));
} else {
result = false;
}
add = 0;
} else if (opt_with_arg.has(ptn)) {
if (arg.length > offset + 1) {
opts.add(new AssocPair(ptn, arg[offset + 1]));
add = 1;
} else {
opts.add(new AssocPair(ptn, ""));
result = true;
add = 0;
}
} else {
result = false;
add = 0;
}
return add;
}
public boolean isSuccess() {
return result;
}
/**
* オプションが指定されていたかどうかを判定する
*
* @param key
* 検索するオプション名
* @return 指定されていた/いなかった
*/
public boolean flag(String key) {
return opts.has(key);
}
/**
* オプションで指定されていた値を取得する。
*
* @param key
* 検索するオプション名
* @return 指定されていた値(文字列)
* @throws GetOptException
* 与えられた文字列のオプションがない場合
*/
public String getValue(String key) throws GetOptException {
Object obj = null;
try {
obj = opts.search(key);
} catch (NoSuchException e) {
throw new GetOptException("no such options." + key);
}
if (obj instanceof AssocPair) {
return ((AssocPair) obj).getValue();
} else {
throw new GetOptException("this option doesn't have any value.");
}
}
/**
* すべてのオプションを配列で得る。
*
* @return オプションの配列
*/
private String[] getAllOpt() {
String[] o = new String[opts.size()];
for (int i = 0; i < opts.size(); i++) {
o[i] = ((NamedObject) opts.get(i)).getName();
}
return o;
}
/**
* すべての引数を配列にして返す
*
* @return 引数の配列
*/
public String[] getArgs() {
return args;
}
/**
* パタンのうちoffset以降を配列に格納して返す
*
* @param ptn
* パタン
* @param offset
* オフセット
* @return 文字列の配列
*/
private String[] setArgs(String[] ptn, int offset) {
int argc = ptn.length - offset;
String[] args = new String[argc];
for (int i = 0; i < argc; i++) {
args[i] = ptn[i + offset];
}
return args;
}
/**
* 名前と値の組を表わすオブジェクト
*/
class AssocPair extends NamedObject {
/** フラグ */
String value;
/**
* コンストラクタ
*
* @param name
* 名前
* @param value
* フラグ
*/
private AssocPair(String name, String value) {
super(name);
this.value = value;
}
private String getValue() {
return value;
}
}
private void print_opt_flag() {
for (int i = 0; i < opt_flag.size(); i++) {
System.out.println(((NamedObject) opt_flag.get(i)).getName());
}
}
private void print_opt_with_arg() {
for (int i = 0; i < opt_with_arg.size(); i++) {
System.out.println(((NamedObject) opt_with_arg.get(i)).getName());
}
}
public static void main(String args[]) throws Exception {
System.out.println("GetOpt test.");
String sp = "vh";
String lp = "version:";
GetOpt go = new GetOpt(sp, lp, args);
go.print_opt_flag();
go.print_opt_with_arg();
String[] o = go.getAllOpt();
String[] a = go.getArgs();
for (int i = 0; i < o.length; i++)
System.out.println("Option " + o[i]);
if (a != null) {
for (int i = 0; i < a.length; i++)
System.out.println("Argument " + a[i]);
} else {
System.out.println("no argument");
}
System.out.println(go.flag("v"));
System.out.println(go.flag("h"));
System.out.println(go.flag("version"));
try {
System.out.println(go.getValue("version"));
} catch (GetOptException e) {
System.out.println("not specified such option.");
}
System.out.println(go.isSuccess());
}
}