#include "IpoptConfig.h"
#include "IpOptionsList.hpp"
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
namespace Ipopt
{
bool OptionsList::SetStringValue(
const std::string& tag,
const std::string& value,
bool allow_clobber,
bool dont_print
)
{
if( IsValid(reg_options_) )
{
SmartPtr<const RegisteredOption> option = reg_options_->GetOption(tag);
if( IsNull(option) )
{
if( IsValid(jnlst_) )
{
jnlst_->Printf(J_ERROR, J_MAIN,
"Tried to set Option: %s. It is not a valid option. Please check the list of available options.\n", tag.c_str());
}
return false;
}
if( option->Type() != OT_String )
{
if( IsValid(jnlst_) )
{
std::string msg = "Tried to set Option: " + tag;
msg += ". It is a valid option, but it is of type ";
if( option->Type() == OT_Number )
{
msg += " Number";
}
else if( option->Type() == OT_Integer )
{
msg += " Integer";
}
else
{
msg += " Unknown";
}
msg += ", not of type String. Please check the documentation for options.\n";
jnlst_->Printf(J_ERROR, J_MAIN, "%s", msg.c_str());
option->OutputDescription(*jnlst_);
}
return false;
}
if( !option->IsValidStringSetting(value) )
{
if( IsValid(jnlst_) )
{
jnlst_->Printf(J_ERROR, J_MAIN,
"Setting: \"%s\" is not a valid setting for Option: %s. Check the option documentation.\n",
value.c_str(), tag.c_str());
option->OutputDescription(*jnlst_);
}
return false;
}
}
if( !will_allow_clobber(tag) )
{
if( IsValid(jnlst_) )
{
std::string msg = "WARNING: Tried to set option \"" + tag;
msg += "\" to a value of \"" + value;
msg += "\",\n but the previous value is set to disallow clobbering.\n";
msg += " The setting will remain as: \"" + tag;
msg += " " + options_[lowercase(tag)].GetValue();
msg += "\"\n";
jnlst_->Printf(J_WARNING, J_MAIN, "%s", msg.c_str());
}
}
else
{
OptionsList::OptionValue optval(value, allow_clobber, dont_print);
options_[lowercase(tag)] = optval;
}
return true;
}
bool OptionsList::SetNumericValue(
const std::string& tag,
Number value,
bool allow_clobber,
bool dont_print
)
{
char buffer[256];
Snprintf(buffer, 255, "%g", value);
if( IsValid(reg_options_) )
{
SmartPtr<const RegisteredOption> option = reg_options_->GetOption(tag);
if( IsNull(option) )
{
if( IsValid(jnlst_) )
{
jnlst_->Printf(J_ERROR, J_MAIN,
"Tried to set Option: %s. It is not a valid option. Please check the list of available options.\n", tag.c_str());
}
return false;
}
if( option->Type() != OT_Number )
{
if( IsValid(jnlst_) )
{
std::string msg = "Tried to set Option: " + tag;
msg += ". It is a valid option, but it is of type ";
if( option->Type() == OT_String )
{
msg += " String";
}
else if( option->Type() == OT_Integer )
{
msg += " Integer";
}
else
{
msg += " Unknown";
}
msg += ", not of type Number. Please check the documentation for options.\n";
jnlst_->Printf(J_ERROR, J_MAIN, "%s", msg.c_str());
option->OutputDescription(*jnlst_);
}
return false;
}
if( !option->IsValidNumberSetting(value) )
{
if( IsValid(jnlst_) )
{
jnlst_->Printf(J_ERROR, J_MAIN,
"Setting: \"%s\" is not a valid setting for Option: %s. Check the option documentation.\n",
buffer, tag.c_str());
option->OutputDescription(*jnlst_);
}
return false;
}
}
if( !will_allow_clobber(tag) )
{
if( IsValid(jnlst_) )
{
std::string msg = "WARNING: Tried to set option \"" + tag;
msg += "\" to a value of \"";
msg += buffer;
msg += "\",\n but the previous value is set to disallow clobbering.\n";
msg += " The setting will remain as: \"" + tag;
msg += " " + options_[lowercase(tag)].GetValue();
msg += "\"\n";
jnlst_->Printf(J_WARNING, J_MAIN, "%s", msg.c_str());
}
}
else
{
OptionsList::OptionValue optval(buffer, allow_clobber, dont_print);
options_[lowercase(tag)] = optval;
}
return true;
}
bool OptionsList::SetIntegerValue(
const std::string& tag,
Index value,
bool allow_clobber,
bool dont_print
)
{
char buffer[256];
Snprintf(buffer, 255, "%" IPOPT_INDEX_FORMAT, value);
if( IsValid(reg_options_) )
{
SmartPtr<const RegisteredOption> option = reg_options_->GetOption(tag);
if( IsNull(option) )
{
if( IsValid(jnlst_) )
{
jnlst_->Printf(J_ERROR, J_MAIN,
"Tried to set Option: %s. It is not a valid option. Please check the list of available options.\n", tag.c_str());
}
return false;
}
if( option->Type() != OT_Integer )
{
if( IsValid(jnlst_) )
{
std::string msg = "Tried to set Option: " + tag;
msg += ". It is a valid option, but it is of type ";
if( option->Type() == OT_String )
{
msg += " String";
}
else if( option->Type() == OT_Number )
{
msg += " Number";
}
else
{
msg += " Unknown";
}
msg += ", not of type Integer. Please check the documentation for options.\n";
jnlst_->Printf(J_ERROR, J_MAIN, "%s", msg.c_str());
option->OutputDescription(*jnlst_);
}
return false;
}
if( !option->IsValidIntegerSetting(value) )
{
if( IsValid(jnlst_) )
{
jnlst_->Printf(J_ERROR, J_MAIN,
"Setting: \"%s\" is not a valid setting for Option: %s. Check the option documentation.\n",
buffer, tag.c_str());
option->OutputDescription(*jnlst_);
}
return false;
}
}
if( !will_allow_clobber(tag) )
{
if( IsValid(jnlst_) )
{
std::string msg = "WARNING: Tried to set option \"" + tag;
msg += "\" to a value of \"";
msg += buffer;
msg += "\",\n but the previous value is set to disallow clobbering.\n";
msg += " The setting will remain as: \"" + tag;
msg += " " + options_[lowercase(tag)].GetValue();
msg += "\"\n";
jnlst_->Printf(J_WARNING, J_MAIN, "%s", msg.c_str());
}
}
else
{
OptionsList::OptionValue optval(buffer, allow_clobber, dont_print);
options_[lowercase(tag)] = optval;
}
return true;
}
bool OptionsList::UnsetValue(
const std::string& tag
)
{
if( !will_allow_clobber(tag) )
{
if( IsValid(jnlst_) )
{
std::string msg = "WARNING: Tried to unset option \"" + tag;
msg += "\",\n but the current value is set to disallow clobbering.\n";
msg += " The setting will remain as: \"" + tag;
msg += " " + options_[lowercase(tag)].GetValue();
msg += "\"\n";
jnlst_->Printf(J_WARNING, J_MAIN, "%s", msg.c_str());
}
return false;
}
return options_.erase(lowercase(tag)) > 0;
}
bool OptionsList::SetStringValueIfUnset(
const std::string& tag,
const std::string& value,
bool allow_clobber,
bool dont_print
)
{
std::string val;
bool found = GetStringValue(tag, val, "");
if( !found )
{
return SetStringValue(tag, value, allow_clobber, dont_print);
}
return true;
}
bool OptionsList::SetNumericValueIfUnset(
const std::string& tag,
Number value,
bool allow_clobber,
bool dont_print
)
{
Number val;
bool found = GetNumericValue(tag, val, "");
if( !found )
{
return SetNumericValue(tag, value, allow_clobber, dont_print);
}
return true;
}
bool OptionsList::SetIntegerValueIfUnset(
const std::string& tag,
Index value,
bool allow_clobber,
bool dont_print
)
{
Index val;
bool found = GetIntegerValue(tag, val, "");
if( !found )
{
return SetIntegerValue(tag, value, allow_clobber, dont_print);
}
return true;
}
bool OptionsList::GetStringValue(
const std::string& tag,
std::string& value,
const std::string& prefix
) const
{
SmartPtr<const RegisteredOption> option = NULL;
bool found = find_tag(tag, prefix, value);
if( IsValid(reg_options_) )
{
option = reg_options_->GetOption(tag);
if( IsNull(option) )
{
std::string msg = "IPOPT tried to get the value of Option: " + tag;
msg += ". It is not a valid registered option.";
THROW_EXCEPTION(OPTION_INVALID, msg);
}
if( option->Type() != OT_String )
{
std::string msg = "IPOPT tried to get the value of Option: " + tag;
msg += ". It is a valid option, but it is of type ";
if( option->Type() == OT_Integer )
{
msg += " Integer";
}
else if( option->Type() == OT_Number )
{
msg += " Number";
}
else
{
msg += " Unknown";
}
msg += ", not of type String. Please check the documentation for options.";
if( IsValid(jnlst_) )
{
option->OutputDescription(*jnlst_);
}
THROW_EXCEPTION(OPTION_INVALID, msg);
}
if( found )
{
value = option->MapStringSetting(value);
}
else
{
value = option->DefaultString();
}
}
return found;
}
bool OptionsList::GetEnumValue(
const std::string& tag,
Index& value,
const std::string& prefix
) const
{
std::string str;
SmartPtr<const RegisteredOption> option = NULL;
bool found = find_tag(tag, prefix, str);
if( IsValid(reg_options_) )
{
option = reg_options_->GetOption(tag);
if( IsNull(option) )
{
std::string msg = "IPOPT tried to get the value of Option: " + tag;
msg += ". It is not a valid registered option.";
THROW_EXCEPTION(OPTION_INVALID, msg);
}
if( option->Type() != OT_String )
{
std::string msg = "IPOPT tried to get the value of Option: " + tag;
msg += ". It is a valid option, but it is of type ";
if( option->Type() == OT_Integer )
{
msg += " Integer";
}
else if( option->Type() == OT_Number )
{
msg += " Number";
}
else
{
msg += " Unknown";
}
msg += ", not of type String. Please check the documentation for options.";
if( IsValid(jnlst_) )
{
option->OutputDescription(*jnlst_);
}
THROW_EXCEPTION(OPTION_INVALID, msg);
}
if( found )
{
value = option->MapStringSettingToEnum(str);
}
else
{
value = option->DefaultStringAsEnum();
}
}
return found;
}
bool OptionsList::GetBoolValue(
const std::string& tag,
bool& value,
const std::string& prefix
) const
{
std::string str;
bool ret = GetStringValue(tag, str, prefix);
if( str == "no" || str == "false" || str == "off" )
{
value = false;
}
else if( str == "yes" || str == "true" || str == "on" )
{
value = true;
}
else
{
THROW_EXCEPTION(OPTION_INVALID, "Tried to get a boolean from an option and failed.");
}
return ret;
}
bool OptionsList::GetNumericValue(
const std::string& tag,
Number& value,
const std::string& prefix
) const
{
SmartPtr<const RegisteredOption> option = NULL;
if( IsValid(reg_options_) )
{
option = reg_options_->GetOption(tag);
if( IsNull(option) )
{
std::string msg = "IPOPT tried to get the value of Option: " + tag;
msg += ". It is not a valid registered option.";
THROW_EXCEPTION(OPTION_INVALID, msg);
}
if( option->Type() != OT_Number )
{
std::string msg = "IPOPT tried to get the value of Option: " + tag;
msg += ". It is a valid option, but it is of type ";
if( option->Type() == OT_Integer )
{
msg += " Integer";
}
else if( option->Type() == OT_String )
{
msg += " String";
}
else
{
msg += " Unknown";
}
msg += ", not of type Number. Please check the documentation for options.";
if( IsValid(jnlst_) )
{
option->OutputDescription(*jnlst_);
}
THROW_EXCEPTION(OPTION_INVALID, msg);
}
}
std::string strvalue;
if( find_tag(tag, prefix, strvalue) )
{
char* buffer = new char[strvalue.length() + 1];
strcpy(buffer, strvalue.c_str());
for( size_t i = 0; i < strvalue.length(); ++i )
{
if( buffer[i] == 'd' || buffer[i] == 'D' )
{
buffer[i] = 'e';
}
}
char* p_end;
Number retval = strtod(buffer, &p_end);
if( *p_end != '\0' && !isspace(*p_end) )
{
delete[] buffer;
std::string msg = "Option \"" + tag + "\": Double value expected, but non-numeric value \"" + strvalue
+ "\" found.\n";
THROW_EXCEPTION(OPTION_INVALID, msg);
}
delete[] buffer;
value = retval;
return true;
}
else if( IsValid(option) )
{
value = option->DefaultNumber();
return false;
}
return false;
}
bool OptionsList::GetIntegerValue(
const std::string& tag,
Index& value,
const std::string& prefix
) const
{
SmartPtr<const RegisteredOption> option = NULL;
if( IsValid(reg_options_) )
{
option = reg_options_->GetOption(tag);
if( IsNull(option) )
{
std::string msg = "IPOPT tried to get the value of Option: " + tag;
msg += ". It is not a valid registered option.";
THROW_EXCEPTION(OPTION_INVALID, msg);
}
if( option->Type() != OT_Integer )
{
std::string msg = "IPOPT tried to get the value of Option: " + tag;
msg += ". It is a valid option, but it is of type ";
if( option->Type() == OT_Number )
{
msg += " Number";
}
else if( option->Type() == OT_String )
{
msg += " String";
}
else
{
msg += " Unknown";
}
msg += ", not of type Integer. Please check the documentation for options.";
if( IsValid(jnlst_) )
{
option->OutputDescription(*jnlst_);
}
THROW_EXCEPTION(OPTION_INVALID, msg);
}
}
std::string strvalue;
if( find_tag(tag, prefix, strvalue) )
{
char* p_end;
size_t retval = strtol(strvalue.c_str(), &p_end, 10);
if( *p_end != '\0' && !isspace(*p_end) )
{
std::string msg = "Option \"" + tag + "\": Integer value expected, but non-integer value \"" + strvalue
+ "\" found.\n";
THROW_EXCEPTION(OPTION_INVALID, msg);
}
value = static_cast<Index>(retval);
return true;
}
else if( IsValid(option) )
{
value = option->DefaultInteger();
return false;
}
return false;
}
const std::string& OptionsList::lowercase(
const std::string& tag
) const
{
lowercase_buffer_ = tag;
for( Index i = 0; i < (Index) tag.length(); i++ )
{
lowercase_buffer_[i] = (char) tolower(tag[i]);
}
return lowercase_buffer_;
}
void OptionsList::PrintList(
std::string& list
) const
{
list.erase();
char buffer[256];
Snprintf(buffer, 255, "%40s %-20s %s\n", "Name", "Value", "# times used");
list += buffer;
for( std::map<std::string, OptionValue>::const_iterator p = options_.begin(); p != options_.end(); ++p )
{
Snprintf(buffer, 255, "%40s = %-20s %6" IPOPT_INDEX_FORMAT "\n", p->first.c_str(), p->second.Value().c_str(), p->second.Counter());
list += buffer;
}
}
void OptionsList::PrintUserOptions(
std::string& list
) const
{
list.erase();
char buffer[256];
Snprintf(buffer, 255, "%40s %-20s %s\n", "Name", "Value", "used");
list += buffer;
for( std::map<std::string, OptionValue>::const_iterator p = options_.begin(); p != options_.end(); ++p )
{
if( !p->second.DontPrint() )
{
const char yes[] = "yes";
const char no[] = "no";
const char* used;
if( p->second.Counter() > 0 )
{
used = yes;
}
else
{
used = no;
}
Snprintf(buffer, 255, "%40s = %-20s %4s\n", p->first.c_str(), p->second.Value().c_str(), used);
list += buffer;
}
}
}
bool OptionsList::ReadFromStream(
const Journalist& jnlst,
std::istream& is,
bool allow_clobber
)
{
jnlst.Printf(J_DETAILED, J_MAIN,
"Start reading options from stream.\n");
while( true )
{
std::string tag;
std::string value;
if( !readnexttoken(is, tag) )
{
jnlst.Printf(J_DETAILED, J_MAIN,
"Finished reading options from file.\n");
return true;
}
if( !readnexttoken(is, value) )
{
jnlst.Printf(J_ERROR, J_MAIN,
"Error reading value for tag %s from file.\n", tag.c_str());
return false;
}
jnlst.Printf(J_DETAILED, J_MAIN,
"Adding option \"%s\" with value \"%s\" to OptionsList.\n", tag.c_str(), value.c_str());
if( IsValid(reg_options_) )
{
SmartPtr<const RegisteredOption> option = reg_options_->GetOption(tag);
if( IsNull(option) )
{
std::string msg = "Read Option: \"";
msg += tag;
msg += "\". It is not a valid option. Check the list of available options.";
THROW_EXCEPTION(OPTION_INVALID, msg);
}
if( option->Type() == OT_String )
{
bool result = SetStringValue(tag, value, allow_clobber);
ASSERT_EXCEPTION(result, OPTION_INVALID,
"Error setting string value read from option file.");
}
else if( option->Type() == OT_Number )
{
char* buffer = new char[value.length() + 1];
strcpy(buffer, value.c_str());
for( size_t i = 0; i < value.length(); ++i )
{
if( buffer[i] == 'd' || buffer[i] == 'D' )
{
buffer[i] = 'e';
}
}
char* p_end;
Number retval = strtod(buffer, &p_end);
if( *p_end != '\0' && !isspace(*p_end) )
{
delete[] buffer;
std::string msg = "Option \"" + tag + "\": Double value expected, but non-numeric option value \""
+ value + "\" found.\n";
THROW_EXCEPTION(OPTION_INVALID, msg);
}
delete [] buffer;
bool result = SetNumericValue(tag, retval, allow_clobber);
ASSERT_EXCEPTION(result, OPTION_INVALID,
"Error setting numeric value read from file.");
}
else if( option->Type() == OT_Integer )
{
char* p_end;
size_t retval = strtol(value.c_str(), &p_end, 10);
if( *p_end != '\0' && !isspace(*p_end) )
{
std::string msg = "Option \"" + tag + "\": Integer value expected, but non-integer option value \""
+ value + "\" found.\n";
if( IsValid(jnlst_) )
{
option->OutputDescription(*jnlst_);
}
THROW_EXCEPTION(OPTION_INVALID, msg);
}
bool result = SetIntegerValue(tag, static_cast<Index>(retval), allow_clobber);
ASSERT_EXCEPTION(result, OPTION_INVALID,
"Error setting integer value read from option file.");
}
else
{
DBG_ASSERT(false && "Option Type: Unknown");
}
}
else
{
bool result = SetStringValue(tag, value, allow_clobber);
ASSERT_EXCEPTION(result, OPTION_INVALID,
"Error setting value read from option file.");
}
}
}
bool OptionsList::find_tag(
const std::string& tag,
const std::string& prefix,
std::string& value
) const
{
bool found = false;
std::map<std::string, OptionValue>::const_iterator p;
if( prefix != "" )
{
p = options_.find(lowercase(prefix + tag));
if( p != options_.end() )
{
found = true;
}
}
if( !found )
{
p = options_.find(lowercase(tag));
if( p != options_.end() )
{
found = true;
}
}
if( found )
{
value = p->second.GetValue();
}
return found;
}
bool OptionsList::will_allow_clobber(
const std::string& tag
) const
{
bool allow_clobber = true;
std::map<std::string, OptionValue>::const_iterator p;
p = options_.find(lowercase(tag));
if( p != options_.end() )
{
allow_clobber = p->second.AllowClobber();
}
return allow_clobber;
}
bool OptionsList::readnexttoken(
std::istream& is,
std::string& token
)
{
token.erase();
char c = is.get();
while( !is.eof() && (isspace(c) || c == '#') )
{
if( c == '#' )
{
is.ignore(10000000, '\n');
}
c = is.get();
}
bool inside_quotes = (c == '"');
if( inside_quotes )
{
if( is.eof() )
{
return false; }
c = is.get();
}
if( is.eof() )
{
return false;
}
while( !is.eof() && (inside_quotes || !isspace(c)) )
{
token += (char) c;
c = is.get();
if( inside_quotes && (c == '"') )
{
inside_quotes = false;
if( !is.eof() )
{
c = is.get();
}
}
}
return !inside_quotes;
}
}