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:
Andreas Lauser 2014-09-11 13:05:32 +02:00
parent 6dbe6ae5ac
commit 435e6aa6c5
5 changed files with 147 additions and 88 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
};
}

View File

@ -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 ) {