Banyak aplikasi Java yang dimulai dari baris perintah mengambil argumen untuk mengontrol perilakunya. Argumen ini tersedia dalam argumen larik string yang diteruskan ke main()
metode statis aplikasi . Biasanya, ada dua tipe argumen: opsi (atau sakelar) dan argumen data aktual. Aplikasi Java harus memproses argumen ini dan melakukan dua tugas dasar:
- Periksa apakah sintaks yang digunakan valid dan didukung
- Ambil data aktual yang diperlukan aplikasi untuk menjalankan operasinya
Seringkali, kode yang melakukan tugas-tugas ini dibuat khusus untuk setiap aplikasi dan oleh karena itu membutuhkan usaha yang besar untuk membuat dan memelihara, terutama jika persyaratan melampaui kasus sederhana dengan hanya satu atau dua pilihan. The Options
kelas dijelaskan dalam artikel ini menerapkan pendekatan generik untuk dengan mudah menangani situasi yang paling kompleks. Kelas memungkinkan definisi sederhana dari opsi yang diperlukan dan argumen data, dan menyediakan pemeriksaan sintaks menyeluruh dan akses mudah ke hasil pemeriksaan ini. Fitur Java 5 baru seperti generik dan enum typesafe juga digunakan untuk proyek ini.
Jenis argumen baris perintah
Selama bertahun-tahun, saya telah menulis beberapa alat Java yang menggunakan argumen baris perintah untuk mengontrol perilakunya. Awalnya, saya merasa menjengkelkan untuk membuat dan memelihara kode secara manual untuk memproses berbagai opsi. Hal ini menyebabkan pengembangan kelas prototipe untuk memfasilitasi tugas ini, tetapi kelas tersebut memang memiliki keterbatasan karena, pada pemeriksaan yang cermat, jumlah variasi yang mungkin berbeda untuk argumen baris perintah ternyata signifikan. Akhirnya, saya memutuskan untuk mengembangkan solusi umum untuk masalah ini.
Dalam mengembangkan solusi ini, saya harus menyelesaikan dua masalah utama:
- Identifikasi semua jenis tempat opsi baris perintah dapat muncul
- Temukan cara sederhana untuk memungkinkan pengguna mengekspresikan keragaman ini saat menggunakan kelas yang belum dikembangkan
Analisis Masalah 1 mengarah pada pengamatan berikut:
- Opsi baris perintah yang bertentangan dengan argumen data baris perintah — mulai dengan awalan yang mengidentifikasinya secara unik. Contoh awalan termasuk tanda hubung (
-
) pada platform Unix untuk opsi seperti-a
atau garis miring (/
) pada platform Windows. Opsi dapat berupa sakelar sederhana (yaitu,
-a
dapat hadir atau tidak) atau mengambil nilai. Contohnya adalah:java MyTool -a -b logfile.inp
Opsi yang mengambil nilai dapat memiliki pemisah yang berbeda antara kunci opsi aktual dan nilai. Pemisah seperti itu bisa berupa spasi kosong, titik dua (
:
), atau tanda sama dengan (=
):java MyTool -a -b logfile.inp java MyTool -a -b: logfile.inp java MyTool -a -b = logfile.inp
Opsi yang mengambil nilai dapat menambah satu tingkat kerumitan lagi. Pertimbangkan cara Java mendukung definisi properti lingkungan sebagai contoh:
java -Djava.library.path = / usr / lib ...
- Jadi, di luar key opsi aktual (
D
), separator (=
), dan nilai aktual opsi (/usr/lib
), parameter tambahan (java.library.path
) dapat mengambil sejumlah nilai (dalam contoh di atas, banyak properti lingkungan dapat ditentukan menggunakan sintaks ini ). Dalam artikel ini, parameter ini disebut "detail". - Opsi juga memiliki properti multiplisitas: dapat diperlukan atau opsional, dan berapa kali opsi diizinkan juga dapat bervariasi (seperti tepat sekali, satu kali atau lebih, atau kemungkinan lain).
Argumen data adalah semua argumen baris perintah yang tidak dimulai dengan prefiks. Di sini, jumlah argumen data yang dapat diterima dapat bervariasi antara jumlah minimum dan maksimum (yang tidak harus sama). Selain itu, biasanya aplikasi memerlukan argumen data ini untuk menjadi yang terakhir pada baris perintah, tetapi itu tidak selalu harus demikian. Sebagai contoh:
java MyTool -a -b = logfile.inp data1 data2 data3 // Semua data di akhir
atau
java MyTool -a data1 data2 -b = logfile.inp data3 // Mungkin dapat diterima oleh aplikasi
Aplikasi yang lebih kompleks dapat mendukung lebih dari satu rangkaian opsi:
java MyTool -a -b datafile.inp java MyTool -k [-verbose] foo bar dan java MyTool -check -verify logfile.out
- Akhirnya, aplikasi mungkin memilih untuk mengabaikan opsi yang tidak diketahui atau mungkin menganggap opsi tersebut sebagai kesalahan.
Jadi, dalam menemukan cara untuk memungkinkan pengguna mengekspresikan semua varietas ini, saya datang dengan formulir opsi umum berikut, yang digunakan sebagai dasar untuk artikel ini:
[[]]
Formulir ini harus digabungkan dengan properti multiplisitas seperti yang dijelaskan di atas.
Dalam batasan bentuk umum opsi yang dijelaskan di atas, Options
kelas yang dijelaskan dalam artikel ini dirancang untuk menjadi solusi umum untuk setiap kebutuhan pemrosesan baris perintah yang mungkin dimiliki aplikasi Java.
Kelas pembantu
The Options
kelas, yang merupakan kelas inti untuk solusi yang dijelaskan dalam artikel ini, dilengkapi dengan dua kelas helper:
OptionData
: Kelas ini menampung semua informasi untuk satu opsi tertentuOptionSet
: Kelas ini memiliki serangkaian opsi.Options
sendiri dapat menampung sejumlah set tersebut
Sebelum menjelaskan detail kelas-kelas ini, konsep penting lainnya dari Options
kelas harus diperkenalkan.
Enum yang aman
Awalan, pemisah, dan properti multiplisitas telah ditangkap oleh enum, fitur yang disediakan untuk pertama kalinya oleh Java 5:
public enum Awalan {DASH ('-'), SLASH ('/'); karakter pribadi c; Private Prefix (char c) {this.c = c; } char getName () {return c; }} Pemisah enum publik {COLON (':'), EQUALS ('='), BLANK (''), NONE ('D'); karakter pribadi c; Pemisah pribadi (char c) {this.c = c; } char getName () {return c; }} Multiplisitas enum publik {ONCE, ONCE_OR_MORE, ZERO_OR_ONE, ZERO_OR_MORE; }
Menggunakan enum memiliki beberapa keuntungan: meningkatkan keamanan tipe dan ketat, kontrol yang mudah atas kumpulan nilai yang diizinkan. Enum juga dapat digunakan dengan mudah dengan koleksi umum.
Perhatikan bahwa enum Prefix
dan Separator
memiliki konstruktornya sendiri, yang memungkinkan definisi karakter aktual yang mewakili instance enum ini (versus nama yang digunakan untuk merujuk ke instance enum tertentu). Karakter ini dapat diambil menggunakan getName()
metode enum ini , dan karakter tersebut digunakan untuk java.util.regex
sintaks pola paket. Paket ini digunakan untuk melakukan beberapa pemeriksaan sintaks di Options
kelas, detailnya akan mengikuti.
The Multiplicity
enum saat ini mendukung empat nilai yang berbeda:
ONCE
: Opsi harus muncul tepat satu kaliONCE_OR_MORE
: The option has to occur at least onceZERO_OR_ONCE
: The option can either be absent or present exactly onceZERO_OR_MORE
: The option can either be absent or present any number of times
More definitions can easily be added should the need arise.
The OptionData class
The OptionData
class is basically a data container: firstly, for the data describing the option itself, and secondly, for the actual data found on the command line for that option. This design is already reflected in the constructor:
OptionData(Options.Prefix prefix, String key, boolean detail, Options.Separator separator, boolean value, Options.Multiplicity multiplicity)
The key is used as the unique identifier for this option. Note that these arguments directly reflect the findings described earlier: a full option description must have at least a prefix, a key, and multiplicity. Options taking a value also have a separator and might accept details. Note also that this constructor has package access, so applications cannot directly use it. Class OptionSet
's addOption()
method adds the options. This design principle has the advantage that we have much better control on the actual possible combinations of arguments used to create OptionData
instances. For example, if this constructor were public, you could create an instance with detail set to true
and value set to false
, which is of course nonsense. Rather than having elaborate checks in the constructor itself, I decided to provide a controlled set of addOption()
methods.
The constructor also creates an instance of java.util.regex.Pattern
, which is used for this option's pattern-matching process. One example would be the pattern for an option taking a value, no details, and a nonblank separator:
pattern = java.util.regex.Pattern.compile(prefix.getName() + key + separator.getName() + "(.+)$");
The OptionData
class, as already mentioned, also holds the results of the checks performed by the Options
class. It provides the following public methods to access these results:
int getResultCount() String getResultValue(int index) String getResultDetail(int index)
The first method, getResultCount()
, returns the number of times an option was found. This method design directly ties in with the multiplicity defined for the option. For options taking a value, this value can be retrieved using the getResultValue(int index)
method, where the index can range between 0
and getResultCount() - 1
. For value options that also accept details, these can be similarly accessed using the getResultDetail(int index)
method.
The OptionSet class
The OptionSet
class is basically a container for a set of OptionData
instances and also the data arguments found on the command line.
The constructor has the form:
OptionSet(Options.Prefix prefix, Options.Multiplicity defaultMultiplicity, String setName, int minData, int maxData)
Again, this constructor has package access. Option sets can only be created through the Options
class's different addSet()
methods. The default multiplicity for the options specified here can be overridden when adding an option to the set. The set name specified here is a unique identifier used to refer to the set. minData
and maxData
are the minimum and maximum number of acceptable data arguments for this set.
The public API for OptionSet
contains the following methods:
General access methods:
String getSetName() int getMinData() int getMaxData()
Methods to add options:
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) OptionSet addOption(String key, boolean details, Separator separator, Multiplicity multiplicity)
Methods to access check result data:
java.util.ArrayList getOptionData() OptionData getOption(String key) boolean isSet(String key) java.util.ArrayList getData() java.util.ArrayList getUnmatched()
Note that the methods for adding options that take a Separator
argument create an OptionData
instance accepting a value. The addOption()
methods return the set instance itself, which allows invocation chaining:
Options options = new Options(args); options.addSet("MySet").addOption("a").addOption("b");
After the checks have been performed, their results are available through the remaining methods. getOptionData()
returns a list of all OptionData
instances, while getOption()
allows direct access to a specific option. isSet(String key)
is a convenience method that checks whether an options was found at least once on the command line. getData()
provides access to the data arguments found, while getUnmatched()
lists all options found on the command line for which no matching OptionData
instances were found.
The Options class
Options
is the core class with which applications will interact. It provides several constructors, all of which take the command line argument string array that the main()
method provides as the first argument:
Options(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)
The first constructor in this list is the simplest one using all the default values, while the last one is the most generic.
Table 1: Arguments for the Options() constructors and their meaning
|