--- /dev/null
+/.settings/
+/.project
+/.classpath
+/target/
+
+/doc/apidocs/
+/doc/index.html
+
+/.idea/
+/*.iml
--- /dev/null
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
--- /dev/null
+:PROPERTIES:
+:ID: bb4f96cd-458c-495b-a605-313b2e3e28d2
+:END:
+#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
+#+TITLE: CLI Helper - library to help implementing commandline interfaces
+#+LANGUAGE: en
+#+LATEX_HEADER: \usepackage[margin=1.0in]{geometry}
+#+LATEX_HEADER: \usepackage{parskip}
+#+LATEX_HEADER: \usepackage[none]{hyphenat}
+
+#+OPTIONS: H:20 num:20
+#+OPTIONS: author:nil
+
+* Overview
+:PROPERTIES:
+:ID: fef7ebc3-0f00-4b82-a926-c0cfdf709762
+:END:
+- See also: [[https://www3.svjatoslav.eu/projects/cli-helper/apidocs/][CLI Helper JavaDoc]]
+
+This is library intended to facilitate creation of commandline
+applications in Java programming language. Library is packaged as an
+artifact to Maven repository. This makes it simple to add library as
+dependency to your project.
+
+Library provides following general functionalities:
+- [[id:4fca35e4-fdf1-4675-a36f-6206d6fb72cb][Asking for user input]]
+- [[id:eb7d5632-6152-4d37-8e55-1cf4da21c204][Commandline arguments processing]]
+
+
+* User input helper
+:PROPERTIES:
+:ID: 4fca35e4-fdf1-4675-a36f-6206d6fb72cb
+:END:
+
+The =CLIHelper= provides user-friendly methods to read different data
+types from standard input. It helps validate user input, display
+prompts, handle default values, and enforce optional constraints like
+numeric ranges or string lengths.
+
+For quick usage, here’s a simple example showing how you might query
+users for a boolean value, an integer, and a string:
+
+#+BEGIN_SRC java
+import eu.svjatoslav.commons.cli_helper.CLIHelper;
+
+public class Demo {
+ public static void main(String[] args) {
+ Boolean proceed = CLIHelper.askBoolean("Do you want to proceed?", true);
+ Integer age = CLIHelper.askInteger("Please enter your age:", 18, 0, 120, false);
+ String name = CLIHelper.askString("What is your name?", "Anonymous");
+
+ System.out.println("Proceed: " + proceed);
+ System.out.println("Age: " + age);
+ System.out.println("Name: " + name);
+ }
+}
+#+END_SRC
+
+See [[https://www3.svjatoslav.eu/projects/cli-helper/apidocs/eu/svjatoslav/commons/cli_helper/CLIHelper.html][Javadoc]] for complete API reference.
+
+* CLI argument helper
+:PROPERTIES:
+:ID: eb7d5632-6152-4d37-8e55-1cf4da21c204
+:END:
+
+See also: [[https://clig.dev/][Command Line Interface Guidelines]].
+
+** Command and argument
+
+Every command-line application has a way of receiving input from
+users, usually in the form of command-line arguments. A command-line
+argument is a piece of information provided to the command-line
+application when it's invoked. These arguments are provided as an
+array of strings. The first element of the array (argument 0) is
+typically the name of the command itself.
+
+In the example below, 'my-video-coder' is our command, and the rest
+are arguments:
+
+#+BEGIN_SRC shell
+my-video-coder encode --input vid1.mp4 vid2.mp4 vid3.mp4 --quality 5
+#+END_SRC
+
+To better understand how these concepts work together, let's break
+down our example command:
+
+| argument # | value(s) | type |
+|------------+----------------------------+-------------------------------|
+| 0 | my-video-coder | command |
+| 1 | encode | [[id:94242e8a-c59b-42fd-8cc7-ba3df1938119][subcommand]] |
+| 2 | --input | [[id:ffedf388-4d23-41eb-98d0-83fd3940b24d][option1]] |
+| 3, 4, 5 | vid1.mp4 vid2.mp4 vid3.mp4 | [[id:8a39d20c-421f-4bc7-94e4-8e561e58bea0][parameters for --input option]] |
+| 6 | --quality | [[id:ffedf388-4d23-41eb-98d0-83fd3940b24d][option2]] |
+| 7 | 5 | [[id:8a39d20c-421f-4bc7-94e4-8e561e58bea0][parameter for --quaily option]] |
+
+** Subcommand
+:PROPERTIES:
+:ID: 94242e8a-c59b-42fd-8cc7-ba3df1938119
+:END:
+
+Subcommands are arguments that invoke more specific action that a
+command can perform. They are often used with commands that have
+multiple functions. In our example, *encode* is a subcommand of
+*my-video-coder*.
+
+** Option
+:PROPERTIES:
+:ID: ffedf388-4d23-41eb-98d0-83fd3940b24d
+:END:
+
+Options are arguments that change the behavior of a command or
+subcommand. They usually start with a dash (-) or double dash
+(--). For instance, *--input* and *--quality* are options in our
+example command.
+
+** Parameter
+:PROPERTIES:
+:ID: 8a39d20c-421f-4bc7-94e4-8e561e58bea0
+:END:
+
+Parameter provides additional information to a command, subcommand or
+option.
+
+For instance, in our example:
+- 'vid1.mp4 vid2.mp4 vid3.mp4' are parameters for the *--input* option.
+- '5' is a parameter for the *--quality* option.
+* Getting the library
+Follow instructions to embed *cli-helper* library in your project.
+
+Add following snippets to your project *pom.xml* file:
+
+#+BEGIN_SRC xml
+<dependencies>
+ ...
+ <dependency>
+ <groupId>eu.svjatoslav</groupId>
+ <artifactId>cli-helper</artifactId>
+ <version>1.2</version>
+ </dependency>
+ ...
+</dependencies>
+
+
+<repositories>
+ ...
+ <repository>
+ <id>svjatoslav.eu</id>
+ <name>Svjatoslav repository</name>
+ <url>http://www3.svjatoslav.eu/maven/</url>
+ </repository>
+ ...
+</repositories>
+#+END_SRC
+* Getting the source code
+- This program is free software: released under Creative Commons Zero
+ (CC0) license
+
+- Program author:
+ - Svjatoslav Agejenko
+ - Homepage: https://svjatoslav.eu
+ - Email: mailto://svjatoslav@svjatoslav.eu
+
+- [[https://www.svjatoslav.eu/projects/][Other software projects hosted at svjatoslav.eu]]
+
+** Source code
+- [[https://www2.svjatoslav.eu/gitweb/?p=cli-helper.git;a=snapshot;h=HEAD;sf=tgz][Download latest snapshot in TAR GZ format]]
+
+- [[https://www2.svjatoslav.eu/gitweb/?p=cli-helper.git;a=summary][Browse Git repository online]]
+
+- Clone Git repository using command:
+ : git clone https://www3.svjatoslav.eu/git/cli-helper.git
+
+- See [[https://www3.svjatoslav.eu/projects/cli-helper/apidocs/][JavaDoc]]
+* TODO
+
+List of improvement suggestions:
+
+- Add more concrete examples of how to use the library in JavaDoc
+ comments. This will help developers quickly get started and learn
+ the API.
+
+- Provide more comprehensive unit tests for CliHelper,
+ ParameterParser, Options and subclasses. This will ensure robustness
+ and stability.
+
+- Add JavaDoc comments to all classes and methods where
+ applicable. This will provide better visibility into the library's
+ functionality for developers.
+
+- Add more option types like date/time, regular expression etc.
+
+- Document best practices for using the library in a larger project.
+
+- Implement support for more complex CLI applications like option
+ dependencies and conflicts resolution.
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>eu.svjatoslav</groupId>
+ <artifactId>cli-helper</artifactId>
+ <version>1.3-SNAPSHOT</version>
+ <packaging>jar</packaging>
+ <name>CLI helper</name>
+ <description>Helper library for implementing commandline interface</description>
+ <url>http://www2.svjatoslav.eu/gitbrowse/cli-helper/doc/index.html</url>
+
+ <organization>
+ <name>svjatoslav.eu</name>
+ <url>http://svjatoslav.eu</url>
+ </organization>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <source>8</source>
+ <target>8</target>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.2.1</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>3.5.0</version>
+ <executions>
+ <execution>
+ <id>attach-javadocs</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+<!-- <configuration>-->
+<!-- <!– workaround for https://bugs.openjdk.java.net/browse/JDK-8212233 –>-->
+<!-- <javaApiLinks>-->
+<!-- <property>-->
+<!-- <name>foo</name>-->
+<!-- <value>bar</value>-->
+<!-- </property>-->
+<!-- </javaApiLinks>-->
+<!-- <!– Workaround for https://stackoverflow.com/questions/49472783/maven-is-unable-to-find-javadoc-command –>-->
+<!-- <javadocExecutable>${java.home}/bin/javadoc</javadocExecutable>-->
+<!-- </configuration>-->
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-release-plugin</artifactId>
+ <version>2.5.2</version>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.maven.scm</groupId>
+ <artifactId>maven-scm-provider-gitexe</artifactId>
+ <version>1.9.4</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+
+ </plugins>
+
+ <extensions>
+ <extension>
+ <groupId>org.apache.maven.wagon</groupId>
+ <artifactId>wagon-ssh-external</artifactId>
+ <version>2.6</version>
+ </extension>
+ </extensions>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.13.2</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <distributionManagement>
+ <snapshotRepository>
+ <id>svjatoslav.eu</id>
+ <name>svjatoslav.eu</name>
+ <url>scpexe://svjatoslav.eu:10006/srv/maven</url>
+ </snapshotRepository>
+ <repository>
+ <id>svjatoslav.eu</id>
+ <name>svjatoslav.eu</name>
+ <url>scpexe://svjatoslav.eu:10006/srv/maven</url>
+ </repository>
+ </distributionManagement>
+
+ <scm>
+ <connection>scm:git:ssh://n0@svjatoslav.eu:10006/home/n0/git/cli-helper.git</connection>
+ <developerConnection>scm:git:ssh://n0@svjatoslav.eu:10006/home/n0/git/cli-helper.git</developerConnection>
+ <tag>HEAD</tag>
+ </scm>
+
+</project>
--- /dev/null
+/*
+ * Svjatoslav Commons - shared library of common functionality. Author: Svjatoslav Agejenko.
+ * This project is released under the Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.commons.cli_helper;
+
+import java.io.File;
+import java.util.Scanner;
+
+/**
+ * <h2>CLIHelper</h2>
+ * <p>
+ * A collection of static convenience methods that simplify interactive command‑line
+ * applications by repeatedly prompting the user until valid input is obtained.
+ * Each {@code ask…} method supports optional default values, <em>nullable</em> results,
+ * value‑range enforcement, and context‑sensitive validation rules (e.g. file
+ * attributes).
+ * </p>
+ *
+ * <h3>Design highlights</h3>
+ * <ul>
+ * <li>Uses a <strong>single</strong> {@link Scanner} instance bound to {@code System.in}
+ * to avoid resource‑leak warnings and to ensure scanners do not compete for
+ * the same underlying input stream.</li>
+ * <li>All methods are <em>blocking</em>; they return only when a syntactically and
+ * semantically valid value has been supplied, or when the API contract
+ * explicitly allows an empty/{@code null} result.</li>
+ * <li>Every prompt automatically appends the current default value in square
+ * brackets (e.g. {@code [42]}) so the user can see what will be chosen on
+ * an empty line.</li>
+ * </ul>
+ *
+ * <h3>Usage example</h3>
+ * <pre>{@code
+ * Boolean proceed = CLIHelper.askBoolean("Continue?", true);
+ * Integer betweenFiveAndTen = CLIHelper.askInteger("Pick 5‑10", 7, 5, 10, false);
+ * String name = CLIHelper.askString("Your name", "Anonymous", 2, 20, false);
+ * }</pre>
+ */
+public final class CLIHelper {
+
+ /**
+ * Shared {@link Scanner} for all input so that we do not open multiple
+ * scanners on {@link System#in System.in} (which would otherwise cause an
+ * {@code IllegalStateException} when one scanner closes the underlying
+ * stream).
+ */
+ private static final Scanner SCANNER = new Scanner(System.in);
+
+ /*‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑ Boolean ‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑*/
+
+ /**
+ * Repeatedly prompts the user for a Boolean value.
+ * <p>
+ * Accepted affirmative tokens (case‑insensitive): <kbd>y</kbd>, <kbd>yes</kbd>,
+ * <kbd>true</kbd>.<br>
+ * Accepted negative tokens: <kbd>n</kbd>, <kbd>no</kbd>, <kbd>false</kbd>.
+ * </p>
+ *
+ * <p>If the user submits an empty line:</p>
+ * <ul>
+ * <li>If {@code defaultValue != null} – return that value.</li>
+ * <li>Else if {@code allowEmpty == true} – return {@code null}.</li>
+ * <li>Otherwise the prompt is displayed again.</li>
+ * </ul>
+ *
+ * @param prompt message shown to the user (without trailing colon)
+ * @param defaultValue value returned when the user simply hits {@code Enter}; may be {@code null}
+ * @param allowEmpty whether an empty line without a default should yield {@code null}
+ * @return {@code Boolean.TRUE}, {@code Boolean.FALSE}, the supplied {@code defaultValue}, or {@code null}
+ */
+ public static Boolean askBoolean(final String prompt, final Boolean defaultValue, final boolean allowEmpty) {
+ while (true) {
+ String displayPrompt = prompt + (defaultValue != null ? " [" + defaultValue + "]" : "") + ": ";
+ System.out.print(displayPrompt);
+
+ String line = SCANNER.nextLine().trim();
+
+ if (line.isEmpty()) {
+ if (defaultValue != null) {
+ return defaultValue;
+ }
+ if (allowEmpty) {
+ return null;
+ }
+ System.out.println("Input cannot be empty. Please enter y/yes/true or n/no/false.");
+ continue;
+ }
+
+ switch (line.toLowerCase()) {
+ case "y":
+ case "yes":
+ case "true":
+ return Boolean.TRUE;
+ case "n":
+ case "no":
+ case "false":
+ return Boolean.FALSE;
+ default:
+ System.out.println("Invalid input. Please enter y/yes/true or n/no/false.");
+ }
+ }
+ }
+
+ /**
+ * Convenience overload – assumes {@code allowEmpty == false}.
+ *
+ * @see #askBoolean(String, Boolean, boolean)
+ */
+ public static Boolean askBoolean(final String prompt, final Boolean defaultValue) {
+ return askBoolean(prompt, defaultValue, false);
+ }
+
+ /**
+ * Convenience overload – no default value, {@code allowEmpty == false}.
+ *
+ * @throws IllegalStateException if the user never provides a valid token
+ * @see #askBoolean(String, Boolean, boolean)
+ */
+ public static Boolean askBoolean(final String prompt) {
+ return askBoolean(prompt, null, false);
+ }
+
+ /*‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑ Float ‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑*/
+
+ /**
+ * Prompts the user for a {@code float} value that satisfies optional
+ * minimum/maximum bounds.
+ *
+ * @param prompt message shown to the user
+ * @param defaultValue value returned on empty input; may be {@code null}
+ * @param min inclusive lower bound, or {@code null} for no check
+ * @param max inclusive upper bound, or {@code null} for no check
+ * @param allowEmpty whether an empty line without a default should yield {@code null}
+ * @return the parsed {@link Float}, {@code defaultValue}, or {@code null}
+ */
+ public static Float askFloat(final String prompt, final Float defaultValue,
+ final Float min, final Float max, final boolean allowEmpty) {
+ while (true) {
+ String displayPrompt = prompt + (defaultValue != null ? " [" + defaultValue + "]" : "") + ": ";
+ System.out.print(displayPrompt);
+ String input = SCANNER.nextLine().trim();
+
+ if (input.isEmpty()) {
+ if (defaultValue != null) {
+ return defaultValue;
+ }
+ if (allowEmpty) {
+ return null;
+ }
+ System.out.println("Input cannot be empty. Please enter a valid float.");
+ continue;
+ }
+
+ try {
+ float parsed = Float.parseFloat(input);
+ if (min != null && parsed < min) {
+ System.out.println("Value must be at least " + min + ".");
+ continue;
+ }
+ if (max != null && parsed > max) {
+ System.out.println("Value must be at most " + max + ".");
+ continue;
+ }
+ return parsed;
+ } catch (NumberFormatException ex) {
+ System.out.println("Invalid number format. Try again.");
+ }
+ }
+ }
+
+ /**
+ * Overload without bounds; {@code allowEmpty == false}.
+ *
+ * @see #askFloat(String, Float, Float, Float, boolean)
+ */
+ public static Float askFloat(final String prompt, final Float defaultValue) {
+ return askFloat(prompt, defaultValue, null, null, false);
+ }
+
+ /**
+ * Overload without default or bounds; {@code allowEmpty == false}.
+ *
+ * @see #askFloat(String, Float, Float, Float, boolean)
+ */
+ public static Float askFloat(final String prompt) {
+ return askFloat(prompt, null, null, null, false);
+ }
+
+ /*‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑ Long ‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑*/
+
+ /**
+ * Prompts the user for a {@code long} value that satisfies optional
+ * minimum/maximum bounds.
+ *
+ * @param prompt message shown to the user
+ * @param defaultValue value returned on empty input; may be {@code null}
+ * @param min inclusive lower bound, or {@code null}
+ * @param max inclusive upper bound, or {@code null}
+ * @param allowEmpty whether an empty line without a default should yield {@code null}
+ * @return the parsed {@link Long}, {@code defaultValue}, or {@code null}
+ */
+ public static Long askLong(final String prompt, final Long defaultValue,
+ final Long min, final Long max, final boolean allowEmpty) {
+ while (true) {
+ String displayPrompt = prompt + (defaultValue != null ? " [" + defaultValue + "]" : "") + ": ";
+ System.out.print(displayPrompt);
+ String input = SCANNER.nextLine().trim();
+
+ if (input.isEmpty()) {
+ if (defaultValue != null) {
+ return defaultValue;
+ }
+ if (allowEmpty) {
+ return null;
+ }
+ System.out.println("Input cannot be empty. Please enter a valid long.");
+ continue;
+ }
+
+ try {
+ long parsed = Long.parseLong(input);
+ if (min != null && parsed < min) {
+ System.out.println("Value must be at least " + min + ".");
+ continue;
+ }
+ if (max != null && parsed > max) {
+ System.out.println("Value must be at most " + max + ".");
+ continue;
+ }
+ return parsed;
+ } catch (NumberFormatException ex) {
+ System.out.println("Invalid number format. Try again.");
+ }
+ }
+ }
+
+ /**
+ * Overload without bounds; {@code allowEmpty == false}.
+ */
+ public static Long askLong(final String prompt, final Long defaultValue) {
+ return askLong(prompt, defaultValue, null, null, false);
+ }
+
+ /**
+ * Overload without default or bounds; {@code allowEmpty == false}.
+ */
+ public static Long askLong(final String prompt) {
+ return askLong(prompt, null, null, null, false);
+ }
+
+ /*‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑ Integer ‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑*/
+
+ /**
+ * Prompts the user for an {@code int} value within optional bounds.
+ *
+ * @param prompt message shown to the user
+ * @param defaultValue value returned on empty input; may be {@code null}
+ * @param min inclusive lower bound, or {@code null}
+ * @param max inclusive upper bound, or {@code null}
+ * @param allowEmpty whether an empty line without a default should yield {@code null}
+ * @return the parsed {@link Integer}, {@code defaultValue}, or {@code null}
+ */
+ public static Integer askInteger(final String prompt, final Integer defaultValue,
+ final Integer min, final Integer max, final boolean allowEmpty) {
+ while (true) {
+ String displayPrompt = prompt + (defaultValue != null ? " [" + defaultValue + "]" : "") + ": ";
+ System.out.print(displayPrompt);
+ String input = SCANNER.nextLine().trim();
+
+ if (input.isEmpty()) {
+ if (defaultValue != null) {
+ return defaultValue;
+ }
+ if (allowEmpty) {
+ return null;
+ }
+ System.out.println("Input cannot be empty. Please enter a valid integer.");
+ continue;
+ }
+
+ try {
+ int parsed = Integer.parseInt(input);
+ if (min != null && parsed < min) {
+ System.out.println("Value must be at least " + min + ".");
+ continue;
+ }
+ if (max != null && parsed > max) {
+ System.out.println("Value must be at most " + max + ".");
+ continue;
+ }
+ return parsed;
+ } catch (NumberFormatException ex) {
+ System.out.println("Invalid number format. Try again.");
+ }
+ }
+ }
+
+ /**
+ * Overload without bounds; {@code allowEmpty == false}.
+ */
+ public static Integer askInteger(final String prompt, final Integer defaultValue) {
+ return askInteger(prompt, defaultValue, null, null, false);
+ }
+
+ /**
+ * Overload without default or bounds; {@code allowEmpty == false}.
+ */
+ public static Integer askInteger(final String prompt) {
+ return askInteger(prompt, null, null, null, false);
+ }
+
+ /*‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑ String ‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑*/
+
+ /**
+ * Prompts the user for a non‑empty {@link String} and validates its length.
+ *
+ * @param prompt message shown to the user
+ * @param defaultValue value returned on empty input; may be {@code null}
+ * @param minLength inclusive lower bound; {@code null} means no check
+ * @param maxLength inclusive upper bound; {@code null} means no check
+ * @param allowEmpty whether an empty line without a default should yield {@code null}
+ * @return the typed {@link String}, {@code defaultValue}, or {@code null}
+ */
+ public static String askString(final String prompt, final String defaultValue,
+ final Integer minLength, final Integer maxLength, final boolean allowEmpty) {
+ while (true) {
+ String displayPrompt = prompt + (defaultValue != null ? " [" + defaultValue + "]" : "") + ": ";
+ System.out.print(displayPrompt);
+ String input = SCANNER.nextLine().trim();
+
+ if (input.isEmpty()) {
+ if (defaultValue != null) {
+ return defaultValue;
+ }
+ if (allowEmpty) {
+ return null;
+ }
+ System.out.println("Input cannot be empty. Please enter a valid string.");
+ continue;
+ }
+
+ if (minLength != null && input.length() < minLength) {
+ System.out.println("Input must be at least " + minLength + " characters long.");
+ continue;
+ }
+ if (maxLength != null && input.length() > maxLength) {
+ System.out.println("Input must be at most " + maxLength + " characters long.");
+ continue;
+ }
+
+ return input;
+ }
+ }
+
+ /**
+ * Overload without length bounds; {@code allowEmpty == false}.
+ */
+ public static String askString(final String prompt, final String defaultValue) {
+ return askString(prompt, defaultValue, null, null, false);
+ }
+
+ /**
+ * Overload without default or bounds; {@code allowEmpty == false}.
+ */
+ public static String askString(final String prompt) {
+ return askString(prompt, null, null, null, false);
+ }
+
+ /*‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑ File / Directory ‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑*/
+
+ /**
+ * Prompts the user for a <strong>file path</strong> and validates various
+ * attributes (existence, readability, writability, executability).
+ * <p>
+ * A {@link File} object is <em>returned as‑is</em>; no attempt is made to
+ * canonicalise or resolve symlinks.
+ * </p>
+ *
+ * @param prompt message shown to the user
+ * @param defaultValue value returned on empty input; may be {@code null}
+ * @param mustExist if non‑{@code null}: {@code true} ⇒ file must exist, {@code false} ⇒ file must <em>not</em> exist
+ * @param mustReadable if non‑{@code null}: {@code true} ⇒ {@link File#canRead()} must be {@code true}
+ * @param mustWritable if non‑{@code null}: {@code true} ⇒ {@link File#canWrite()} must be {@code true}
+ * @param mustExecutable if non‑{@code null}: {@code true} ⇒ {@link File#canExecute()} must be {@code true}
+ * @param allowEmpty whether an empty line without a default should yield {@code null}
+ * @return a {@link File} satisfying all constraints, {@code defaultValue}, or {@code null}
+ */
+ public static File askFile(final String prompt,
+ final File defaultValue,
+ final Boolean mustExist,
+ final Boolean mustReadable,
+ final Boolean mustWritable,
+ final Boolean mustExecutable,
+ final boolean allowEmpty) {
+
+ while (true) {
+ String displayPrompt = prompt + (defaultValue != null ? " [" + defaultValue.getPath() + "]" : "") + ": ";
+ System.out.print(displayPrompt);
+ String input = SCANNER.nextLine().trim();
+
+ if (input.isEmpty()) {
+ if (defaultValue != null) {
+ return defaultValue;
+ }
+ if (allowEmpty) {
+ return null;
+ }
+ System.out.println("Input cannot be empty. Please enter a file path.");
+ continue;
+ }
+
+ File file = new File(input);
+
+ /* Validation chain */
+ if (file.exists() && !file.isFile()) {
+ System.out.println("Path is not a file. Please specify a file.");
+ continue;
+ }
+
+ if (mustExist != null) {
+ if (mustExist && !file.exists()) {
+ System.out.println("File does not exist. You must specify an existing file.");
+ continue;
+ } else if (!mustExist && file.exists()) {
+ System.out.println("File already exists. You must specify a non‑existing file.");
+ continue;
+ }
+ }
+
+ if (mustReadable != null) {
+ if (mustReadable && file.exists() && !file.canRead()) {
+ System.out.println("File is not readable. You must specify a readable file.");
+ continue;
+ } else if (!mustReadable && file.canRead()) {
+ System.out.println("File is readable. You must specify a non‑readable file.");
+ continue;
+ }
+ }
+
+ if (mustWritable != null) {
+ if (mustWritable && file.exists() && !file.canWrite()) {
+ System.out.println("File is not writable. You must specify a writable file.");
+ continue;
+ } else if (!mustWritable && file.canWrite()) {
+ System.out.println("File is writable. You must specify a non‑writable file.");
+ continue;
+ }
+ }
+
+ if (mustExecutable != null) {
+ if (mustExecutable && file.exists() && !file.canExecute()) {
+ System.out.println("File is not executable. You must specify an executable file.");
+ continue;
+ } else if (!mustExecutable && file.canExecute()) {
+ System.out.println("File is executable. You must specify a non‑executable file.");
+ continue;
+ }
+ }
+
+ return file;
+ }
+ }
+
+ /**
+ * Overload with no attribute constraints; {@code allowEmpty == false}.
+ */
+ public static File askFile(final String prompt, final File defaultValue) {
+ return askFile(prompt, defaultValue, null, null, null, null, false);
+ }
+
+ /**
+ * Overload with no default or attribute constraints; {@code allowEmpty == false}.
+ */
+ public static File askFile(final String prompt) {
+ return askFile(prompt, null, null, null, null, null, false);
+ }
+
+ /**
+ * Prompts the user for a <strong>directory path</strong> and validates various
+ * attributes (existence, readability, writability, executability).
+ *
+ * @param prompt message shown to the user
+ * @param defaultValue value returned on empty input; may be {@code null}
+ * @param mustExist if non‑{@code null}: {@code true} ⇒ directory must exist, {@code false} ⇒ directory must <em>not</em> exist
+ * @param mustReadable if non‑{@code null}: {@code true} ⇒ directory must be readable
+ * @param mustWritable if non‑{@code null}: {@code true} ⇒ directory must be writable
+ * @param mustExecutable if non‑{@code null}: {@code true} ⇒ directory must be executable
+ * @param allowEmpty whether an empty line without a default should yield {@code null}
+ * @return a directory {@link File} satisfying all constraints, {@code defaultValue}, or {@code null}
+ */
+ public static File askDirectory(final String prompt,
+ final File defaultValue,
+ final Boolean mustExist,
+ final Boolean mustReadable,
+ final Boolean mustWritable,
+ final Boolean mustExecutable,
+ final boolean allowEmpty) {
+
+ while (true) {
+ String displayPrompt = prompt + (defaultValue != null ? " [" + defaultValue.getPath() + "]" : "") + ": ";
+ System.out.print(displayPrompt);
+ String input = SCANNER.nextLine().trim();
+
+ if (input.isEmpty()) {
+ if (defaultValue != null) {
+ return defaultValue;
+ }
+ if (allowEmpty) {
+ return null;
+ }
+ System.out.println("Input cannot be empty. Please enter a directory path.");
+ continue;
+ }
+
+ File dir = new File(input);
+
+ if (dir.exists() && !dir.isDirectory()) {
+ System.out.println("Path is not a directory. Please specify a directory.");
+ continue;
+ }
+
+ if (mustExecutable != null) {
+ if (mustExecutable && !dir.canExecute()) {
+ System.out.println("Directory is not executable. You must specify an executable directory.");
+ continue;
+ } else if (!mustExecutable && dir.canExecute()) {
+ System.out.println("Directory is executable. You must specify a non‑executable directory.");
+ continue;
+ }
+ }
+
+ if (mustExist != null) {
+ if (mustExist && !dir.exists()) {
+ System.out.println("Directory does not exist. You must specify an existing directory.");
+ continue;
+ } else if (!mustExist && dir.exists()) {
+ System.out.println("Directory already exists. You must specify a non‑existing directory.");
+ continue;
+ }
+ }
+
+ if (mustReadable != null) {
+ if (mustReadable && dir.exists() && !dir.canRead()) {
+ System.out.println("Directory is not readable. You must specify a readable directory.");
+ continue;
+ } else if (!mustReadable && dir.canRead()) {
+ System.out.println("Directory is readable. You must specify a non‑readable directory.");
+ continue;
+ }
+ }
+
+ if (mustWritable != null) {
+ if (mustWritable && dir.exists() && !dir.canWrite()) {
+ System.out.println("Directory is not writable. You must specify a writable directory.");
+ continue;
+ } else if (!mustWritable && dir.canWrite()) {
+ System.out.println("Directory is writable. You must specify a non‑writable directory.");
+ continue;
+ }
+ }
+
+ return dir;
+ }
+ }
+
+ /**
+ * Overload with no attribute constraints; {@code allowEmpty == false}.
+ */
+ public static File askDirectory(final String prompt, final File defaultValue) {
+ return askDirectory(prompt, defaultValue, null, null, null, null, false);
+ }
+
+ /**
+ * Overload with no default or attribute constraints; {@code allowEmpty == false}.
+ */
+ public static File askDirectory(final String prompt) {
+ return askDirectory(prompt, null, null, null, null, null, false);
+ }
+
+}
--- /dev/null
+/*
+ * Svjatoslav Commons - shared library of common functionality. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.commons.cli_helper.parameter_parser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static eu.svjatoslav.commons.cli_helper.parameter_parser.ParameterCount.NONE;
+import static java.lang.String.join;
+import static java.util.Collections.addAll;
+
+/**
+ * Represents a command-line option (a flag or switch), which can be configured with:
+ * <ul>
+ * <li>whether it's mandatory</li>
+ * <li>the number of parameters accepted</li>
+ * <li>a textual description</li>
+ * <li>aliases (the actual CLI flags, e.g., "--help" or "-h")</li>
+ * </ul>
+ *
+ * @param <T> the type of object returned by {@link #getValue()}
+ * @param <I> the actual subclass type, used to enable fluent method chaining
+ */
+public abstract class Option<T, I extends Option<?, I>> {
+
+ /**
+ * Human-readable purpose of this option (e.g., "Input image path").
+ * For a description of the parameter format (e.g., "file", "integer"), see {@link #describeFormat()}.
+ */
+ public final String description;
+
+ /** The list of parameters provided on the command line for this option (if any). */
+ public final List<String> parameters = new ArrayList<>();
+
+ /** How many parameters this option supports: NONE, ONE, or ONE_OR_MORE. */
+ final ParameterCount parameterCount;
+
+ /** The aliases by which this option can be invoked (e.g. "-f", "--file"). */
+ private final List<String> aliases = new ArrayList<>();
+
+ /** Whether this option is mandatory (i.e., must appear at least once on the CLI). */
+ protected boolean mandatory = false;
+
+ /** Whether this option was actually present on the CLI. */
+ private boolean isPresent = false;
+
+ /**
+ * Fully-custom constructor for an option.
+ *
+ * @param mandatory {@code true} if the option is required, {@code false} otherwise
+ * @param parameterCount the number of parameters required: NONE, ONE, or ONE_OR_MORE
+ * @param description a textual description of the option
+ * @param aliases2 zero or more aliases (e.g. "-f", "--file")
+ */
+ public Option(final boolean mandatory,
+ final ParameterCount parameterCount,
+ final String description,
+ final String... aliases2) {
+ this.mandatory = mandatory;
+ this.description = description;
+ this.parameterCount = parameterCount;
+ addAll(aliases, aliases2);
+ }
+
+ /**
+ * Simpler constructor that defaults to a non-mandatory option.
+ * You can make it mandatory later by calling {@link #setMandatory()}.
+ *
+ * @param description a textual description of the option
+ * @param parameterCount the number of parameters required: NONE, ONE, or ONE_OR_MORE
+ * @param aliases2 zero or more aliases
+ */
+ public Option(final String description,
+ final ParameterCount parameterCount,
+ final String... aliases2) {
+ this(false, parameterCount, description, aliases2);
+ }
+
+ /**
+ * Adds additional aliases to this option for user convenience.
+ *
+ * @param aliasArray additional aliases (e.g., "-f", "--file")
+ * @return this option (for method chaining)
+ */
+ @SuppressWarnings("unchecked")
+ public I addAliases(final String... aliasArray) {
+ addAll(aliases, aliasArray);
+ return (I) this;
+ }
+
+ /**
+ * Adds a parameter to this option, validating it against {@link #isValid(String)}.
+ *
+ * @param parameterString the parameter string to add
+ * @return {@code true} if the parameter was valid and added, otherwise {@code false}
+ */
+ public boolean addParameter(final String parameterString) {
+ // Check if this option is supposed to have parameters at all
+ if (parameterCount.equals(NONE)) {
+ System.out.println("Error! No parameters are allowed for option(s): "
+ + getAliasesAsString());
+ return false;
+ }
+
+ // If only ONE parameter is allowed, but we already have one
+ if ((!parameters.isEmpty()) && parameterCount.equals(ParameterCount.ONE)) {
+ System.out.println("Error! Only one parameter is allowed for argument(s): "
+ + getAliasesAsString());
+ return false;
+ }
+
+ // Validate the parameter itself
+ if (isValid(parameterString)) {
+ parameters.add(parameterString);
+ return true;
+ } else {
+ System.out.println("Error! Invalid parameter \"" + parameterString
+ + "\". It should be " + describeFormat() + ".");
+ return false;
+ }
+ }
+
+ /**
+ * Describes the kind or format of the expected parameter(s), e.g. "File", "Integer", etc.
+ *
+ * @return a short string describing the parameter format
+ */
+ public abstract String describeFormat();
+
+ /**
+ * Returns this option's aliases as a comma-separated string (e.g. "-f, --file").
+ */
+ public String getAliasesAsString() {
+ return aliases.isEmpty() ? "<no aliases>" : join(", ", aliases);
+ }
+
+ /**
+ * Returns a help message that includes the aliases, whether the option is mandatory,
+ * the format, and a brief description.
+ *
+ * @return a descriptive help string
+ */
+ public String getHelp() {
+ StringBuilder result = new StringBuilder();
+ // First line: aliases plus (mandatory?, format)
+ result.append(getAliasesAsString());
+ if (!NONE.equals(parameterCount)) {
+ result.append(" (")
+ .append(isMandatory() ? "mandatory, " : "")
+ .append(describeFormat())
+ .append(")");
+ if (ParameterCount.ONE_OR_MORE.equals(parameterCount)) {
+ result.append("...");
+ }
+ }
+ result.append("\n");
+ // Second line: indentation plus description
+ result.append(" ").append(description).append("\n");
+ return result.toString();
+ }
+
+ /**
+ * Returns the parsed value (usually constructed from {@link #parameters}).
+ *
+ * @return the parsed value as the generic type T.
+ * @throws RuntimeException if the parameters are insufficient or invalid in a subclass.
+ */
+ public abstract T getValue();
+
+ /**
+ * @return {@code true} if this option is mandatory.
+ */
+ public boolean isMandatory() {
+ return mandatory;
+ }
+
+ /**
+ * @return {@code true} if this option was present on the command line.
+ */
+ public boolean isPresent() {
+ return isPresent;
+ }
+
+ /**
+ * Marks this option as present or not present.
+ *
+ * @param present {@code true} if it was found in the CLI arguments, else {@code false}.
+ */
+ protected void setPresent(final boolean present) {
+ this.isPresent = present;
+ }
+
+ /**
+ * Checks if a given alias matches this option.
+ *
+ * @param alias the alias to check
+ * @return {@code true} if the alias is registered for this option
+ */
+ public boolean matchesAlias(final String alias) {
+ return aliases.contains(alias);
+ }
+
+ /**
+ * Called when no more arguments can be added to this option, giving it a chance
+ * to verify correct usage (e.g., check if required parameters are missing).
+ *
+ * @return {@code true} if the usage so far is valid; otherwise {@code false}.
+ */
+ public boolean noMoreArguments() {
+ // If we expect at least one parameter but none were provided
+ if (!parameterCount.equals(NONE) && parameters.isEmpty()) {
+ System.out.println("Error! " + getAliasesAsString()
+ + " requires at least one parameter.");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Sets this option as mandatory.
+ *
+ * @return this option, for method chaining
+ */
+ @SuppressWarnings("unchecked")
+ public I setMandatory() {
+ this.mandatory = true;
+ return (I) this;
+ }
+
+ /**
+ * Checks if a single parameter string is valid for this option.
+ *
+ * @param value the parameter string to test
+ * @return {@code true} if valid; {@code false} otherwise
+ */
+ public abstract boolean isValid(String value);
+}
--- /dev/null
+/*
+ * Svjatoslav Commons - shared library of common functionality. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.commons.cli_helper.parameter_parser;
+
+/**
+ * Defines how many parameters a command-line option can accept.
+ */
+public enum ParameterCount {
+ /** Option has no parameters. */
+ NONE,
+
+ /** Option has exactly one parameter. */
+ ONE,
+
+ /** Option can have one or more parameters. */
+ ONE_OR_MORE
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Svjatoslav Commons - shared library of common functionality. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.commons.cli_helper.parameter_parser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class parses command-line arguments against a set of registered {@link Option} objects.
+ * <p>
+ * Usage:
+ * <pre>{@code
+ * Parser parser = new Parser();
+ * FileOption fileOpt = parser.add(new FileOption("Input file").mustExist())
+ * .addAliases("-f", "--file")
+ * .setMandatory();
+ * parser.parse(args);
+ * if (!parser.checkSuccess()) {
+ * parser.showHelp();
+ * System.exit(1);
+ * }
+ * File inputFile = fileOpt.getValue();
+ * }</pre>
+ */
+public class Parser {
+
+ private final List<Option<?, ? extends Option<?, ?>>> options = new ArrayList<>();
+
+ /**
+ * Registers an option with this parser.
+ *
+ * @param option the option to be added
+ * @param <E> the concrete type of the option
+ * @return the same option object, for chaining
+ */
+ public <E extends Option<?, E>> E add(final E option) {
+ options.add(option);
+ return option;
+ }
+
+ /**
+ * Looks up an option by alias.
+ *
+ * @param alias the alias to search for (e.g., "-f" or "--file")
+ * @return the matching option, or {@code null} if not found
+ */
+ public Option<?, ?> findParameterByAlias(final String alias) {
+ for (final Option<?, ?> option : options) {
+ if (option.matchesAlias(alias)) {
+ return option;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Parses the provided command-line arguments, matching them to registered options and their parameters.
+ *
+ * @param args command-line arguments (usually from main(String[]))
+ * @return {@code true} if parsing succeeded without errors; otherwise {@code false}.
+ */
+ public boolean parse(final String[] args) {
+ Option<?, ?> currentOption = null;
+
+ for (final String argument : args) {
+ Option<?, ?> optionForAlias = findParameterByAlias(argument);
+
+ if (optionForAlias == null) {
+ // If this argument is not a recognized option alias, treat it as a parameter to the current option
+ if (currentOption == null) {
+ System.out.println("Unknown command-line parameter: " + argument);
+ return false;
+ }
+ // Attempt to add this argument as a parameter to the current option
+ if (!currentOption.addParameter(argument)) {
+ return false;
+ }
+ } else {
+ // We found a recognized option
+ // First, let the previous option finalize (check if it has all needed params, etc.)
+ if (currentOption != null) {
+ if (!currentOption.noMoreArguments()) {
+ return false;
+ }
+ }
+ // Switch to the newly recognized option
+ optionForAlias.setPresent(true);
+ currentOption = optionForAlias;
+ }
+ }
+
+ // Finalize the last option (if any)
+ if (currentOption != null) {
+ if (!currentOption.noMoreArguments()) {
+ return false;
+ }
+ }
+
+ // Finally, check mandatory options
+ return checkMandatoryArgumentsPresent();
+ }
+
+ /**
+ * Checks whether all mandatory options were present.
+ *
+ * @return {@code true} if all mandatory options are present, otherwise {@code false}.
+ */
+ private boolean checkMandatoryArgumentsPresent() {
+ for (final Option<?, ?> option : options) {
+ if (option.isMandatory() && !option.isPresent()) {
+ System.out.println("Error! Mandatory parameter ("
+ + option.getAliasesAsString() + ") is not specified.");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * A convenience method to see if the last parse succeeded. If you stored the result of {@link #parse(String[])} in a
+ * boolean, you can also just check that.
+ *
+ * @return {@code true} if all mandatory options are present (and no parse errors occurred).
+ */
+ public boolean checkSuccess() {
+ return checkMandatoryArgumentsPresent();
+ }
+
+ /**
+ * Prints all available options, their formats, and their descriptions. Usually called upon parse failure.
+ */
+ public void showHelp() {
+ System.out.println("Available command-line arguments:");
+ for (final Option<?, ?> option : options) {
+ System.out.print(option.getHelp());
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Svjatoslav Commons - shared library of common functionality. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.commons.cli_helper.parameter_parser.parameter;
+
+import eu.svjatoslav.commons.cli_helper.parameter_parser.ParameterCount;
+import eu.svjatoslav.commons.cli_helper.parameter_parser.Option;
+
+import java.io.File;
+
+/**
+ * Represents a command-line option that accepts exactly one parameter interpreted as a directory path.
+ */
+public class DirectoryOption extends Option<File, DirectoryOption> {
+
+ /** Defines whether the directory must exist, must not exist, or if it does not matter. */
+ private ExistenceType existenceType = ExistenceType.DOES_NOT_MATTER;
+
+ /**
+ * Creates a DirectoryOption object with a specified description.
+ *
+ * @param description a brief description of what this directory option is for.
+ */
+ public DirectoryOption(final String description) {
+ super(description, ParameterCount.ONE);
+ }
+
+ @Override
+ public String describeFormat() {
+ switch (existenceType) {
+ case MUST_EXIST: return "Existing directory.";
+ case MUST_NOT_EXIST: return "Non-existing directory.";
+ default: return "Directory.";
+ }
+ }
+
+ /**
+ * Returns the directory as a {@link File} object.
+ *
+ * @throws RuntimeException if the user did not provide exactly one parameter.
+ */
+ @Override
+ public File getValue() {
+ if (parameters.size() != 1) {
+ throw new RuntimeException("Parameter '" + description
+ + "' must have exactly 1 argument.");
+ }
+ return new File(parameters.get(0));
+ }
+
+ /**
+ * Requires that the directory must already exist.
+ *
+ * @return this DirectoryOption instance for chaining
+ */
+ public DirectoryOption mustExist() {
+ existenceType = ExistenceType.MUST_EXIST;
+ return this;
+ }
+
+ /**
+ * Requires that the directory must not already exist.
+ *
+ * @return this DirectoryOption instance for chaining
+ */
+ public DirectoryOption mustNotExist() {
+ existenceType = ExistenceType.MUST_NOT_EXIST;
+ return this;
+ }
+
+ /**
+ * Checks whether the provided path is valid based on the {@link ExistenceType}.
+ *
+ * @param value the directory path to validate
+ * @return {@code true} if valid, otherwise {@code false}
+ */
+ @Override
+ public boolean isValid(final String value) {
+ final File file = new File(value);
+
+ switch (existenceType) {
+ case MUST_EXIST:
+ return file.exists() && file.isDirectory();
+
+ case MUST_NOT_EXIST:
+ return !file.exists();
+
+ case DOES_NOT_MATTER:
+ // If it exists and is a file, it's invalid
+ if (file.exists() && file.isFile()) {
+ return false;
+ }
+ return true;
+
+ default:
+ return false;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Svjatoslav Commons - shared library of common functionality. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.commons.cli_helper.parameter_parser.parameter;
+
+import eu.svjatoslav.commons.cli_helper.parameter_parser.ParameterCount;
+import eu.svjatoslav.commons.cli_helper.parameter_parser.Option;
+
+import java.io.File;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Represents a command-line option that accepts one or more directory paths.
+ */
+public class DirectoryOptions extends Option<List<File>, DirectoryOptions> {
+
+ /** Defines whether each directory must exist, must not exist, or if it does not matter. */
+ private ExistenceType existenceType = ExistenceType.DOES_NOT_MATTER;
+
+ /**
+ * Creates a DirectoryOptions object with a specified description, requiring one or more directories.
+ *
+ * @param description a brief description of what these directories represent.
+ */
+ public DirectoryOptions(final String description) {
+ super(description, ParameterCount.ONE_OR_MORE);
+ }
+
+ @Override
+ public String describeFormat() {
+ switch (existenceType) {
+ case MUST_EXIST: return "One to many existing directories.";
+ case MUST_NOT_EXIST: return "One to many non-existing directories.";
+ default: return "One to many directories.";
+ }
+ }
+
+ /**
+ * Returns the directories as a list of {@link File} objects.
+ */
+ @Override
+ public List<File> getValue() {
+ return parameters.stream().map(File::new).collect(Collectors.toList());
+ }
+
+ /**
+ * Requires that each directory must exist.
+ *
+ * @return this DirectoryOptions instance
+ */
+ public DirectoryOptions mustExist() {
+ existenceType = ExistenceType.MUST_EXIST;
+ return this;
+ }
+
+ /**
+ * Requires that each directory must not exist.
+ *
+ * @return this DirectoryOptions instance
+ */
+ public DirectoryOptions mustNotExist() {
+ existenceType = ExistenceType.MUST_NOT_EXIST;
+ return this;
+ }
+
+ /**
+ * Validates each directory path against the specified {@link ExistenceType}.
+ */
+ @Override
+ public boolean isValid(final String value) {
+ final File file = new File(value);
+
+ switch (existenceType) {
+ case MUST_EXIST:
+ return file.exists() && file.isDirectory();
+
+ case MUST_NOT_EXIST:
+ return !file.exists();
+
+ case DOES_NOT_MATTER:
+ if (file.exists() && file.isFile()) {
+ return false;
+ }
+ return true;
+
+ default:
+ return false;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Svjatoslav Commons - shared library of common functionality. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.commons.cli_helper.parameter_parser.parameter;
+
+/**
+ * Defines whether a file/directory resource must exist, must not exist, or if it does not matter.
+ */
+public enum ExistenceType {
+ /** Resource shall exist. */
+ MUST_EXIST,
+
+ /** Resource shall not exist. */
+ MUST_NOT_EXIST,
+
+ /** Resource existence does not matter. */
+ DOES_NOT_MATTER
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Svjatoslav Commons - shared library of common functionality. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.commons.cli_helper.parameter_parser.parameter;
+
+import eu.svjatoslav.commons.cli_helper.parameter_parser.ParameterCount;
+import eu.svjatoslav.commons.cli_helper.parameter_parser.Option;
+
+import java.io.File;
+
+/**
+ * Represents a command-line option that accepts exactly one parameter which is interpreted as a file path.
+ * By default, {@link ExistenceType#DOES_NOT_MATTER} is used (i.e., the file may or may not exist).
+ */
+public class FileOption extends Option<File, FileOption> {
+
+ /** Specifies whether the file must exist, must not exist, or does not matter. */
+ private ExistenceType existenceType = ExistenceType.DOES_NOT_MATTER;
+
+ /**
+ * Creates a FileOption requiring exactly one parameter that represents a file.
+ *
+ * @param description a brief description of what this option is for (e.g., "Path to input file").
+ */
+ public FileOption(final String description) {
+ super(description, ParameterCount.ONE);
+ }
+
+ /**
+ * Checks whether the given file path is valid according to the specified {@link ExistenceType}.
+ *
+ * @param existenceType the required existence status for the file
+ * @param path the file path to validate
+ * @return {@code true} if the path meets the existence requirement and is not a directory when it must be a file.
+ */
+ protected static boolean isFileValid(ExistenceType existenceType, String path) {
+ final File file = new File(path);
+
+ switch (existenceType) {
+ case MUST_EXIST:
+ return file.exists() && file.isFile();
+
+ case MUST_NOT_EXIST:
+ return !file.exists();
+
+ case DOES_NOT_MATTER:
+ // If the file exists, ensure it's not a directory
+ if (file.exists() && file.isDirectory()) {
+ return false;
+ }
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Provides a short description of the expected format (e.g., "Existing file", "Non-existing file", or "File").
+ *
+ * @return a string describing the format of this file parameter.
+ */
+ @Override
+ public String describeFormat() {
+ switch (existenceType) {
+ case MUST_EXIST: return "Existing file.";
+ case MUST_NOT_EXIST: return "Non-existing file.";
+ default: return "File.";
+ }
+ }
+
+ /**
+ * Returns the file chosen by the user (as a {@link File} object).
+ *
+ * @return a {@link File} object constructed from the single parameter.
+ * @throws RuntimeException if the option does not have exactly 1 parameter.
+ */
+ @Override
+ public File getValue() {
+ if (parameters.size() != 1) {
+ throw new RuntimeException("Parameter '" + description
+ + "' must have exactly 1 argument.");
+ }
+ return new File(parameters.get(0));
+ }
+
+ /**
+ * Enforces that the file path must point to an existing file.
+ *
+ * @return this FileOption instance, for method chaining
+ */
+ public FileOption mustExist() {
+ existenceType = ExistenceType.MUST_EXIST;
+ return this;
+ }
+
+ /**
+ * Enforces that the file path must not exist.
+ *
+ * @return this FileOption instance, for method chaining
+ */
+ public FileOption mustNotExist() {
+ existenceType = ExistenceType.MUST_NOT_EXIST;
+ return this;
+ }
+
+ /**
+ * Validates the given parameter against the {@link ExistenceType} rule.
+ *
+ * @param value the file path to validate
+ * @return {@code true} if the file path meets the existence requirement
+ */
+ @Override
+ public boolean isValid(final String value) {
+ return isFileValid(existenceType, value);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Svjatoslav Commons - shared library of common functionality. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.commons.cli_helper.parameter_parser.parameter;
+
+import eu.svjatoslav.commons.cli_helper.parameter_parser.Option;
+import eu.svjatoslav.commons.cli_helper.parameter_parser.ParameterCount;
+
+import java.io.File;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Represents a command-line option that accepts one or more file paths.
+ */
+public class FileOptions extends Option<List<File>, FileOptions> {
+
+ /** Specifies whether each file must exist, must not exist, or does not matter. */
+ private ExistenceType existenceType = ExistenceType.DOES_NOT_MATTER;
+
+ /**
+ * Constructs a FileOptions object.
+ *
+ * @param description a brief description of the option's purpose.
+ */
+ public FileOptions(final String description) {
+ super(description, ParameterCount.ONE_OR_MORE);
+ }
+
+ /**
+ * Provides a short description of the expected file format(s).
+ *
+ * @return either "One to many existing files.", "One to many non-existing files.", or "One to many files."
+ */
+ @Override
+ public String describeFormat() {
+ switch (existenceType) {
+ case MUST_EXIST: return "One to many existing files.";
+ case MUST_NOT_EXIST: return "One to many non-existing files.";
+ default: return "One to many files.";
+ }
+ }
+
+ /**
+ * Returns the list of file paths as {@link File} objects.
+ *
+ * @return a list of {@link File} objects corresponding to user input.
+ */
+ @Override
+ public List<File> getValue() {
+ return parameters.stream().map(File::new).collect(Collectors.toList());
+ }
+
+ /**
+ * Requires that each file must already exist.
+ *
+ * @return this FileOptions instance for chaining
+ */
+ public FileOptions mustExist() {
+ existenceType = ExistenceType.MUST_EXIST;
+ return this;
+ }
+
+ /**
+ * Requires that each file must not already exist.
+ *
+ * @return this FileOptions instance for chaining
+ */
+ public FileOptions mustNotExist() {
+ existenceType = ExistenceType.MUST_NOT_EXIST;
+ return this;
+ }
+
+ /**
+ * Validates a single file path against the configured existence type.
+ */
+ @Override
+ public boolean isValid(final String value) {
+ return FileOption.isFileValid(existenceType, value);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Svjatoslav Commons - shared library of common functionality. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.commons.cli_helper.parameter_parser.parameter;
+
+import eu.svjatoslav.commons.cli_helper.parameter_parser.Option;
+import eu.svjatoslav.commons.cli_helper.parameter_parser.ParameterCount;
+
+/**
+ * Represents a command-line option that accepts exactly one floating-point parameter.
+ */
+public class FloatOption extends Option<Float, FloatOption> {
+
+ /**
+ * Constructs a FloatOption with a brief description and exactly one parameter.
+ *
+ * @param description a brief description of the option (e.g., "Scale factor").
+ */
+ public FloatOption(final String description) {
+ super(description, ParameterCount.ONE);
+ }
+
+ /**
+ * Describes the expected format ("Floating point number").
+ */
+ @Override
+ public String describeFormat() {
+ return "Floating point number. Example: 3.14";
+ }
+
+ /**
+ * Returns the float value specified by the user.
+ *
+ * @throws RuntimeException if the user did not provide exactly one parameter.
+ */
+ @Override
+ public Float getValue() {
+ if (parameters.size() != 1) {
+ throw new RuntimeException("Parameter '" + description
+ + "' must have exactly 1 argument.");
+ }
+ return Float.parseFloat(parameters.get(0));
+ }
+
+ /**
+ * Checks if the given string can be parsed as a float.
+ *
+ * @param value the string to validate
+ * @return {@code true} if the string is a valid float, otherwise {@code false}
+ */
+ @Override
+ public boolean isValid(final String value) {
+ try {
+ Float.valueOf(value);
+ return true;
+ } catch (final NumberFormatException e) {
+ return false;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Svjatoslav Commons - shared library of common functionality. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.commons.cli_helper.parameter_parser.parameter;
+
+import eu.svjatoslav.commons.cli_helper.parameter_parser.ParameterCount;
+import eu.svjatoslav.commons.cli_helper.parameter_parser.Option;
+
+/**
+ * Represents a command-line option that accepts exactly one parameter interpreted as an integer.
+ */
+public class IntegerOption extends Option<Integer, IntegerOption> {
+
+ /**
+ * Constructs an IntegerOption with exactly one parameter required.
+ *
+ * @param description a brief description of what this option represents.
+ */
+ public IntegerOption(final String description) {
+ super(description, ParameterCount.ONE);
+ }
+
+ /**
+ * Describes the expected format ("Integer.").
+ */
+ @Override
+ public String describeFormat() {
+ return "Integer.";
+ }
+
+ /**
+ * Returns the integer value specified by the user.
+ *
+ * @throws RuntimeException if the user did not provide exactly one parameter.
+ */
+ @Override
+ public Integer getValue() {
+ if (parameters.size() != 1) {
+ throw new RuntimeException("Parameter '" + description
+ + "' must have exactly 1 argument.");
+ }
+ return Integer.parseInt(parameters.get(0));
+ }
+
+ /**
+ * Checks whether the given string can be parsed as an integer.
+ *
+ * @param value the string to validate
+ * @return {@code true} if the string is a valid integer, otherwise {@code false}
+ */
+ @Override
+ public boolean isValid(final String value) {
+ try {
+ Integer.valueOf(value);
+ return true;
+ } catch (final NumberFormatException e) {
+ return false;
+ }
+ }
+}
--- /dev/null
+/*
+ * Svjatoslav Commons - shared library of common functionality. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.commons.cli_helper.parameter_parser.parameter;
+
+import eu.svjatoslav.commons.cli_helper.parameter_parser.ParameterCount;
+import eu.svjatoslav.commons.cli_helper.parameter_parser.Option;
+
+/**
+ * Represents a command-line option that accepts exactly zero parameters.
+ * Often used for flags that are either present or absent.
+ */
+public class NullOption extends Option<Boolean, NullOption> {
+
+ /**
+ * Creates a NullOption (e.g. a boolean flag) that requires no parameters.
+ *
+ * @param description a brief description of what this option does (e.g., "Enable debug mode").
+ */
+ public NullOption(final String description) {
+ super(description, ParameterCount.NONE);
+ }
+
+ /**
+ * Describes the expected format of this option (i.e., no parameters).
+ *
+ * @return the string "None."
+ */
+ @Override
+ public String describeFormat() {
+ return "None.";
+ }
+
+ /**
+ * Returns whether this option was present on the command line.
+ *
+ * @return {@code true} if the user specified this option, otherwise {@code false}.
+ */
+ @Override
+ public Boolean getValue() {
+ return isPresent();
+ }
+
+ /**
+ * Always returns {@code true}, since this option has no parameters and needs no validation.
+ */
+ @Override
+ public boolean isValid(final String value) {
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Svjatoslav Commons - shared library of common functionality. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.commons.cli_helper.parameter_parser.parameter;
+
+import eu.svjatoslav.commons.cli_helper.parameter_parser.ParameterCount;
+import eu.svjatoslav.commons.cli_helper.parameter_parser.Option;
+
+/**
+ * Represents a command-line option that accepts exactly one string parameter.
+ * An optional default value can be provided; if so, this option is considered "present" even without user input.
+ */
+public class StringOption extends Option<String, StringOption> {
+
+ /**
+ * Default value to return if the user does not provide a parameter.
+ * If non-null, this option is automatically considered present.
+ */
+ public final String defaultValue;
+
+ /**
+ * Constructs a StringOption with no default value.
+ *
+ * @param description a brief description of what this option does.
+ */
+ public StringOption(final String description) {
+ super(description, ParameterCount.ONE);
+ this.defaultValue = null;
+ }
+
+ /**
+ * Constructs a StringOption with a specified default value.
+ *
+ * @param description a brief description of what this option does.
+ * @param defaultValue the default string to use if the user does not supply a parameter.
+ */
+ public StringOption(final String description, String defaultValue) {
+ super(description, ParameterCount.ONE);
+ this.defaultValue = defaultValue;
+
+ // If a default value is provided, mark this option as present by default.
+ this.setPresent(true);
+ }
+
+ @Override
+ public String describeFormat() {
+ return "String.";
+ }
+
+ /**
+ * Returns the string parameter or the default value if none was provided.
+ *
+ * @throws RuntimeException if multiple parameters were provided.
+ */
+ @Override
+ public String getValue() {
+ if (parameters.isEmpty() && defaultValue != null) {
+ return defaultValue;
+ }
+ if (parameters.size() == 1) {
+ return parameters.get(0);
+ }
+ throw new RuntimeException("Parameter '" + description
+ + "' must have exactly 1 argument.");
+ }
+
+ /**
+ * Always returns {@code true} since any string is considered valid for this option.
+ */
+ @Override
+ public boolean isValid(final String value) {
+ return true;
+ }
+}
--- /dev/null
+/*
+ * Svjatoslav Commons - shared library of common functionality. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.commons.cli_helper.parameter_parser.parameter;
+
+import eu.svjatoslav.commons.cli_helper.parameter_parser.ParameterCount;
+import eu.svjatoslav.commons.cli_helper.parameter_parser.Option;
+
+import java.util.List;
+
+/**
+ * Represents a command-line option that accepts one or more string parameters.
+ */
+public class StringOptions extends Option<List<String>, StringOptions> {
+
+ /**
+ * Creates a StringOptions object, requiring one or more string parameters.
+ *
+ * @param description a brief description of what these strings represent.
+ */
+ public StringOptions(final String description) {
+ super(description, ParameterCount.ONE_OR_MORE);
+ }
+
+ @Override
+ public String describeFormat() {
+ return "One to many string.";
+ }
+
+ /**
+ * Returns the list of strings provided by the user.
+ */
+ @Override
+ public List<String> getValue() {
+ return parameters;
+ }
+
+ /**
+ * Always returns true since any string is valid for this option.
+ */
+ @Override
+ public boolean isValid(final String value) {
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Svjatoslav Commons - shared library of common functionality. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.commons.cli_helper.parameter_parser;
+
+import eu.svjatoslav.commons.cli_helper.parameter_parser.parameter.FileOption;
+import eu.svjatoslav.commons.cli_helper.parameter_parser.parameter.StringOption;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+
+public class ParserTest {
+
+ Parser parser;
+
+ @Before
+ public void setUp() {
+ parser = new Parser();
+ }
+
+ @Test
+ public void testParse() throws IOException {
+
+ // define allowed parameters
+ final StringOption helpOption = parser.add(new StringOption("Show help screen")
+ .addAliases("--help", "-h").setMandatory());
+
+ final StringOption compileOption = parser.add(new StringOption("Compile code"))
+ .addAliases("--compile", "-c");
+
+ FileOption fileOption = parser.add(new FileOption("Input file")
+ .addAliases("-i").mustExist());
+
+ createTemporaryFile();
+
+ // check help generation
+ parser.showHelp();
+
+ // parse arguments
+ parser.parse(new String[]{"--help", "section", "-i", "/tmp/file with spaces"});
+
+ // --help was in the arguments
+ assertTrue(helpOption.isPresent());
+
+ // compile was not present
+ assertFalse(compileOption.isPresent());
+
+ // validate that help argument was "section"
+ assertEquals("section", helpOption.getValue());
+
+ assertTrue(fileOption.isPresent());
+ assertEquals("/tmp/file with spaces", fileOption.getValue().getAbsolutePath());
+
+ }
+
+ private void createTemporaryFile() throws IOException {
+ File fileWithSpaces = new File("/tmp/file with spaces");
+ FileWriter writer = new FileWriter(fileWithSpaces);
+ writer.write("test");
+ writer.close();
+ }
+}
--- /dev/null
+#!/bin/bash
+cd "${0%/*}"; if [ "$1" != "T" ]; then gnome-terminal -e "'$0' T"; exit; fi;
+
+cd ..
+
+cola
+git push
+
+echo ""
+echo "Press ENTER to close this window."
+read
--- /dev/null
+#!/bin/bash
+
+#
+# This is a helper bash script that starts IntelliJ with the current project.
+# Script is written is such a way that you can simply click on it in file
+# navigator to run it.
+#
+#
+# Script assumes:
+#
+# + GNU operating system
+# + IntelliJ is installed and commandline launcher "idea" is enabled.
+#
+
+cd "${0%/*}"
+cd ..
+
+setsid idea . &>/dev/null
--- /dev/null
+#!/bin/bash
+cd "${0%/*}"; if [ "$1" != "T" ]; then gnome-terminal -e "'$0' T"; exit; fi;
+
+cd ..
+
+# Build the project jar file and the apidocs.
+mvn clean package
+
+# Export org to html using emacs in batch mode
+(
+ cd doc/
+
+ rm -f index.html
+ emacs --batch -l ~/.emacs --visit=index.org --funcall=org-html-export-to-html --kill
+
+ #rm setup.html
+ #emacs --batch -l ~/.emacs --visit=setup.org --funcall=org-html-export-to-html --kill
+)
+
+# Copy the apidocs to the doc folder so that they can be uploaded to the server.
+rm -rf doc/apidocs/
+cp -r target/apidocs/ doc/
+
+# Upload project homepage to the server.
+rsync -avz --delete -e 'ssh -p 10006' doc/ n0@www3.svjatoslav.eu:/mnt/big/projects/cli-helper/
+
+echo ""
+echo "Press ENTER to close this window."
+read