extend "StarToken" class
that's because not every token which contains a star represents an repetion, e.g. 'PROD*' and '123*456' (with the quotes) are strings...
This commit is contained in:
parent
6dbe6ae5ac
commit
435e6aa6c5
@ -96,16 +96,19 @@ namespace Opm {
|
||||
if (self->sizeType() == ALL) {
|
||||
while (rawRecord->size() > 0) {
|
||||
std::string token = rawRecord->pop_front();
|
||||
if (tokenContainsStar( token )) {
|
||||
StarToken<ValueType> st(token);
|
||||
|
||||
std::string countString;
|
||||
std::string valueString;
|
||||
if (isStarToken(token, countString, valueString)) {
|
||||
StarToken<ValueType> st(token, countString, valueString);
|
||||
ValueType value;
|
||||
|
||||
if (st.hasValue()) {
|
||||
value = st.value();
|
||||
deckItem->push_backMultiple( value , st.multiplier());
|
||||
deckItem->push_backMultiple( value , st.count());
|
||||
} else {
|
||||
value = self->getDefault();
|
||||
for (size_t i=0; i < st.multiplier(); i++)
|
||||
for (size_t i=0; i < st.count(); i++)
|
||||
deckItem->push_backDefault( value );
|
||||
}
|
||||
} else {
|
||||
@ -122,8 +125,10 @@ namespace Opm {
|
||||
// The '*' should be interpreted as a repetition indicator, but it must
|
||||
// be preceeded by an integer...
|
||||
std::string token = rawRecord->pop_front();
|
||||
if (tokenContainsStar( token )) {
|
||||
StarToken<ValueType> st(token);
|
||||
std::string countString;
|
||||
std::string valueString;
|
||||
if (isStarToken(token, countString, valueString)) {
|
||||
StarToken<ValueType> st(token, countString, valueString);
|
||||
|
||||
if (!st.hasValue())
|
||||
deckItem->push_backDefault( self->getDefault() );
|
||||
@ -135,11 +140,11 @@ namespace Opm {
|
||||
// number of defaults pass item boundaries...
|
||||
std::string singleRepetition;
|
||||
if (st.hasValue())
|
||||
singleRepetition = boost::lexical_cast<std::string>(st.value());
|
||||
singleRepetition = st.valueString();
|
||||
else
|
||||
singleRepetition = "1*";
|
||||
|
||||
for (size_t i=0; i < st.multiplier() - 1; i++)
|
||||
for (size_t i=0; i < st.count() - 1; i++)
|
||||
rawRecord->push_front(singleRepetition);
|
||||
} else {
|
||||
ValueType value = readValueToken<ValueType>(token);
|
||||
|
@ -209,6 +209,8 @@ BOOST_AUTO_TEST_CASE(ParseWithDefault_defaultAppliedCorrectInDeck) {
|
||||
parserRecord.addItem(itemString);
|
||||
parserRecord.addItem(itemDouble);
|
||||
|
||||
// according to the RM, this is invalid ("an asterisk by itself is not sufficient"),
|
||||
// but it seems to appear in the wild. Thus, we interpret this as "1*"...
|
||||
{
|
||||
RawRecordPtr rawRecord(new RawRecord("* /"));
|
||||
DeckItemConstPtr deckStringItem = itemString->scan(rawRecord);
|
||||
@ -224,7 +226,6 @@ BOOST_AUTO_TEST_CASE(ParseWithDefault_defaultAppliedCorrectInDeck) {
|
||||
BOOST_CHECK(deckDoubleItem->defaultApplied(0));
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
RawRecordPtr rawRecord(new RawRecord("/"));
|
||||
DeckItemConstPtr deckStringItem = itemString->scan(rawRecord);
|
||||
@ -259,7 +260,7 @@ BOOST_AUTO_TEST_CASE(ParseWithDefault_defaultAppliedCorrectInDeck) {
|
||||
BOOST_CHECK(!deckDoubleItem->defaultApplied(0));
|
||||
}
|
||||
|
||||
|
||||
// again this is invalid according to the RM, but it is used anyway in the wild...
|
||||
{
|
||||
RawRecordPtr rawRecord(new RawRecord("* * * /"));
|
||||
DeckItemConstPtr deckStringItem = itemString->scan(rawRecord);
|
||||
|
@ -18,7 +18,6 @@
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <opm/parser/eclipse/RawDeck/StarToken.hpp>
|
||||
@ -27,19 +26,47 @@
|
||||
|
||||
namespace Opm {
|
||||
|
||||
bool tokenContainsStar(const std::string& token) {
|
||||
size_t pos = token.find('*');
|
||||
if (pos == std::string::npos)
|
||||
bool isStarToken(const std::string& token,
|
||||
std::string& countString,
|
||||
std::string& valueString) {
|
||||
// find first character which is not a digit
|
||||
size_t pos = 0;
|
||||
for (; pos < token.length(); ++pos)
|
||||
if (!std::isdigit(token[pos]))
|
||||
break;
|
||||
|
||||
// if no such character exists or if this character is not a star, the token is
|
||||
// not a "star token" (i.e. it is not a "repeat this value N times" token.
|
||||
if (pos >= token.size() || token[pos] != '*')
|
||||
return false;
|
||||
else {
|
||||
if (pos == 0) return true;
|
||||
try {
|
||||
boost::lexical_cast<int>(token.substr(0, pos));
|
||||
return true;
|
||||
}
|
||||
catch (boost::bad_lexical_cast&) {
|
||||
return false;
|
||||
}
|
||||
// Quote from the Eclipse Reference Manual: "An asterisk by
|
||||
// itself is not sufficent". However, our experience is that
|
||||
// Eclipse accepts such tokens and we therefore interpret "*"
|
||||
// as "1*".
|
||||
//
|
||||
// Tokens like "*12" are recognized as a star token
|
||||
// here, but we will throw in the code which uses
|
||||
// StarToken<T>. (Because Eclipse does not seem to
|
||||
// accept these and we would stay as closely to the spec as
|
||||
// possible.)
|
||||
else if (pos == 0) {
|
||||
countString = "";
|
||||
valueString = token.substr(pos + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// if a star is prefixed by an unsigned integer N, then this should be
|
||||
// interpreted as "repeat value after star N times"
|
||||
try {
|
||||
boost::lexical_cast<int>(token.substr(0, pos));
|
||||
}
|
||||
catch (...) {
|
||||
// the lexical cast may fail as the number of digits may be too large...
|
||||
return false;
|
||||
}
|
||||
|
||||
countString = token.substr(0, pos);
|
||||
valueString = token.substr(pos + 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -22,16 +22,13 @@
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <cstdlib> // strtol, strtod
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#define STAR '*'
|
||||
#define C_EOS '\0'
|
||||
|
||||
namespace Opm {
|
||||
|
||||
bool tokenContainsStar(const std::string& token);
|
||||
bool isStarToken(const std::string& token,
|
||||
std::string& countString,
|
||||
std::string& valueString);
|
||||
|
||||
template <class T>
|
||||
T readValueToken(const std::string& valueToken ) {
|
||||
@ -46,60 +43,83 @@ namespace Opm {
|
||||
|
||||
template <class T>
|
||||
class StarToken {
|
||||
public:
|
||||
StarToken(const std::string& token)
|
||||
{
|
||||
if (!isStarToken(token, m_countString, m_valueString))
|
||||
throw std::invalid_argument("Token \""+token+"\" is not a repetition specifier");
|
||||
init_(token);
|
||||
}
|
||||
|
||||
public:
|
||||
StarToken(const std::string& token) : m_value(T()), m_hasValue(false) {
|
||||
size_t star_pos = token.find( STAR );
|
||||
|
||||
if (star_pos != std::string::npos) {
|
||||
|
||||
if (token[0] == STAR)
|
||||
m_multiplier = 1;
|
||||
else {
|
||||
char * error_ptr;
|
||||
m_multiplier = strtol( token.c_str() , &error_ptr , 10);
|
||||
StarToken(const std::string& token, const std::string& countStr, const std::string& valueStr)
|
||||
: m_countString(countStr)
|
||||
, m_valueString(valueStr)
|
||||
{
|
||||
init_(token);
|
||||
}
|
||||
|
||||
if (m_multiplier <= 0 || error_ptr[0] != STAR)
|
||||
throw std::invalid_argument("Parsing multiplier from " + token + " failed");
|
||||
}
|
||||
{
|
||||
const std::string& value_token = token.substr( star_pos + 1);
|
||||
if (value_token.size()) {
|
||||
m_value = readValueToken<T>( value_token );
|
||||
m_hasValue = true;
|
||||
if (star_pos == 0)
|
||||
throw std::invalid_argument("Failed to extract multiplier from token:" + token);
|
||||
} else {
|
||||
m_hasValue = false;
|
||||
}
|
||||
}
|
||||
} else
|
||||
throw std::invalid_argument("The input token: \'" + token + "\' does not contain a \'*\'.");
|
||||
size_t count() const {
|
||||
return m_count;
|
||||
}
|
||||
|
||||
T value() const {
|
||||
if (!hasValue())
|
||||
throw std::invalid_argument("The input token did not specify a value ");
|
||||
return m_value;
|
||||
}
|
||||
|
||||
bool hasValue() const {
|
||||
return !m_valueString.empty();
|
||||
}
|
||||
|
||||
// returns the coubt as rendered in the deck. note that this might be different
|
||||
// than just converting the return value of count() to a string because an empty
|
||||
// count is interpreted as 1...
|
||||
const std::string& countString() const {
|
||||
return m_countString;
|
||||
}
|
||||
|
||||
// returns the value as rendered in the deck. note that this might be different
|
||||
// than just converting the return value of value() to a string because values
|
||||
// might have different representations in the deck (e.g. strings can be
|
||||
// specified with and without quotes and but spaces are only allowed using the
|
||||
// first representation.)
|
||||
const std::string& valueString() const {
|
||||
return m_valueString;
|
||||
}
|
||||
|
||||
private:
|
||||
// internal initialization method. the m_countString and m_valueString attributes
|
||||
// must be set before calling this method.
|
||||
void init_(const std::string& token) {
|
||||
// special-case the interpretation of a lone star as "1*" but do not
|
||||
// allow constructs like "*123"...
|
||||
if (m_countString == "") {
|
||||
if (m_valueString != "")
|
||||
// TODO: decorate the deck with a warning instead?
|
||||
throw std::invalid_argument("Not specifying a count also implies not specifying a value. Token: \'" + token + "\'.");
|
||||
|
||||
// TODO: since this is explicitly forbidden by the documentation it might
|
||||
// be a good idea to decorate the deck with a warning?
|
||||
m_count = 1;
|
||||
}
|
||||
else {
|
||||
m_count = boost::lexical_cast<int>(m_countString);
|
||||
|
||||
if (m_count == 0)
|
||||
// TODO: decorate the deck with a warning instead?
|
||||
throw std::invalid_argument("Specifing zero repetitions is not allowed. Token: \'" + token + "\'.");
|
||||
}
|
||||
|
||||
size_t multiplier() const {
|
||||
return m_multiplier;
|
||||
}
|
||||
if (!m_valueString.empty())
|
||||
m_value = readValueToken<T>( m_valueString );
|
||||
}
|
||||
|
||||
|
||||
T value() const {
|
||||
if (!m_hasValue)
|
||||
throw std::invalid_argument("The input token did not specify a value ");
|
||||
return m_value;
|
||||
}
|
||||
|
||||
|
||||
bool hasValue() const {
|
||||
return m_hasValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
ssize_t m_multiplier;
|
||||
T m_value;
|
||||
bool m_hasValue;
|
||||
};
|
||||
ssize_t m_count;
|
||||
T m_value;
|
||||
std::string m_countString;
|
||||
std::string m_valueString;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
@ -28,21 +28,25 @@ BOOST_AUTO_TEST_CASE(NoStarThrows) {
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(InvalidMultiplierThrow) {
|
||||
BOOST_AUTO_TEST_CASE(InvalidCountThrow) {
|
||||
BOOST_REQUIRE_THROW( Opm::StarToken<int> st("X*") , std::invalid_argument);
|
||||
BOOST_REQUIRE_THROW( Opm::StarToken<int> st("1.25*") , std::invalid_argument);
|
||||
BOOST_REQUIRE_THROW( Opm::StarToken<int> st("-3*") , std::invalid_argument);
|
||||
BOOST_REQUIRE_THROW( Opm::StarToken<int> st("0*") , std::invalid_argument);
|
||||
BOOST_REQUIRE_THROW( Opm::StarToken<int> st("*123") , std::invalid_argument);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(MultiplierCOrrect) {
|
||||
BOOST_AUTO_TEST_CASE(CountCorrect) {
|
||||
Opm::StarToken<int> st1("*");
|
||||
Opm::StarToken<int> st2("5*");
|
||||
Opm::StarToken<int> st3("54*");
|
||||
BOOST_REQUIRE_EQUAL(1U , st1.multiplier());
|
||||
BOOST_REQUIRE_EQUAL(5U , st2.multiplier());
|
||||
BOOST_REQUIRE_EQUAL(54U , st3.multiplier());
|
||||
BOOST_CHECK(st1.count() == 1);
|
||||
BOOST_CHECK(st1.countString() == "");
|
||||
BOOST_CHECK(st1.valueString() == "");
|
||||
BOOST_CHECK(!st1.hasValue());
|
||||
BOOST_REQUIRE_EQUAL(5U , st2.count());
|
||||
BOOST_REQUIRE_EQUAL(54U , st3.count());
|
||||
}
|
||||
|
||||
|
||||
@ -63,7 +67,7 @@ BOOST_AUTO_TEST_CASE(IntMalformedValueThrows) {
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(StarNoMultiplierThrows) {
|
||||
BOOST_AUTO_TEST_CASE(StarNoCountThrows) {
|
||||
BOOST_CHECK_THROW( Opm::StarToken<int> st1("*10") , std::invalid_argument);
|
||||
BOOST_CHECK_THROW( Opm::StarToken<double> st1("*1.0") , std::invalid_argument);
|
||||
BOOST_CHECK_THROW( Opm::StarToken<std::string> st1("*String") , std::invalid_argument);
|
||||
@ -114,11 +118,13 @@ BOOST_AUTO_TEST_CASE(CorrectStringValue) {
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( ContainsStar_WithStar_ReturnsTrue ) {
|
||||
BOOST_CHECK_EQUAL( true , Opm::tokenContainsStar("*") );
|
||||
BOOST_CHECK_EQUAL( true , Opm::tokenContainsStar("1*") );
|
||||
BOOST_CHECK_EQUAL( true , Opm::tokenContainsStar("1*2") );
|
||||
std::string countString, valueString;
|
||||
BOOST_CHECK_EQUAL( true , Opm::isStarToken("*", countString, valueString) );
|
||||
BOOST_CHECK_EQUAL( true , Opm::isStarToken("*1", countString, valueString) );
|
||||
BOOST_CHECK_EQUAL( true , Opm::isStarToken("1*", countString, valueString) );
|
||||
BOOST_CHECK_EQUAL( true , Opm::isStarToken("1*2", countString, valueString) );
|
||||
|
||||
BOOST_CHECK_EQUAL( false , Opm::tokenContainsStar("12") );
|
||||
BOOST_CHECK_EQUAL( false , Opm::isStarToken("12", countString, valueString) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( readValueToken_basic_validity_tests ) {
|
||||
|
Loading…
Reference in New Issue
Block a user