Mnoho aplikací Java spuštěných z příkazového řádku bere argumenty k řízení jejich chování. Tyto argumenty jsou k dispozici v argumentu řetězcového pole předaného do statické aplikace hlavní()
metoda. Typicky existují dva typy argumentů: volby (nebo přepínače) a skutečné datové argumenty. Aplikace Java musí tyto argumenty zpracovat a provést dva základní úkoly:
- Zkontrolujte, zda je použitá syntaxe platná a podporovaná
- Načíst skutečná data požadovaná pro aplikaci k provádění jejích operací
Kód, který provádí tyto úkoly, je často vytvořen na míru pro každou aplikaci, a proto vyžaduje značné úsilí jak k vytvoření, tak k udržení, zejména pokud požadavky přesahují jednoduché případy pouze s jednou nebo dvěma možnostmi. The Možnosti
třída popsaná v tomto článku implementuje obecný přístup ke snadné zvládnutí nejsložitějších situací. Třída umožňuje jednoduchou definici požadovaných voleb a argumentů dat a poskytuje důkladné kontroly syntaxe a snadný přístup k výsledkům těchto kontrol. Pro tento projekt byly také použity nové funkce Java 5, jako jsou generické a bezpečné výčty.
Typy argumentů příkazového řádku
V průběhu let jsem napsal několik nástrojů Java, které berou argumenty příkazového řádku, aby kontrolovaly jejich chování. Brzy jsem zjistil, že je nepříjemné ručně vytvářet a udržovat kód pro zpracování různých možností. To vedlo k vývoji třídy prototypu k usnadnění tohoto úkolu, ale tato třída měla jistě svá omezení, protože při důkladném prozkoumání se ukázalo, že počet možných různých variant pro argumenty příkazového řádku je významný. Nakonec jsem se rozhodl vyvinout obecné řešení tohoto problému.
Při vývoji tohoto řešení jsem musel vyřešit dva hlavní problémy:
- Určete všechny varianty, ve kterých se mohou vyskytnout možnosti příkazového řádku
- Najděte jednoduchý způsob, jak uživatelům umožnit vyjádřit tyto variace při použití dosud nevyvinuté třídy
Analýza problému 1 vedla k následujícím pozorováním:
- Možnosti příkazového řádku na rozdíl od argumentů dat příkazového řádku - začněte předponou, která je jednoznačně identifikuje. Příklady předpon zahrnují pomlčku (
-
) na platformách Unix pro možnosti jako-A
nebo lomítko (/
) na platformách Windows. Možnosti mohou být buď jednoduché přepínače (tj.
-A
může být přítomen nebo ne) nebo získat hodnotu. Příkladem je:java MyTool -a -b logfile.inp
Možnosti, které mají hodnotu, mohou mít různé oddělovače mezi skutečným klíčem možnosti a hodnotou. Takovými oddělovači může být prázdné místo, dvojtečka (
:
), nebo znaménko rovná se (=
):java MyTool -a -b logfile.inp java MyTool -a -b: logfile.inp java MyTool -a -b = logfile.inp
Možnosti, které mají hodnotu, mohou přidat další úroveň složitosti. Jako příklad zvažte způsob, jakým Java podporuje definici vlastností prostředí:
java -Djava.library.path = / usr / lib ...
- Takže nad skutečný klíč volby (
D
), oddělovač (=
) a skutečná hodnota opce (/ usr / lib
), další parametr (cesta java.library.path
) může nabývat libovolného počtu hodnot (ve výše uvedeném příkladu lze pomocí této syntaxe zadat řadu vlastností prostředí). V tomto článku se tento parametr nazývá „detail“. - Možnosti mají také vlastnost multiplicity: mohou být povinné nebo volitelné a počet povolených časů se může také lišit (například přesně jednou, jednou nebo vícekrát nebo jinými možnostmi).
Argumenty dat jsou všechny argumenty příkazového řádku, které nezačínají předponou. Zde se přijatelný počet takových datových argumentů může lišit mezi minimálním a maximálním počtem (které nemusí být nutně stejné). Kromě toho aplikace obvykle vyžaduje, aby tyto datové argumenty byly na příkazovém řádku poslední, ale nemusí tomu tak vždy být. Například:
java MyTool -a -b = logfile.inp data1 data2 data3 // Všechna data na konci
nebo
java MyTool -a data1 data2 -b = logfile.inp data3 // Může být přijatelné pro aplikaci
Složitější aplikace mohou podporovat více než jednu sadu možností:
java MyTool -a -b datafile.inp java MyTool -k [-verbose] foo bar duh java MyTool -check -verify logfile.out
- Nakonec se aplikace může rozhodnout ignorovat jakékoli neznámé možnosti nebo může takové možnosti považovat za chybu.
Při navrhování způsobu, jak umožnit uživatelům vyjádřit všechny tyto odrůdy, jsem přišel s následujícím formulářem obecných možností, který se používá jako základ pro tento článek:
[[]]
Tento formulář musí být kombinován s vlastností multiplicity, jak je popsáno výše.
V rámci omezení obecné formy možnosti popsané výše, Možnosti
třída popsaná v tomto článku je navržena tak, aby byla obecným řešením pro všechny potřeby zpracování příkazového řádku, které aplikace Java může mít.
Pomocné třídy
The Možnosti
class, což je základní třída pro řešení popsané v tomto článku, přichází se dvěma pomocnými třídami:
OptionData
: Tato třída obsahuje všechny informace o jedné konkrétní možnostiOptionSet
: Tato třída obsahuje sadu možností.Možnosti
sama může pojmout libovolný počet takových sad
Před popisem podrobností o těchto třídách je třeba zvážit další důležité pojmy Možnosti
třída musí být zavedena.
Typické výčty
Předpona, oddělovač a vlastnost multiplicity byly zachyceny výčty, což je funkce poskytovaná poprvé v Javě 5:
public enum Předpona {DASH ('-'), SLASH ('/'); soukromé char c; soukromá předpona (char c) {this.c = c; } char getName () {return c; }} public enum Separator {COLON (':'), EQUALS ('='), BLANK (''), NONE ('D'); soukromé char c; soukromý oddělovač (char c) {this.c = c; } char getName () {return c; }} veřejné výčet Multiplicity {ONCE, ONCE_OR_MORE, ZERO_OR_ONE, ZERO_OR_MORE; }
Použití výčtu má některé výhody: vyšší bezpečnost typu a těsné a snadné ovládání sady povolených hodnot. Výčty lze také pohodlně použít s generickými kolekcemi.
Všimněte si, že Předpona
a Oddělovač
enums mají své vlastní konstruktory, což umožňuje definici skutečného charakter představující tuto instanci výčtu (oproti název používá se k označení konkrétní instance výčtu). Tyto znaky lze načíst pomocí těchto výčtů getName ()
metody a znaky jsou použity pro java.util.regex
syntaxe vzoru balíčku. Tento balíček se používá k provádění některých kontrol syntaxe v souboru Možnosti
třída, jejíž podrobnosti budou následovat.
The Násobnost
enum v současné době podporuje čtyři různé hodnoty:
JEDNOU
: Možnost musí nastat přesně jednouONCE_OR_MORE
: Možnost se musí objevit alespoň jednouZERO_OR_ONCE
: Možnost může být buď nepřítomná, nebo přítomná přesně jednouZERO_OR_MORE
: Možnost může být buď nepřítomná, nebo může být zobrazena libovolněkrát
V případě potřeby lze snadno přidat další definice.
Třída OptionData
The OptionData
třída je v podstatě datový kontejner: za prvé pro data popisující samotnou možnost a za druhé pro skutečná data nalezená na příkazovém řádku pro tuto možnost. Tento návrh se již projevuje v konstruktoru:
OptionData (Předpona Předpona, Předvolba řetězce, Booleovský detail, Možnost Oddělovač oddělovače, Boolovská hodnota, Možnosti. Multiplicita multiplicita)
Klíč se používá jako jedinečný identifikátor pro tuto možnost. Všimněte si, že tyto argumenty přímo odrážejí zjištění popsaná dříve: úplný popis možnosti musí mít alespoň předponu, klíč a multiplicitu. Možnosti s hodnotou mají také oddělovač a mohou přijímat podrobnosti. Všimněte si také, že tento konstruktor má přístup k balíčku, takže jej aplikace nemohou přímo používat. Třída OptionSet
je addOption ()
metoda přidává možnosti. Tento princip návrhu má tu výhodu, že máme mnohem lepší kontrolu nad skutečnými možnými kombinacemi argumentů použitých k vytvoření OptionData
instance. Například pokud byl tento konstruktor veřejný, můžete vytvořit instanci s nastaveným detailem na skutečný
a hodnota nastavena na Nepravdivé
, což je samozřejmě nesmysl. Místo komplikovaných kontrol v samotném konstruktoru jsem se rozhodl poskytnout kontrolovanou sadu addOption ()
metody.
Konstruktor také vytvoří instanci java.util.regex.Vzorek
, který se používá pro proces porovnávání vzorů této možnosti. Jedním příkladem by mohl být vzor pro možnost, která má hodnotu, žádné podrobnosti a neprázdný oddělovač:
pattern = java.util.regex.Pattern.compile (prefix.getName () + klíč + oddělovač.getName () + "(. +) $");
The OptionData
třída, jak již bylo zmíněno, uchovává také výsledky kontrol prováděných Možnosti
třída. Poskytuje následující veřejné metody pro přístup k těmto výsledkům:
int getResultCount () String getResultValue (int index) String getResultDetail (int index)
První metoda, getResultCount ()
, vrací počet nalezených možností. Tento návrh metody přímo navazuje na multiplicitu definovanou pro tuto možnost. U možností, které mají hodnotu, lze tuto hodnotu načíst pomocí getResultValue (int index)
metoda, kde se index může pohybovat mezi 0
a getResultCount () - 1
. U možností hodnot, které také přijímají podrobnosti, k nim lze podobně přistupovat pomocí getResultDetail (int index)
metoda.
Třída OptionSet
The OptionSet
třída je v podstatě kontejner pro sadu OptionData
instance a také datové argumenty nalezené na příkazovém řádku.
Konstruktor má tvar:
OptionSet (Options.Prefix prefix, Optionss.Multiplicity defaultMultiplicity, String setName, int minData, int maxData)
Tento konstruktor má opět přístup k balíčku. Sady možností lze vytvářet pouze prostřednictvím Možnosti
třída je jiná addSet ()
metody. Výchozí multiplicita pro zde uvedené možnosti může být přepsána při přidání možnosti do sady. Zde zadaný název sady je jedinečný identifikátor používaný k označení sady. minData
a maxData
jsou minimální a maximální počet přijatelných datových argumentů pro tuto sadu.
Veřejné API pro OptionSet
obsahuje následující metody:
Obecné metody přístupu:
Řetězec getSetName () int getMinData () int getMaxData ()
Metody přidání možností:
OptionSet addOption (klíč řetězce) OptionSet addOption (klíč řetězce, multiplicita multiplicity) OptionSet addOption (klíč řetězce, oddělovač oddělovače) OptionSet addOption (klíč řetězce, oddělovač oddělovač, multiplicita multiplicity) OptionSet addOption (klíč řetězce, booleovské podrobnosti, oddělovač oddělovače) OptionSet addOption (Klíč řetězce, booleovské podrobnosti, oddělovač oddělovače, multiplicita multiplicita)
Metody přístupu k datům výsledků kontroly:
java.util.ArrayList getOptionData () OptionData getOption (řetězec) boolean isSet (řetězec) java.util.ArrayList getData () java.util.ArrayList getUnmatched ()
Všimněte si, že metody pro přidání možností, které berou a Oddělovač
argument vytvořit OptionData
instance přijímající hodnotu. The addOption ()
metody vracejí samotnou instanci sady, což umožňuje řetězení volání:
Možnosti options = nové Možnosti (args); options.addSet ("MySet"). addOption ("a"). addOption ("b");
Po provedení kontrol jsou jejich výsledky k dispozici prostřednictvím zbývajících metod. getOptionData ()
vrací seznam všech OptionData
instance, zatímco getOption ()
umožňuje přímý přístup ke konkrétní možnosti. isSet (řetězec)
je pohodlná metoda, která kontroluje, zda byly možnosti na příkazovém řádku nalezeny alespoň jednou. getData ()
poskytuje přístup k nalezeným datovým argumentům, zatímco getUnmatched ()
vypíše seznam všech možností nalezených na příkazovém řádku, pro které neexistuje shoda OptionData
instance byly nalezeny.
Třída Možnosti
Možnosti
je základní třída, se kterou budou aplikace interagovat. Poskytuje několik konstruktorů, které všechny přebírají pole řetězců argumentů příkazového řádku, které hlavní()
metoda poskytuje jako první argument:
Možnosti (String args []) Options (String args [], int data) Options (String args [], int defMinData, int defMaxData) Options (String args [], Multiplicity defaultMultiplicity) Options (String args [], Multiplicity defaultMultiplicity, int data) Options (String args [], Multiplicity defaultMultiplicity, int defMinData, int defMaxData) Options (String args [], Prefix prefix) Options (String args [], Prefix prefix, int data) Options (String args [], Prefix prefix, int defMinData, int defMaxData) Options (String args [], Prefix prefix, Multiplicity defaultMultiplicity) Options (String args [], Prefix prefix, Multiplicity defaultMultiplicity, int data) Options (String args [], Prefix prefix, Multiplicity defaultMultiplicity, int defMinData, int defMaxData)
První konstruktor v tomto seznamu je nejjednodušší pomocí všech výchozích hodnot, zatímco poslední je nejobecnější.
Tabulka 1: Argumenty pro konstruktory Options () a jejich význam
|