Programmering

Behandler kommandolinjeargumenter i Java: Saken er avsluttet

Mange Java-applikasjoner startet fra kommandolinjen tar argumenter for å kontrollere oppførselen. Disse argumentene er tilgjengelige i strengarrayargumentet som sendes inn i programmets statiske hoved() metode. Vanligvis er det to typer argumenter: alternativer (eller brytere) og faktiske dataargumenter. Et Java-program må behandle disse argumentene og utføre to grunnleggende oppgaver:

  1. Sjekk om syntaksen er gyldig og støttet
  2. Hent de faktiske dataene som kreves for at applikasjonen skal utføre operasjonene

Ofte er koden som utfører disse oppgavene skreddersydd for hver applikasjon og krever derfor betydelig innsats både for å opprette og vedlikeholde, spesielt hvis kravene går utover enkle tilfeller med bare ett eller to alternativer. De Alternativer klasse beskrevet i denne artikkelen implementerer en generisk tilnærming for å enkelt håndtere de mest komplekse situasjonene. Klassen gir en enkel definisjon av de nødvendige alternativene og dataargumentene, og gir grundige syntakskontroller og enkel tilgang til resultatene av disse kontrollene. Nye Java 5-funksjoner som generikk og typesafe enums ble også brukt til dette prosjektet.

Argumenttyper for kommandolinje

Gjennom årene har jeg skrevet flere Java-verktøy som tar kommandolinjeargumenter for å kontrollere oppførselen. Tidlig syntes jeg det var irriterende å opprette og vedlikeholde koden manuelt for behandling av de forskjellige alternativene. Dette førte til utviklingen av en prototypeklasse for å lette denne oppgaven, men den klassen hadde riktignok sine begrensninger siden antallet nøye mulige forskjellige varianter for kommandolinjeargumenter viste seg å være betydelig ved nøye inspeksjon. Etter hvert bestemte jeg meg for å utvikle en generell løsning på dette problemet.

I utviklingen av denne løsningen måtte jeg løse to hovedproblemer:

  1. Identifiser alle varianter der kommandolinjealternativer kan forekomme
  2. Finn en enkel måte å la brukerne uttrykke disse variantene når de bruker klassen som ennå ikke skal utvikles

Analyse av oppgave 1 førte til følgende observasjoner:

  • Kommandolinjealternativer i motsetning til kommandolinjedataargumenter - start med et prefiks som identifiserer dem unikt. Prefikseksempler inkluderer en bindestrek (-) på Unix-plattformer for alternativer som -en eller en skråstrek (/) på Windows-plattformer.
  • Alternativene kan enten være enkle brytere (dvs. -en kan være til stede eller ikke) eller ta en verdi. Et eksempel er:

    java MyTool -a -b logfile.inp 
  • Alternativer som tar en verdi kan ha forskjellige skilletegn mellom den faktiske opsjonstasten og verdien. Slike separatorer kan være et tomt rom, et kolon (:), eller et likhetstegn (=):

    java MyTool -a -b logfile.inp java MyTool -a -b: logfile.inp java MyTool -a -b = logfile.inp 
  • Alternativer som tar en verdi kan legge til enda et nivå av kompleksitet. Tenk på måten Java støtter definisjonen av miljøegenskaper som et eksempel:

    java -Djava.library.path = / usr / lib ... 
  • Så utover selve alternativtasten (D), skilletegn (=), og opsjonens faktiske verdi (/ usr / lib), en ekstra parameter (java.library.path) kan ta på seg et hvilket som helst antall verdier (i eksemplet ovenfor kan mange miljøegenskaper spesifiseres ved hjelp av denne syntaksen). I denne artikkelen kalles denne parameteren "detalj".
  • Alternativene har også en mangfoldighetsegenskap: de kan være nødvendige eller valgfrie, og antall ganger de er tillatt kan også variere (for eksempel nøyaktig en gang, en gang eller mer, eller andre muligheter).
  • Dataargumenter er alle kommandolinjeargumenter som ikke starter med et prefiks. Her kan det akseptable antallet slike dataargumenter variere mellom et minimum og et maksimalt antall (som ikke nødvendigvis er det samme). I tillegg krever vanligvis et program at disse dataargumentene skal være sist på kommandolinjen, men det trenger ikke alltid være tilfelle. For eksempel:

    java MyTool -a -b = logfile.inp data1 data2 data3 // Alle data på slutten 

    eller

    java MyTool -a data1 data2 -b = logfile.inp data3 // Kan være akseptabelt for et program 
  • Mer komplekse applikasjoner kan støtte mer enn ett sett med alternativer:

    java MyTool -a -b datafile.inp java MyTool -k [-verbose] foo bar duh java MyTool -check -verify logfile.out 
  • Til slutt kan et program velge å ignorere ukjente alternativer eller vurdere slike alternativer som en feil.

