// Revision: 41 1.19.1.4 source/core/testfw/iargdict.hpp, testfw, ioc.v400, 980918 
#ifndef _IARGDICT_
#define _IARGDICT_
/**
 * Class IArgumentDictionary: General purpose class for command line parsing.
 *
 * IArgumentDictionary is a general-purpose class for parsing text arguments on
 * a command-line and creates an ordered collection of key/value pairs.
 * It parses arguments from an ITest object, a single string, or UNIX-style
 * argc/argv parameters.
 *

 * @package Test Framework
 * @category Testing
 *
 * @author Alan Liu
 * @task Initial Test Framework
 * @author Esther Tong
 * @task TestFrameworkLite
 * @author David McCusker, Christoper Miller, Carol Widra, and Kwansook Nam
 * @task Many of the other Contributors to TestFramework
 *
 * @copyright
 *      IBM Open Class Library
 *      (C) Copyright International Business Machines Corporation 1997
 *      Licensed Material - Program-Property of IBM - All Rights Reserved.
 *
 */

#include <limits.h>
#include <float.h>
#include <iset2.h>
#include <iprimtyp.hpp>
#include <iseq2.h>
#include <istring.hpp>
#include <ism2.h>

#include <itfwpl.hpp>  // pragma library definitions

#if __IBMCPP__ >= 400
#pragma namemangling(compat)
#endif

class IStringWrapper;
class ITest;
class ITieredTextBuffer;

#pragma enum(4)
#pragma pack(push,4)

//-----------------------------------------------------------------------------
/**
 * General purpose class for parsing command line text arguments.
 *
 * IArgumentDictionary is a general-purpose class for parsing text arguments on
 * a command line and creates an ordered collection of key/value pairs.
 * It parses arguments from an ITest object, a single string, or UNIX-style
 * argc/argv parameters.
 *
 * A leading hyphen character identifies keywords.  Anything without a leading
 * hyphen is a value argument.  A keyword picks up the following argument if it
 * is not another keyword.  This example shows how IArgumentDictionary parses the
 * command line input to create the key/value pairs.
 * <PRE>
 * Command-line input:
 *
 *   -parm -n sample1 Sue Tom -ccc 84 85
 *
 * </PRE>
 *
 * This table lists the options defined for the example:
 * <PRE>
 *    Key     Value     Description
 *    ---     -----     -----------
 *    -parm             The value associated with -parm is an empty IText, not NIL.
 *    -n      sample1   The -n argument picks up the following argument.
 *    1       Sue       There is no associated key so "1" is assigned.
 *    2       Tom       There is no associated key so "2" is assigned.
 *    -ccc    84        The -ccc argument picks up the value "84".
 *    3       85        There is no associated key so "3" is assigned.
 *
 * </PRE>
 *
 * The command line input arguments should be parsed and handled
 * in the setup member function.
 *
 */

