com.helger.cli.Option Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ph-cli Show documentation
Show all versions of ph-cli Show documentation
Java commandline interface based on Apache commons-cli
The newest version!
/*
* Original copyright by Apache Software Foundation
* Copyright (C) 2017-2024 Philip Helger (www.helger.com)
* philip[at]helger[dot]com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.helger.cli;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.Nonempty;
import com.helger.commons.annotation.ReturnsMutableCopy;
import com.helger.commons.builder.IBuilder;
import com.helger.commons.equals.EqualsHelper;
import com.helger.commons.hashcode.HashCodeGenerator;
import com.helger.commons.string.StringHelper;
import com.helger.commons.string.ToStringGenerator;
@Immutable
public class Option implements IOptionBase
{
public enum EOptionMultiplicity
{
/** 0..1, {0,1} or ?
*/
OPTIONAL_ONCE,
/** 0..n, {0,} or *
*/
OPTIONAL_MANY,
/** 1..1, required */
REQUIRED_ONCE,
/** 1..n, {1,} or +
*/
REQUIRED_MANY;
public boolean isOptional ()
{
return this == OPTIONAL_ONCE || this == OPTIONAL_MANY;
}
public boolean isRequired ()
{
return this == REQUIRED_ONCE || this == REQUIRED_MANY;
}
public boolean isOnce ()
{
return this == OPTIONAL_ONCE || this == REQUIRED_ONCE;
}
public boolean isRepeatable ()
{
return this == OPTIONAL_MANY || this == REQUIRED_MANY;
}
@Nonnull
public EOptionMultiplicity getAsRequired ()
{
if (this == OPTIONAL_ONCE)
return REQUIRED_ONCE;
if (this == OPTIONAL_MANY)
return REQUIRED_MANY;
return this;
}
@Nonnull
public EOptionMultiplicity getAsOptional ()
{
if (this == REQUIRED_ONCE)
return OPTIONAL_ONCE;
if (this == REQUIRED_MANY)
return OPTIONAL_MANY;
return this;
}
@Nonnull
public EOptionMultiplicity getAsRepeatable ()
{
if (this == OPTIONAL_ONCE)
return OPTIONAL_MANY;
if (this == REQUIRED_ONCE)
return REQUIRED_MANY;
return this;
}
@Nonnull
public EOptionMultiplicity getAsOnce ()
{
if (this == OPTIONAL_MANY)
return OPTIONAL_ONCE;
if (this == REQUIRED_MANY)
return REQUIRED_ONCE;
return this;
}
}
public static final char DEFAULT_VALUE_SEPARATOR = 0;
/** constant that specifies the number of argument values is infinite */
public static final int INFINITE_VALUES = -1;
private static final Logger LOGGER = LoggerFactory.getLogger (Option.class);
/** the name of the option */
private final String m_sShortOpt;
/** the long representation of the option */
private final String m_sLongOpt;
/** description of the option */
private final String m_sDescription;
/** The minimum argument count. Must be ≤ maxArgs */
private final int m_nMinArgs;
/** The maximum argument count. Must be ≥ minArgs */
private final int m_nMaxArgs;
/**
* the name of the argument for this option. Makes only sense, if minArgs >
* 0
*/
private final String m_sArgName;
/**
* Multiplicity to use.
*/
private final EOptionMultiplicity m_eMultiplicity;
/**
* the character that is the value separator. Makes only sense if minArgs >
* 0
*/
private final char m_cValueSep;
/**
* Private constructor used by the nested Builder class.
*
* @param aBuilder
* builder used to create this option
*/
protected Option (@Nonnull final Builder aBuilder)
{
ValueEnforcer.notNull (aBuilder, "Builder");
m_sShortOpt = aBuilder.m_sShortOpt;
m_sLongOpt = aBuilder.m_sLongOpt;
m_sDescription = aBuilder.m_sDescription;
m_nMinArgs = aBuilder.m_nMinArgs;
m_nMaxArgs = aBuilder.m_nMaxArgs;
m_sArgName = aBuilder.m_sArgName;
m_eMultiplicity = aBuilder.m_eMultiplicity;
m_cValueSep = aBuilder.m_cValueSep;
}
/**
* @return the 'unique' internal Option identifier. Either short or long
* option name.
* @see #getShortOpt()
* @see #getLongOpt()
*/
@Nonnull
@Nonempty
public final String getKey ()
{
// if 'opt' is null, then it is a 'long' option
return hasShortOpt () ? m_sShortOpt : m_sLongOpt;
}
/**
* Retrieve the name of this Option.
*
* @return The name of this option. May be null
if this instance
* only has a "long option".
* @see #hasShortOpt()
* @see #getLongOpt()
*/
@Nullable
public String getShortOpt ()
{
return m_sShortOpt;
}
public boolean hasShortOpt ()
{
return StringHelper.hasText (m_sShortOpt);
}
public boolean hasShortOpt (@Nullable final String sShortOpt)
{
return sShortOpt != null && sShortOpt.equals (m_sShortOpt);
}
/**
* Retrieve the long name of this Option.
*
* @return Long name of this option, or null
if there is no long
* name.
* @see #hasLongOpt()
* @see #getShortOpt()
*/
@Nullable
public String getLongOpt ()
{
return m_sLongOpt;
}
/**
* Query to see if this Option has a long name
*
* @return boolean flag indicating existence of a long name
*/
public boolean hasLongOpt ()
{
return m_sLongOpt != null;
}
public boolean hasLongOpt (@Nullable final String sLongOpt)
{
return sLongOpt != null && sLongOpt.equals (m_sLongOpt);
}
public boolean matches (@Nullable final String sOptName)
{
return sOptName != null && (sOptName.equals (m_sShortOpt) || sOptName.equals (m_sLongOpt));
}
/**
* Retrieve the self-documenting description of this Option
*
* @return The string description of this option
*/
@Nullable
public String getDescription ()
{
return m_sDescription;
}
/**
* @return true
if a description is present, false
* if not.
*/
public boolean hasDescription ()
{
return StringHelper.hasText (m_sDescription);
}
/**
* @return Minimum number of (required) arguments. Always ≥ 0.
*/
@Nonnegative
public int getMinArgCount ()
{
return m_nMinArgs;
}
public boolean hasMinArgs ()
{
return m_nMinArgs != 0;
}
/**
* @return Maximum number of arguments. Is always ≥ 0 or
* {@link #INFINITE_VALUES} for unlimited arguments.
*/
@Nonnegative
public int getMaxArgCount ()
{
return m_nMaxArgs;
}
public boolean hasInfiniteArgs ()
{
return m_nMaxArgs == INFINITE_VALUES;
}
public boolean canHaveMoreValues (final int nSize)
{
return hasInfiniteArgs () || nSize < m_nMaxArgs;
}
public boolean canHaveArgs ()
{
return hasInfiniteArgs () || m_nMaxArgs > 0;
}
/**
* Gets the display name for the argument value.
*
* @return the display name for the argument value.
*/
@Nullable
public String getArgName ()
{
return m_sArgName;
}
/**
* @return true
if this option can appear multiple times on a
* commandline, false
if it can occur at maximum once.
*/
public boolean isRepeatable ()
{
return m_eMultiplicity.isRepeatable ();
}
/**
* Query to see if this {@link Option} is mandatory
*
* @return boolean flag indicating whether this Option is mandatory
*/
public boolean isRequired ()
{
return m_eMultiplicity.isRequired ();
}
/**
* Returns whether the display name for the argument value has been set.
*
* @return if the display name for the argument value has been set.
*/
public boolean hasArgName ()
{
return StringHelper.hasText (m_sArgName);
}
/**
* Returns the value separator character.
*
* @return the value separator character.
*/
public char getValueSeparator ()
{
return m_cValueSep;
}
/**
* Return whether this Option has specified a value separator.
*
* @return whether this Option has specified a value separator.
*/
public boolean hasValueSeparator ()
{
return m_cValueSep != DEFAULT_VALUE_SEPARATOR;
}
@Override
public boolean equals (final Object o)
{
if (this == o)
return true;
if (o == null || getClass () != o.getClass ())
return false;
final Option rhs = (Option) o;
return EqualsHelper.equals (m_sShortOpt, rhs.m_sShortOpt) && EqualsHelper.equals (m_sLongOpt, rhs.m_sLongOpt);
}
@Override
public int hashCode ()
{
return new HashCodeGenerator (this).append (m_sShortOpt).append (m_sLongOpt).getHashCode ();
}
@Override
public String toString ()
{
return new ToStringGenerator (this).appendIfNotNull ("Opt", m_sShortOpt)
.appendIfNotNull ("LongOpt", m_sLongOpt)
.appendIfNotNull ("Description", m_sDescription)
.appendIf ("MinArgs", m_nMinArgs, this::hasMinArgs)
.appendIf ("MaxArgs",
m_nMaxArgs == INFINITE_VALUES ? "infinite" : Integer.toString (m_nMaxArgs),
this::canHaveArgs)
.appendIfNotNull ("ArgName", m_sArgName)
.append ("Multiplicity", m_eMultiplicity)
.appendIf ("ValueSep", m_cValueSep, this::hasValueSeparator)
.getToString ();
}
/**
* Returns a {@link Builder} without a short option to create an
* {@link Option} using descriptive methods.
*
* @return a new {@link Builder} instance
* @since 10.1.3
*/
@Nonnull
public static Builder builder ()
{
return new Builder (null);
}
/**
* Returns a {@link Builder} to create an {@link Option} using descriptive
* methods.
*
* @param sShortOpt
* short representation of the option
* @return a new {@link Builder} instance
* @throws IllegalArgumentException
* if there are any non valid Option characters in {@code opt}
*/
@Nonnull
public static Builder builder (@Nullable final String sShortOpt)
{
return new Builder (sShortOpt);
}
/**
* A nested builder class to create Option
instances using
* descriptive methods.
*
* Example usage:
*
*
* Option option = Option.builder ("a").required (true).longOpt ("arg-name").build ();
*
*/
public static class Builder implements IBuilder