Så når jeg utviklet en måte å la brukerne uttrykke alle disse variantene, kom jeg opp med følgende generelle alternativskjema, som brukes som grunnlag for denne artikkelen:

[[]] 

Dette skjemaet må kombineres med mangfoldighetsegenskapen som beskrevet ovenfor.

Innenfor begrensningene for den generelle formen for et alternativ beskrevet ovenfor, er Alternativer klasse beskrevet i denne artikkelen er designet for å være den generelle løsningen for alle kommandolinjebehandlingsbehov som et Java-program måtte ha.

Hjelperklassene

De Alternativer klasse, som er kjerneklassen for løsningen beskrevet i denne artikkelen, kommer med to hjelperklasser:

  1. OptionData: Denne klassen inneholder all informasjon for ett bestemt alternativ
  2. OptionSet: Denne klassen har et sett med alternativer. Alternativer kan inneholde et hvilket som helst antall slike sett

Før du beskriver detaljene i disse klassene, andre viktige konsepter i Alternativer klasse må innføres.

Typesafe enums

Prefikset, skilletegnet og mangfoldighetsegenskapen er fanget opp av enums, en funksjon gitt for første gang av Java 5:

public enum Prefix {DASH ('-'), SLASH ('/'); privat røye c; privat prefiks (char c) {this.c = c; } char getName () {return c; }} offentlig enum Separator {COLON (':'), EQUALS ('='), BLANK (''), INGEN ('D'); privat røye c; privat separator (char c) {this.c = c; } char getName () {return c; }} offentlig enum Multiplicity {ONCE, ONCE_OR_MORE, ZERO_OR_ONE, ZERO_OR_MORE; } 

Bruk av enums har noen fordeler: økt type sikkerhet og stram, uanstrengt kontroll over settet med tillatte verdier. Enums kan også enkelt brukes med generiserte samlinger.

Merk at Prefiks og Separator enums har sine egne konstruktører, slik at definisjonen av en faktisk kan defineres karakter som representerer denne enumforekomsten (versus Navn brukes til å referere til den spesielle enum-forekomsten). Disse tegnene kan hentes ved hjelp av disse enumene ' getName () metoder, og tegnene brukes til java.util.regex pakkens mønstersyntaks. Denne pakken brukes til å utføre noen av syntakskontrollene i Alternativer klasse, som detaljer følger.

De Mangfold enum støtter for tiden fire forskjellige verdier:

  1. EN GANG: Alternativet må forekomme nøyaktig en gang
  2. ONCE_OR_MORE: Alternativet må forekomme minst en gang
  3. ZERO_OR_ONCE: Alternativet kan enten være fraværende eller være til stede nøyaktig en gang
  4. ZERO_OR_MORE: Alternativet kan enten være fraværende eller være til stede et hvilket som helst antall ganger

Flere definisjoner kan lett legges til dersom behovet skulle oppstå.

OptionData-klassen

De OptionData klasse er i utgangspunktet en databeholder: for det første for dataene som beskriver selve alternativet, og for det andre for de faktiske dataene som finnes på kommandolinjen for det alternativet. Denne designen gjenspeiles allerede i konstruktøren:

OptionData (Alternativer. Prefiks, strengnøkkel, boolsk detalj, Alternativer. Separatorseparator, boolsk verdi, Alternativer. Multiple multiplicity) 

Nøkkelen brukes som den unike identifikatoren for dette alternativet. Merk at disse argumentene direkte gjenspeiler funnene beskrevet tidligere: en fullstendig beskrivelse av alternativet må ha minst et prefiks, en nøkkel og mangfold. Alternativer som tar en verdi, har også en skilletegn og kan akseptere detaljer. Legg også merke til at denne konstruktøren har pakketilgang, så applikasjoner kan ikke bruke den direkte. Klasse OptionSets addOption () metoden legger til alternativene. Dette designprinsippet har fordelen at vi har mye bedre kontroll på de faktiske mulige kombinasjonene av argumenter som brukes til å lage OptionData tilfeller. For eksempel, hvis denne konstruktøren var offentlig, kan du opprette en forekomst med detaljer satt til ekte og verdi satt til falsk, som selvfølgelig er tull. I stedet for å ha forseggjorte kontroller i selve konstruktøren, bestemte jeg meg for å gi et kontrollert sett med addOption () metoder.

Konstruktøren lager også en forekomst av java.util.regex.Mønster, som brukes til dette alternativets mønster-matching-prosess. Et eksempel ville være mønsteret for et alternativ som tar en verdi, ingen detaljer og en ikke-blank skilletegn:

mønster = java.util.regex.Pattern.compile (prefix.getName () + nøkkel + separator.getName () + "(. +) $"); 

De OptionData klasse, som allerede nevnt, har også resultatene av kontrollene som er utført av Alternativer klasse. Det gir følgende offentlige metoder for å få tilgang til disse resultatene:

