rosaria/Legacy/Aria/src/ArArgumentBuilder.cpp
2021-12-16 14:07:59 +00:00

970 lines
25 KiB
C++

/*
Adept MobileRobots Robotics Interface for Applications (ARIA)
Copyright (C) 2004, 2005 ActivMedia Robotics LLC
Copyright (C) 2006, 2007, 2008, 2009, 2010 MobileRobots Inc.
Copyright (C) 2011, 2012, 2013 Adept Technology
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
If you wish to redistribute ARIA under different terms, contact
Adept MobileRobots for information about a commercial version of ARIA at
robots@mobilerobots.com or
Adept MobileRobots, 10 Columbia Drive, Amherst, NH 03031; +1-603-881-7960
*/
#include "ArExport.h"
#include "ariaOSDef.h"
#include "ArArgumentBuilder.h"
#include "ArLog.h"
#include <stdarg.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
char * cppstrdup(const char *str)
{
char *ret;
ret = new char[strlen(str) + 1];
strcpy(ret, str);
return ret;
}
/**
* @param argvLen the largest number of arguments to parse
* @param extraSpaceChar if not NULL, then this character will also be
* used to break up arguments (in addition to whitespace)
* @param ignoreNormalSpaces a bool set to true if only the extraSpaceChar
* should be used to separate arguments
* @param isPreCompressQuotes a bool set to true if strings enclosed in
* double-quotes should be parsed as a single argument (such strings
* must be surrounded by spaces). This is roughly equivalent to calling
* ArArgumentBuilder::compressQuoted(false) on the resulting builder, but
* is more efficient and handles embedded spaces better. The default value
* is false and preserves the original behavior where each argument is a
* space-separated alphanumeric string.
**/
AREXPORT ArArgumentBuilder::ArArgumentBuilder(size_t argvLen,
char extraSpaceChar,
bool ignoreNormalSpaces,
bool isPreCompressQuotes)
{
myArgc = 0;
myOrigArgc = 0;
myArgvLen = argvLen;
myArgv = new char *[myArgvLen];
myFirstAdd = true;
myExtraSpace = extraSpaceChar;
myIgnoreNormalSpaces = ignoreNormalSpaces;
myIsPreCompressQuotes = isPreCompressQuotes;
myIsQuiet = false;
}
AREXPORT ArArgumentBuilder::ArArgumentBuilder(const ArArgumentBuilder & builder)
{
size_t i;
myFullString = builder.myFullString;
myExtraString = builder.myExtraString;
myArgc = builder.getArgc();
myArgvLen = builder.getArgvLen();
myOrigArgc = myArgc;
myArgv = new char *[myArgvLen];
for (i = 0; i < myArgc; i++)
myArgv[i] = cppstrdup(builder.getArg(i));
//myArgv[i] = strdup(builder.getArg(i));
myIsQuiet = builder.myIsQuiet;
myExtraSpace = builder.myExtraSpace;
myIgnoreNormalSpaces = builder.myIgnoreNormalSpaces;
myIsPreCompressQuotes = builder.myIsPreCompressQuotes;
}
AREXPORT ArArgumentBuilder &ArArgumentBuilder::operator=(const ArArgumentBuilder & builder)
{
if (this != &builder) {
size_t i = 0;
// Delete old stuff...
if (myOrigArgc > 0)
{
for (i = 0; i < myOrigArgc; ++i)
delete[] myArgv[i];
}
delete[] myArgv;
// Then copy new stuff...
myFullString = builder.myFullString;
myExtraString = builder.myExtraString;
myArgc = builder.getArgc();
myArgvLen = builder.getArgvLen();
myOrigArgc = myArgc;
myArgv = new char *[myArgvLen];
for (i = 0; i < myArgc; i++) {
myArgv[i] = cppstrdup(builder.getArg(i));
}
myIsQuiet = builder.myIsQuiet;
myExtraSpace = builder.myExtraSpace;
myIgnoreNormalSpaces = builder.myIgnoreNormalSpaces;
myIsPreCompressQuotes = builder.myIsPreCompressQuotes;
}
return *this;
}
AREXPORT ArArgumentBuilder::~ArArgumentBuilder()
{
size_t i;
if (myOrigArgc > 0)
{
for (i = 0; i < myOrigArgc; ++i)
delete[] myArgv[i];
}
delete[] myArgv;
}
AREXPORT void ArArgumentBuilder::removeArg(size_t which,
bool isRebuildFullString)
{
size_t i;
char *temp;
if (which < 0) {
ArLog::log(ArLog::Terse, "ArArgumentBuilder::removeArg: cannot remove arg at negative index (%i)",
which);
return;
}
if (which > myArgc - 1)
{
ArLog::log(ArLog::Terse, "ArArgumentBuilder::removeArg: %d is greater than the number of arguments which is %d", which, myArgc);
return;
}
temp = myArgv[which];
//delete[] myArgv[which];
for (i = which; i < myArgc - 1; i++)
myArgv[i] = myArgv[i+1];
// delete the one off the end
myArgc -= 1;
// Just stuffing the deleted argument value at the end of the array (not sure why)
myArgv[i] = temp;
// Note: It seems that compressQuoted calls removeArg and depends on it not
// changing the full string. Therefore, the parameter was added so that the
// desired behavior could be specified by the caller.
if (isRebuildFullString) {
rebuildFullString();
}
}
AREXPORT void ArArgumentBuilder::add(const char *str, ...)
{
char buf[10000];
va_list ptr;
va_start(ptr, str);
vsnprintf(buf, sizeof(buf), str, ptr);
internalAdd(buf, -1);
va_end(ptr);
}
bool ArArgumentBuilder::isSpace(char c)
{
if ((!myIgnoreNormalSpaces) && (isspace(c))) {
return true;
}
else if ((myExtraSpace != '\0') && (c == myExtraSpace)) {
return true;
}
return false;
} // end method isSpace
bool ArArgumentBuilder::isStartArg(const char *buf,
int len,
int index,
int *endArgFlagsOut)
{
if (index < len) {
if (!isSpace(buf[index])) {
if ((myIsPreCompressQuotes) && (buf[index] == '\"')) {
if (endArgFlagsOut != NULL) {
*endArgFlagsOut = ANY_SPACE | QUOTE;
}
}
else { // not precompressed quote
if (endArgFlagsOut != NULL) {
// This is set to ANY_SPACE to preserve the original behavior -- i.e. space
// character "matches" with myExtraSpace (if set)
*endArgFlagsOut = ANY_SPACE;
}
} // end else not precompressed quote
return true;
} // end if not space
} // end if not end of buf
return false;
} // end method isStartArg
bool ArArgumentBuilder::isEndArg(const char *buf,
int len,
int &index,
int endArgFlags)
{
if (index >= len) {
return true;
}
if ((endArgFlags & QUOTE) != 0) {
if (buf[index] == '\"') {
if ((index + 1 >= len) || (buf[index + 1] == '\0')) {
// Quote is at EOL
index++;
return true;
}
else if (isSpace(buf[index + 1])) {
// Space follows quote
index++;
return true;
}
} // end if quote found
} // end if need to find quote
else { // just looking for a space
if (isSpace(buf[index])) {
return true;
}
} // end else just looking for a space
return false;
} // end method isEndArg
/**
Internal function that adds a string starting at some given space
@param str the string to add
@param position the position to add the string at, a position less
than 0 means to add at the end, if this number is greater than how
many positions exist then it will also be added at the end
**/
AREXPORT void ArArgumentBuilder::internalAdd(const char *str, int position)
{
char buf[10000];
int i = 0;
int j = 0;
size_t k = 0;
int len = 0;
bool addAtEnd = true;
//size_t startingArgc = getArgc();
bool isArgInProgress = false;
int curArgStartIndex = -1;
if (position < 0 || (size_t)position > myArgc)
addAtEnd = true;
else
addAtEnd = false;
strncpy(buf, str, sizeof(buf));
len = strlen(buf);
// can do whatever you want with the buf now
// first we advance to non-space
for (i = 0; i < len; ++i)
{
if (!isSpace(buf[i])) {
//(!myIgnoreNormalSpaces && !isspace(buf[i])) ||
// (myExtraSpace != '\0' && buf[i] != myExtraSpace))
break;
} // end if non-space found
} // end for each char in buffer
// see if we're done
if (i == len)
{
if (!myIsQuiet) {
ArLog::log(ArLog::Verbose, "All white space add for argument builder.");
}
return;
}
int endArgFlags = ANY_SPACE;
// walk through the line until we get to the end of the buffer...
// we keep track of if we're looking for white space or non-white...
// if we're looking for white space when we find it we have finished
// one argument, so we toss that into argv, reset pointers and moveon
for (curArgStartIndex = i; ; ++i)
{
// Remove the slash of escaped spaces. This is primarily done to handle command
// line arguments (especially on Linux). If quotes are "pre-compressed", then
// the backslash is preserved. Consecutive backslashes are also preserved
// (i.e. "\\ " is not modified).
if (!myIsPreCompressQuotes &&
(buf[i] == '\\') && (i + 1 < len) && (buf[i + 1] == ' ') &&
((i == 0) || (buf[i - 1] != '\\')))
{
for (j = i; j < len && j != '\0'; j++)
{
buf[j] = buf[j + 1];
}
--len;
} // end if escaped space
// If we're not in the middle of an argument, then determine whether the
// current buffer position marks the start of one.
else if ((!isArgInProgress) &&
(i < len) &&
(buf[i] != '\0') &&
(isStartArg(buf, len, i, &endArgFlags)))
{
curArgStartIndex = i;
isArgInProgress = true;
}
// If we are in the middle of an argument, then determine whether the current
// buffer position marks the end of it. (Note that i may be incremented by
// isEndArg when quotes are pre-compressed.)
else if (isArgInProgress &&
((i == len) ||
(buf[i] == '\0') ||
(isEndArg(buf, len, i, endArgFlags))))
{
// see if we have room in our argvLen
if (myArgc + 1 >= myArgvLen)
{
ArLog::log(ArLog::Terse, "ArArgumentBuilder::Add: could not add argument since argc (%u) has grown beyond the argv given in the constructor (%u)", myArgc, myArgvLen);
}
else // room in arg array
{
// if we're adding at the end just put it there, also put it
// at the end if its too far out
if (addAtEnd)
{
myArgv[myArgc] = new char[i - curArgStartIndex + 1];
strncpy(myArgv[myArgc], &buf[curArgStartIndex], i - curArgStartIndex);
myArgv[myArgc][i - curArgStartIndex] = '\0';
// add to our full string
// if its not our first add a space (or whatever our space char is)
if (!myFirstAdd && myExtraSpace == '\0')
myFullString += " ";
else if (!myFirstAdd)
myFullString += myExtraSpace;
myFullString += myArgv[myArgc];
myFirstAdd = false;
myArgc++;
myOrigArgc = myArgc;
}
// otherwise stick it where we wanted it if we can or just
else // insert arg at specified position
{
// first move things down
for (k = myArgc + 1; k > (size_t)position; k--)
{
myArgv[k] = myArgv[k - 1];
}
myArgc++;
myOrigArgc = myArgc;
myArgv[position] = new char[i - curArgStartIndex + 1];
strncpy(myArgv[position], &buf[curArgStartIndex], i - curArgStartIndex);
myArgv[position][i - curArgStartIndex] = '\0';
position++;
rebuildFullString();
myFirstAdd = false;
} // end else insert arg at specified position
} // end else room in arg array
isArgInProgress = false;
endArgFlags = ANY_SPACE;
} // end else if found end of argument
// if we're at the end or its a null, we're at the end of the buf
if (i == len || buf[i] == '\0')
break;
} // end for each char in buffer (after non-whitespace found)
} // end method internalAdd
/**
@param str the string to add
@param position the position to add the string at, a position less
than 0 means to add at the end, if this number is greater than how
many positions exist then it will also be added at the end
**/
AREXPORT void ArArgumentBuilder::addPlain(const char *str, int position)
{
internalAdd(str, position);
}
/**
@param argc how many arguments to add
@param argv the strings to add
@param position the position to add the string at, a position less
than 0 means to add at the end, if this number is greater than how
many positions exist then it will also be added at the end
**/
AREXPORT void ArArgumentBuilder::addStrings(char **argv, int argc,
int position)
{
addStrings(argc, argv, position);
}
/**
@param argc how many arguments to add
@param argv the strings to add
@param position the position to add the string at, a position less
than 0 means to add at the end, if this number is greater than how
many positions exist then it will also be added at the end
**/
AREXPORT void ArArgumentBuilder::addStrings(int argc, char **argv,
int position)
{
int i;
if(position < 0)
{
// Don't try to use incremental positions, since the sequence would be out
// of order, just keep using the same special "at-end" meaning of negative position
// value
for (i = 0; i < argc; i++)
internalAdd(argv[i], position);
}
else
{
for (i = 0; i < argc; i++)
internalAdd(argv[i], position + i);
}
}
/**
@param argc how many arguments to add
@param argv the strings to add
@param position the position to add the string at, a position less
than 0 means to add at the end, if this number is greater than how
many positions exist then it will also be added at the end
**/
AREXPORT void ArArgumentBuilder::addStringsAsIs(int argc, char **argv,
int position)
{
int i;
if(position < 0)
{
// Don't try to use incremental positions, since the sequence would be out
// of order, just keep using the same special "at-end" meaning of negative position
// value
for (i = 0; i < argc; i++)
internalAddAsIs(argv[i], position);
}
else
{
for (i = 0; i < argc; i++)
internalAddAsIs(argv[i], position + i);
}
}
/**
@param str the string to add
@param position the position to add the string at, a position less
than 0 means to add at the end, if this number is greater than how
many positions exist then it will also be added at the end
**/
AREXPORT void ArArgumentBuilder::addPlainAsIs(const char *str, int position)
{
internalAddAsIs(str, position);
}
AREXPORT void ArArgumentBuilder::internalAddAsIs(const char *str, int position)
{
size_t k = 0;
bool addAtEnd;
if (position < 0 || (size_t)position > myArgc)
addAtEnd = true;
else
addAtEnd = false;
if (addAtEnd)
{
myArgv[myArgc] = new char[strlen(str) + 1];
strcpy(myArgv[myArgc], str);
myArgv[myArgc][strlen(str)] = '\0';
// add to our full string
// if its not our first add a space (or whatever our space char is)
if (!myFirstAdd && myExtraSpace == '\0')
myFullString += " ";
else if (!myFirstAdd)
myFullString += myExtraSpace;
myFullString += myArgv[myArgc];
myFirstAdd = false;
myArgc++;
myOrigArgc = myArgc;
}
else
{
// first move things down
for (k = myArgc + 1; k > (size_t)position; k--)
{
myArgv[k] = myArgv[k - 1];
}
myArgc++;
myOrigArgc = myArgc;
myArgv[position] = new char[strlen(str) + 1];
strcpy(myArgv[position], str);
myArgv[position][strlen(str)] = '\0';
rebuildFullString();
myFirstAdd = false;
}
}
AREXPORT size_t ArArgumentBuilder::getArgc(void) const
{
return myArgc;
}
AREXPORT char** ArArgumentBuilder::getArgv(void) const
{
return myArgv;
}
AREXPORT const char *ArArgumentBuilder::getFullString(void) const
{
return myFullString.c_str();
}
AREXPORT const char *ArArgumentBuilder::getExtraString(void) const
{
return myExtraString.c_str();
}
AREXPORT void ArArgumentBuilder::setExtraString(const char *str)
{
myExtraString = str;
}
AREXPORT void ArArgumentBuilder::setFullString(const char *str)
{
myFullString = str;
}
AREXPORT const char* ArArgumentBuilder::getArg(size_t whichArg) const
{
if ((whichArg >= 0) && (whichArg < myArgc)) {
return myArgv[whichArg];
}
else {
return NULL;
}
}
AREXPORT void ArArgumentBuilder::log(void) const
{
size_t i;
ArLog::log(ArLog::Terse, "Num arguments: %d", myArgc);
for (i = 0; i < myArgc; ++i)
ArLog::log(ArLog::Terse, "Arg %d: %s", i, myArgv[i]);
}
AREXPORT bool ArArgumentBuilder::isArgBool(size_t whichArg) const
{
if (whichArg > myArgc || getArg(whichArg) == NULL)
return false;
if (strcasecmp(getArg(whichArg), "true") == 0 ||
strcasecmp(getArg(whichArg), "1") == 0 ||
strcasecmp(getArg(whichArg), "false") == 0 ||
strcasecmp(getArg(whichArg), "0") == 0)
return true;
else
return false;
}
AREXPORT bool ArArgumentBuilder::getArgBool(size_t whichArg,
bool *ok) const
{
bool isSuccess = false;
bool ret = false;
const char *str = getArg(whichArg);
// If the arg was successfully obtained...
if (str != NULL) {
if (strcasecmp(str, "true") == 0 ||
strcasecmp(str, "1") == 0) {
isSuccess = true;
ret = true;
}
else if (strcasecmp(str, "false") == 0 ||
strcasecmp(str, "0") == 0) {
isSuccess = true;
ret = false;
}
} // end if valid arg
if (ok != NULL) {
*ok = isSuccess;
}
if (isSuccess) {
return ret;
}
else {
return 0;
}
} // end method getArgBool
AREXPORT bool ArArgumentBuilder::isArgInt(size_t whichArg, bool forceHex) const
{
const char *str;
int ret;
char *endPtr;
if (whichArg > myArgc || getArg(whichArg) == NULL)
return false;
int base = 10;
str = getArg(whichArg);
if (forceHex)
base = 16;
// see if it has the hex prefix and strip it
if (strlen(str) > 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
{
str = &str[2];
base = 16;
}
ret = strtol(str, &endPtr, base);
if (endPtr[0] == '\0' && endPtr != str)
return true;
else
return false;
}
AREXPORT int ArArgumentBuilder::getArgInt(size_t whichArg,
bool *ok, bool forceHex) const
{
bool isSuccess = false;
int ret = 0;
const char *str = getArg(whichArg);
// If the specified arg was successfully obtained...
if (str != NULL) {
int base = 10;
if (forceHex)
base = 16;
// see if it has the hex prefix and strip it
if (strlen(str) > 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
{
str = &str[2];
base = 16;
}
char *endPtr = NULL;
ret = strtol(str, &endPtr, base);
if (endPtr[0] == '\0' && endPtr != str) {
isSuccess = true;
}
} // end if valid arg
if (ok != NULL) {
*ok = isSuccess;
}
if (isSuccess)
return ret;
else
return 0;
} // end method getArgInt
AREXPORT bool ArArgumentBuilder::isArgLongLongInt(size_t whichArg) const
{
const char *str;
long long int ret;
char *endPtr;
if (whichArg > myArgc || getArg(whichArg) == NULL)
return false;
int base = 10;
str = getArg(whichArg);
// see if its a hex number
if (strlen(str) > 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
{
str = &str[2];
base = 16;
}
#ifndef _MSC_VER
ret = strtoll(str, &endPtr, base);
#else
ret = _strtoi64(str, &endPtr, base);
#endif
if (endPtr[0] == '\0' && endPtr != str)
return true;
else
return false;
}
AREXPORT int ArArgumentBuilder::getArgLongLongInt(size_t whichArg,
bool *ok) const
{
bool isSuccess = false;
long long ret = 0;
const char *str = getArg(whichArg);
// If the specified arg was successfully obtained...
if (str != NULL) {
int base = 10;
// see if its a hex number
if (strlen(str) > 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
{
str = &str[2];
base = 16;
}
char *endPtr = NULL;
#ifndef _MSC_VER
ret = strtoll(str, &endPtr, base);
#else
ret = _strtoi64(str, &endPtr, base);
#endif
if (endPtr[0] == '\0' && endPtr != str) {
isSuccess = true;
}
} // end if valid arg
if (ok != NULL) {
*ok = isSuccess;
}
if (isSuccess)
return ret;
else
return 0;
} // end method getArgInt
AREXPORT bool ArArgumentBuilder::isArgDouble(size_t whichArg) const
{
const char *str;
double ret;
char *endPtr;
if (whichArg > myArgc || getArg(whichArg) == NULL)
return false;
str = getArg(whichArg);
if (strcmp(str, "-INF") == 0)
{
return true;
}
else if (strcmp(str, "INF") == 0)
{
return true;
}
else
{
ret = strtod(str, &endPtr);
if (endPtr[0] == '\0' && endPtr != str)
return true;
else
return false;
}
}
AREXPORT double ArArgumentBuilder::getArgDouble(size_t whichArg,
bool *ok) const
{
bool isSuccess = false;
double ret = 0;
const char *str = getArg(whichArg);
// If the specified arg was successfully obtained...
if (str != NULL) {
if (strcmp(str, "-INF") == 0)
{
isSuccess = true;
ret = -HUGE_VAL;
}
else if (strcmp(str, "INF") == 0)
{
isSuccess = true;
ret = HUGE_VAL;
}
else { // check regular values
char *endPtr = NULL;
ret = strtod(str, &endPtr);
if (endPtr[0] == '\0' && endPtr != str) {
isSuccess = true;
}
} // end else check regular values
} // end if valid arg
if (ok != NULL) {
*ok = isSuccess;
}
if (isSuccess)
return ret;
else
return 0;
} // end method getArgDouble
AREXPORT void ArArgumentBuilder::compressQuoted(bool stripQuotationMarks)
{
size_t argLen;
size_t i;
std::string myNewArg;
for (i = 0; i < myArgc; i++)
{
argLen = strlen(myArgv[i]);
if (stripQuotationMarks && argLen >= 2 &&
myArgv[i][0] == '"' && myArgv[i][argLen - 1] == '"')
{
myNewArg = &myArgv[i][1];
myNewArg[myNewArg.size() - 1] = '\0';
delete[] myArgv[i];
// but replacing ourself with the new arg
myArgv[i] = cppstrdup(myNewArg.c_str());
//myArgv[i] = strdup(myNewArg.c_str());
continue;
}
// if this arg begins with a quote but doesn't end with one
if (argLen >= 2 && myArgv[i][0] == '"' && myArgv[i][argLen - 1] != '"')
{
// start the new value for this arg, if stripping quotations
// then start after the quote
if (stripQuotationMarks)
myNewArg = &myArgv[i][1];
else
myNewArg = myArgv[i];
bool isEndQuoteFound = false;
// now while the end char of the next args isn't the end of our
// start quote we toss things into this arg
while ((i + 1 < myArgc) && !isEndQuoteFound) {
int nextArgLen = strlen(myArgv[i+1]);
// Check whether the next arg contains the ending quote...
if ((nextArgLen > 0) &&
(myArgv[i+1][nextArgLen - 1] == '"'))
{
isEndQuoteFound = true;
}
// Concatenate the next arg to this one...
myNewArg += " ";
myNewArg += myArgv[i+1];
// if we are striping quotes off then replace the quote
if (stripQuotationMarks && myNewArg.size() > 0 && isEndQuoteFound)
myNewArg[myNewArg.size() - 1] = '\0';
// removing those next args
removeArg(i+1);
// and ourself
delete[] myArgv[i];
// but replacing ourself with the new arg
myArgv[i] = cppstrdup(myNewArg.c_str());
//myArgv[i] = strdup(myNewArg.c_str());
}
}
}
}
AREXPORT void ArArgumentBuilder::setQuiet(bool isQuiet)
{
myIsQuiet = isQuiet;
}
AREXPORT void ArArgumentBuilder::rebuildFullString()
{
myFullString = "";
for (size_t k = 0; k < myArgc; k++)
{
myFullString += myArgv[k];
// Don't tack an extra space on at the end.
if (k < myArgc - 1) {
myFullString += " ";
}
}
} // end method rebuildFullString
// ----------------------------------------------------------------------------
bool ArArgumentBuilderCompareOp::operator()(ArArgumentBuilder* arg1,
ArArgumentBuilder* arg2) const
{
if (arg1 == NULL) {
return true;
}
else if (arg2 == NULL) {
return false;
}
std::string arg1String = arg1->getFullString();
std::string arg2String = arg2->getFullString();
return (arg1String.compare(arg2String) < 0);
}