class IArgumentDictionary {
public:
#ifdef _AIX
        static const IString& kEmptyText;
#else
        static const IString& _Import kEmptyText;
#endif

//-------------------------------------------------------------------------
// Canonical methods
//-------------------------------------------------------------------------

/** @group Construction, Destruction, and Assignment */

/**
 * IArgumentDictionary Constructors
 *
 * IArgumentDictionary provides several constructors with a variety of signatures.
 * Each constructor calls a corresponding importTextArguments functions.  All
 * constructors accept an optional parameter that specifies the options to be
 * treated as naked options.
 *
 * This unique constructor uses the outputTextStream() of the ITest object for its
 * own error message output.  When this constructor is used within an ITest,
 * a failure to parse a number (which typically causes the test to fail) produces
 * an error message in the diagnostic output of the ITest.  If a different constructor
 * is used, the error message appears but is lost thereafter.  It can then be difficult
 * to determine what caused the test to fail.
 *
 * @xtra This constructor takes a constant reference to a ITest object and uses
 * the inputs in theTest as arguments.
 *
 * @param theTest          Test object
 * @param nakedOptions     The options that never take values
 */
IArgumentDictionary(ITest& theTest, const IString& nakedOptions = IArgumentDictionary::kEmptyText);


/**
 * @xtra This constructor takes an IString argument (argumentString) and uses
 * its key/value pairs as the input arguments.
 *
 * @param argumentString   The argument string
 * @param nakedOptions     The options that never take values
 */
 IArgumentDictionary(const IString& argumentString, const IString& nakedOptions = IArgumentDictionary::kEmptyText);


/**
 * @xtra This constructor takes the standard UNIX style argc and argv arguments and uses
 * them as the input arguments.
 *
 * @param argc             The standard argc argument as passed in from the command line
 * @param argv             The standard argv argument as passed in from the command line
 * @param nakedOptions     The options that never take values
 */
IArgumentDictionary(const int argc, const char *const *argv, const IString& nakedOptions = IArgumentDictionary::kEmptyText);


/**
 * @xtra Default Constructor
 */
IArgumentDictionary();


/**
 * @xtra Copy Constructor for IArgumentDictionary
 *
 * @param source     The IArgumentDictionary object to be copied.
 *
 */
IArgumentDictionary(const IArgumentDictionary& source);


/**
 * Standard assignment operator for IArgumentDictionary.
 *
 * @param rhs     The IArgumentDictionary object used to do assignment
 *
 */
IArgumentDictionary& operator=(const IArgumentDictionary& rhs);


/**
 * Destructor
 *
 * IArgumentDictionary destructor function.
 */
virtual ~IArgumentDictionary();


//-------------------------------------------------------------------------
/** @group Importing and Exporting IString arguments */


/**
 * Member Function specifyNakedOptions
 *
 * Specifies that the given options never take values in a key/value pair.
 *
 * Call these methods *before* calling ImportTextArguments to
 * change the way input arguments are handled.
 *
 * @param nakedOptions IString containing a space delineated list of the options that never takes values (e.g., ("-p -i -q)")
 *
 *
 */
void specifyNakedOptions(const IString& nakedOptions);


/**
 * Member Function lookupSimilarKeys
 *
 * Toggle turning on/off findSimilarKey lookups.
 *
 * If lookupSimilarKeys(true) is called, the dictionary will henceforth call
 * findSimilarKey for all lookups.  It will also match similar keys during
 * importTextArguments calls when checking to see if keys are naked keys.
 *
 * Call findSimilarKey for all lookups and match similar keys during ImporTextArguments.
 *
 * @param bool     true/false
 * @see findSimilarKey
 * @see importTextArguments
 *
 */
void lookupSimilarKeys(bool);


/**
 * @xtra Copies text arguments from the passed-in parameters.
 *
 * @param theTest The ITest object where the text arguments should be copied from.
 *
 * @see exportTextArguments
 *
 */
void importTextArguments(ITest& theTest);


/**
 * @xtra Copies text arguments from the passed-in parameters.
 *
 * @param argumentString Name of a white-space delimited string of keys and values
 *
 * @see exportTextArguments
 *
 */
void importTextArguments(const IString& argumentString);

/**
 * @xtra Copies text arguments from the passed-in parameters.
 *
 * @param argc UNIX-style argument count
 * @param argv UNIX-style argument vector
 *
 * @see exportTextArguments
 *
 */
void importTextArguments(const int argc, const char *const *argv);



//-------------------------------------------------------------------------

/**
 * Member Function exportTextArguments
 *
 * Exports the arguments in this dictionary as a string of space-delimited keys and values.
 * (semantically const)
 *
 * @param argumentString The string of keys and values
 *
 * @see importTextArguments
 *
 */
void exportTextArguments(IString& argumentString);


//-------------------------------------------------------------------------

/** @group General lookup */

/**
 * Member Function countOfArguments
 *
 * Returns the number of key/value pairs in the dictionary.
 *
 * @return unsigned long value with the number of key/value pairs
 */
unsigned long countOfArguments() const;


/**
 * Member Function removeOnLookup
 *
 * Causes key/value pairs to be removed from the dictionary as they are looked up.
 *
 * If removeOnLookup is called with argument true, then keys and values will be
 * removed from the dictionary (keys will also be deleted) as they are looked up.
 * This is useful if you want to make sure that all arguments are parsed by:
 * <PRE>
 *     - calling removeOnLookup(true) to turn on automatic key/value pair removal
 *     - deleting each argument as you retrieve it
 *     - checking count() == 0 to make sure there are no leftover arguments
 * </PRE>
 *
 * @param bool true to turn on automatic key/value pair removal; false to turn it off
 *
 */
void removeOnLookup(bool);


/**
 * Member Function textAt
 *
 * Returns the value associated with the given key in the argument "value".
 *
 * If the value was numeric, textAt returns it as a string.  If no value was
 * given, an empty IString object is returned.
 *
 * @param key           The given key
 * @param value     IString for returned value
 *
 * @return bool     false if no such key exists; otherwise true
 *
 */
bool textAt(const IString& key, IString& value);


/**
 * Member Function nthValue
 *
 * Returns the keyless value with the given index number in the argument "value".
 *
 * @param index     The given index
 * @param value     IString for returned value
 *
 * @return bool    false if no such value exists; otherwise true
 *
 */
bool nthValue(const short index, IString& value);


/**
 * Member Function findSimilarKey
 *
 * Looks for a key similar to keyToMatch.
 * If a similar key is found, it is returned in the argument newKey.
 *
 * Note:  key is considered to match if it is of equal length or longer than the match
 * key, and if all characters in the match key are equal (case insensitive) to those
 * in the key.  Call lookupSimilarKeys(true) to always have the dictionary look for
 * similar keys.
 *
 * @return True if a similar key is found.  False if no similar key can be found.
 *
 * @param keyToMatch    Name of the key to match
 * @param newKey        Name of the key similar to keyToMatch
 *
 * @see lookupSimilarKeys
 *
 */
bool findSimilarKey(const IString& keyToMatch, IString& newKey) const;

//-------------------------------------------------------------------------
/** @group Number lookup */

/**
 * Member Function numberAt
 *
 * Looks up a number argument corresponding to the given key.
 * The number must fall in the range minAllowed <= x <= maxAllowed.
 *
 * If the number parse badly, the argument does not exist, or
 * is out of range, false is returned and leaves "value" unchanged.
 *
 * @xtra Supports "double" values.
 *
 * @return true if the argument exists, it parses perfectly and is within the given range, inclusively
 * @return false if the number parses badly, is out of range, or the argument does not exist, leaving value unchanged.
 *
 * @param key           Key of the number
 * @param value         Value of the number (double)
 * @param minAllowed    Minimum value allowed (double)
 * @param maxAllowed    Maximum value allowed (double)
 *
 */
bool numberAt(const IString& key, double& value, double minAllowed = -DBL_MAX, double maxAllowed = DBL_MAX);


/**
 * @xtra Supports "long" values.
 *
 * @return true if the argument exists, it parses perfectly and is within the given range, inclusively
 * @return false if the number parses badly, is out of range, or the argument does not exist, leaving value unchanged.
 *
 * @param key           Key of the number
 * @param value         Value of the number (long)
 * @param minAllowed    Minimum value allowed (long)
 * @param maxAllowed    Maximum value allowed (long)
 *
 */
bool numberAt(const IString& key, long& value, long minAllowed = LONG_MIN, long maxAllowed = LONG_MAX);


/**
 * Member Function nthNumber
 *
 * Looks up the nth keyless number argument corresponding to the given index.
 *
 * The number must fall in the range minAllowed and maxAllowed
 *
 * @xtra Supports "double" values.
 *
 * @return true if the argument exists, it parses perfectly, and it is within the given range
 * @return false if the number parses badly, is out of range, or the argument does not exist, leaving value unchanged
 *
 * @param index         Index of the number
 * @param value         Value of the number (double)
 * @param minAllowed    Minimum value allowed (double)
 * @param maxAllowed    Maximum value allowed (double)
 *
 */

bool nthNumber(const short index, double& value, double minAllowed = -DBL_MAX, double maxAllowed = DBL_MAX);

/**
 * @xtra Supports "long" values.
 *
 * @return true if the argument exists, it parses perfectly, and it is within the given range
 * @return false if the number parses badly, is out of range, or the argument does not exist, leaving value unchanged
 *
 * @param index         Index of the number
 * @param value         Value of the number (long)
 * @param minAllowed    Minimum value allowed (long)
 * @param maxAllowed    Maximum value allowed (long)
 *
 */
bool nthNumber(const short index, long& value, long minAllowed = LONG_MIN, long maxAllowed = LONG_MAX);


//------------------------------------------------------------------------------
private:

static void tokenize(const IString& tokenString, ISequence<IString>* textList);

void importTextArguments(ISequence<IString>& textArguments);

bool rawNumberAt(const IString& key, double& value, double& confidence);
bool lookupValue(const IString& keyArg, IString& value);
void initialize(const IString& nakedOptions, ITieredTextBuffer* out = 0);
bool lookupMember(const IString& text, const ISequence<IString>& col, IString& matchingText);

ITieredTextBuffer& out();    // For output of messages, etc.

bool fRemoveOnLookup;
bool fLookupSimilarKeys;
ISequence<IString> fNakedOptions;
ISortedMap<IStringWrapper, IString>* fDictionary;

ITieredTextBuffer*  fOut;    // Not copied
bool fLocalOut;              // If true then we created *fOut, o/w it's an alias

};

#pragma pack(pop)
#pragma enum(pop)

#if __IBMCPP__ >= 400
#pragma namemangling()
#endif

#endif // _IARGDICT_