int getResultCount () String getResultValue (int index) String getResultDetail (int index) 

Den første metoden, getResultCount (), returnerer antall ganger et alternativ ble funnet. Denne metodedesignen knytter seg direkte til mangfoldet som er definert for alternativet. For opsjoner som tar en verdi, kan denne verdien hentes ved hjelp av getResultValue (int-indeks) metode, hvor indeksen kan variere mellom 0 og getResultCount () - 1. For verdialternativer som også godtar detaljer, kan du få tilgang til disse på samme måte ved hjelp av getResultDetail (int-indeks) metode.

OptionSet-klassen

De OptionSet klasse er i utgangspunktet en container for et sett med OptionData forekomster og også dataargumentene som finnes på kommandolinjen.

Konstruktøren har formen:

OptionSet (Options.Prefix prefix, Options.Multiplicity defaultMultiplicity, String setName, int minData, int maxData) 

Igjen har denne konstruktøren pakketilgang. Alternativsett kan bare opprettes via Alternativer klassen er annerledes addSet () metoder. Standardmultiplikiteten for alternativene som er angitt her, kan overstyres når du legger til et alternativ i settet. Settnavnet som er angitt her, er en unik identifikator som brukes til å referere til settet. minData og maxData er minimum og maksimum antall akseptable dataargumenter for dette settet.

Den offentlige API for OptionSet inneholder følgende metoder:

Generelle tilgangsmetoder:

Streng getSetName () int getMinData () int getMaxData () 

Metoder for å legge til alternativer:

OptionSet addOption (String key) OptionSet addOption (String key, Multiplicity multiplicity) OptionSet addOption (String key, Separator separator) OptionSet addOption (String key, Separator separator, Multiplicity multiplicity) OptionSet addOption (String key, boolean details, Separator separator) (Strengnøkkel, boolske detaljer, skilletegn, multiplikasjonsmultiplikitet) 

Metoder for å få tilgang til sjekk resultatdata:

java.util.ArrayList getOptionData () OptionData getOption (String key) boolean isSet (String key) java.util.ArrayList getData () java.util.ArrayList getUnmatched () 

Merk at metodene for å legge til alternativer som tar en Separator argument opprette en OptionData eksempel akseptere en verdi. De addOption () metoder returnerer den angitte forekomsten, som tillater påkallingskjedening:

Alternativer alternativer = nye alternativer (args); options.addSet ("MySet"). addOption ("a"). addOption ("b"); 

Etter at kontrollene er utført, er resultatene tilgjengelige gjennom de gjenværende metodene. getOptionData () returnerer en liste over alle OptionData tilfeller, mens getOption () gir direkte tilgang til et bestemt alternativ. isSet (strengnøkkel) er en bekvemmelighetsmetode som sjekker om en opsjon ble funnet minst en gang på kommandolinjen. getData () gir tilgang til dataene som er funnet, mens getUmmatched () viser alle alternativene som er funnet på kommandolinjen som ikke samsvarer OptionData forekomster ble funnet.

Alternativ-klassen

Alternativer er kjerneklassen som applikasjoner vil samhandle med. Det gir flere konstruktører, som alle tar kommandolinjens argumentstrengmatrise som hoved() metoden gir som det første argumentet:

Alternativer (String args []) Alternativer (String args [], int data) Alternativer (String args [], int defMinData, int defMaxData) Alternativer (String args [], Multiplicity default Multiple) Alternativer (String args [], Multiplicity default int data) Alternativer (String args [], Multiplicity default Multiple, 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) Alternativer (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) 

Den første konstruktøren i denne listen er den enkleste ved å bruke alle standardverdiene, mens den siste er den mest generiske.

Tabell 1: Argumenter for Options () -konstruktører og deres betydning

Verdi Beskrivelse Misligholde
prefiksDette konstruktørargumentet er det eneste stedet der et prefiks kan spesifiseres. Denne verdien videreføres til et hvilket som helst opsjonssett og ethvert alternativ som opprettes senere. Tanken bak denne tilnærmingen er at det i en gitt applikasjon viser seg lite sannsynlig at forskjellige prefikser vil trenge å bli brukt.Prefiks DASH
defaultMultiplicityDenne standardmultiplikiteten sendes til hvert alternativsett og brukes som standard for alternativer lagt til et sett uten å spesifisere et mangfold. Selvfølgelig kan denne mangfoldet overstyres for hvert alternativ som legges til.Mangfold
defMinDatadefMinData er standard minimum antall støttede dataargumenter sendt til hvert alternativsett, men det kan selvfølgelig overstyres når du legger til et sett.0
defMaxDatadefMaxData er standard maksimalt antall støttede dataargumenter som sendes til hvert alternativsett, men det kan selvfølgelig overstyres når du legger til et sett.0
$config[zx-auto] not found$config[zx-overlay] not found