/* Copyright 2013 Statoil ASA. This file is part of the Open Porous Media project (OPM). OPM 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 3 of the License, or (at your option) any later version. OPM 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 OPM. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Opm { void ParserKeyword::setSizeType( ParserKeywordSizeEnum sizeType ) { m_keywordSizeType = sizeType; } void ParserKeyword::setFixedSize( size_t keywordSize) { m_keywordSizeType = FIXED; m_fixedSize = keywordSize; } void ParserKeyword::setTableCollection(bool _isTableCollection) { m_isTableCollection = _isTableCollection; } void ParserKeyword::commonInit(const std::string& name, ParserKeywordSizeEnum sizeType) { m_isTableCollection = false; m_name = name; m_keywordSizeType = sizeType; m_Description = ""; m_fixedSize = 0; m_deckNames.insert(m_name); } ParserKeyword::ParserKeyword(const std::string& name) { commonInit(name, FIXED); } ParserKeyword::ParserKeyword(const std::string& name, const std::string& sizeKeyword, const std::string& sizeItem, bool _isTableCollection) { commonInit( name , OTHER_KEYWORD_IN_DECK); m_isTableCollection = _isTableCollection; initSizeKeyword(sizeKeyword, sizeItem); } void ParserKeyword::clearDeckNames() { m_deckNames.clear(); } void ParserKeyword::addDeckName( const std::string& deckName ) { m_deckNames.insert(deckName); } bool ParserKeyword::hasDimension() const { if (m_records.size() > 0) { bool hasDim = false; for (auto& record : m_records) { if (record->hasDimension()) hasDim = true; } return hasDim; } else return false; } bool ParserKeyword::isTableCollection() const { return m_isTableCollection; } std::string ParserKeyword::getDescription() const { return m_Description; } void ParserKeyword::setDescription(const std::string& description) { m_Description = description; } void ParserKeyword::initSize(const Json::JsonObject& jsonConfig) { // The number of record has been set explicitly with the size: keyword if (jsonConfig.has_item("size")) { Json::JsonObject sizeObject = jsonConfig.get_item("size"); if (sizeObject.is_number()) { m_fixedSize = (size_t) sizeObject.as_int(); m_keywordSizeType = FIXED; } else initSizeKeyword(sizeObject); } else { if (jsonConfig.has_item("num_tables")) { Json::JsonObject numTablesObject = jsonConfig.get_item("num_tables"); if (!numTablesObject.is_object()) throw std::invalid_argument("The num_tables key must point to a {} object"); initSizeKeyword(numTablesObject); m_isTableCollection = true; } else { if (jsonConfig.has_item("items") || jsonConfig.has_item("records")) // The number of records is undetermined - the keyword will be '/' terminated. m_keywordSizeType = SLASH_TERMINATED; else { m_keywordSizeType = FIXED; if (jsonConfig.has_item("data")) m_fixedSize = 1; else m_fixedSize = 0; } } } } ParserKeyword::ParserKeyword(const Json::JsonObject& jsonConfig) { if (jsonConfig.has_item("name")) { ParserKeywordSizeEnum sizeType = UNKNOWN; commonInit(jsonConfig.get_string("name"), sizeType); } else throw std::invalid_argument("Json object is missing the 'name' property"); if (jsonConfig.has_item("deck_names") || jsonConfig.has_item("deck_name_regex") ) // if either the deck names or the regular expression for deck names are // explicitly specified, we do not implicitly add the contents of the 'name' // item to the deck names... clearDeckNames(); initSize(jsonConfig); initDeckNames(jsonConfig); initSectionNames(jsonConfig); initMatchRegex(jsonConfig); if (jsonConfig.has_item("items") && jsonConfig.has_item("records")) throw std::invalid_argument("Fatal error in " + getName() + " configuration. Can NOT have both records: and items:"); if (jsonConfig.has_item("items")) { const Json::JsonObject itemsConfig = jsonConfig.get_item("items"); addItems(itemsConfig); } if (jsonConfig.has_item("records")) { const Json::JsonObject recordsConfig = jsonConfig.get_item("records"); if (recordsConfig.is_array()) { size_t num_records = recordsConfig.size(); for (size_t i = 0; i < num_records; i++) { const Json::JsonObject itemsConfig = recordsConfig.get_array_item(i); addItems(itemsConfig); } } else throw std::invalid_argument("The records item must point to an array item"); } if (jsonConfig.has_item("data")) initData(jsonConfig); if (jsonConfig.has_item("description")) { m_Description = jsonConfig.get_string("description"); } } void ParserKeyword::initSizeKeyword(const std::string& sizeKeyword, const std::string& sizeItem) { m_sizeDefinitionPair = std::pair(sizeKeyword, sizeItem); m_keywordSizeType = OTHER_KEYWORD_IN_DECK; } void ParserKeyword::initSizeKeyword(const Json::JsonObject& sizeObject) { if (sizeObject.is_object()) { std::string sizeKeyword = sizeObject.get_string("keyword"); std::string sizeItem = sizeObject.get_string("item"); initSizeKeyword(sizeKeyword, sizeItem); } else { m_keywordSizeType = ParserKeywordSizeEnumFromString( sizeObject.as_string() ); } } bool ParserKeyword::validNameStart(const std::string& name) { if (name.length() > ParserConst::maxKeywordLength) return false; if (!isalpha(name[0])) return false; return true; } bool ParserKeyword::validInternalName(const std::string& name) { if (name.length() < 2) return false; else if (!std::isalpha(name[0])) return false; for (size_t i = 1; i < name.length(); i++) { char c = name[i]; if (!isalnum(c) && c != '_') { return false; } } return true; } std::string ParserKeyword::getDeckName(const std::string& rawString) { // only look at the first 8 characters (at most) std::string result = rawString.substr(0, 8); // remove any white space boost::algorithm::trim(result); return result; } static inline std::string uppercase( std::string str ) { /* the cctype toupper etc. are often implemented as macros, so its * unreliable with std algorithms. Hand-rolling map instead */ for( auto& c : str ) c = toupper( c ); return str; } bool ParserKeyword::validDeckName(const std::string& name) { // make the keyword string ALL_UPPERCASE because Eclipse seems // to be case-insensitive (although this is one of its // undocumented features...) auto upperCaseName = uppercase( name ); if (!validNameStart(upperCaseName)) return false; for (size_t i = 1; i < upperCaseName.length(); i++) { char c = upperCaseName[i]; if (!isalnum(c) && c != '-' && c != '_' && c != '+') { return false; } } return true; } bool ParserKeyword::hasMultipleDeckNames() const { return m_deckNames.size() > 1; } void ParserKeyword::initDeckNames(const Json::JsonObject& jsonObject) { if (!jsonObject.has_item("deck_names")) return; const Json::JsonObject namesObject = jsonObject.get_item("deck_names"); if (!namesObject.is_array()) throw std::invalid_argument("The 'deck_names' JSON item of keyword "+m_name+" needs to be a list"); if (namesObject.size() > 0) m_deckNames.clear(); for (size_t nameIdx = 0; nameIdx < namesObject.size(); ++ nameIdx) { const Json::JsonObject nameObject = namesObject.get_array_item(nameIdx); if (!nameObject.is_string()) throw std::invalid_argument("The sub-items of 'deck_names' of keyword "+m_name+" need to be strings"); addDeckName(nameObject.as_string()); } } void ParserKeyword::initSectionNames(const Json::JsonObject& jsonObject) { if (!jsonObject.has_item("sections")) throw std::invalid_argument("The 'sections' JSON item of keyword "+m_name+" needs to be defined"); const Json::JsonObject namesObject = jsonObject.get_item("sections"); if (!namesObject.is_array()) throw std::invalid_argument("The 'sections' JSON item of keyword "+m_name+" needs to be a list"); m_validSectionNames.clear(); for (size_t nameIdx = 0; nameIdx < namesObject.size(); ++ nameIdx) { const Json::JsonObject nameObject = namesObject.get_array_item(nameIdx); if (!nameObject.is_string()) throw std::invalid_argument("The sub-items of 'sections' of keyword "+m_name+" need to be strings"); addValidSectionName(nameObject.as_string()); } } void ParserKeyword::initMatchRegex(const Json::JsonObject& jsonObject) { if (!jsonObject.has_item("deck_name_regex")) return; const Json::JsonObject regexStringObject = jsonObject.get_item("deck_name_regex"); if (!regexStringObject.is_string()) throw std::invalid_argument("The 'deck_name_regex' JSON item of keyword "+m_name+" need to be a string"); setMatchRegex(regexStringObject.as_string()); } void ParserKeyword::addItems(const Json::JsonObject& itemsConfig) { if (itemsConfig.is_array()) { size_t num_items = itemsConfig.size(); std::shared_ptr record = std::make_shared(); for (size_t i = 0; i < num_items; i++) { const Json::JsonObject itemConfig = itemsConfig.get_array_item(i); if (itemConfig.has_item("value_type")) { ParserValueTypeEnum valueType = ParserValueTypeEnumFromString(itemConfig.get_string("value_type")); switch (valueType) { case INT: { ParserIntItemConstPtr item = ParserIntItemConstPtr(new ParserIntItem(itemConfig)); record->addItem( item ); } break; case STRING: { ParserStringItemConstPtr item = ParserStringItemConstPtr(new ParserStringItem(itemConfig)); record->addItem(item); } break; case DOUBLE: { ParserDoubleItemPtr item = ParserDoubleItemPtr(new ParserDoubleItem(itemConfig)); initDoubleItemDimension( item , itemConfig ); record->addItem(item); } break; default: throw std::invalid_argument("While parsing "+getName()+": Values of type "+itemConfig.get_string("value_type")+" are not implemented."); } } else throw std::invalid_argument("'value_type' JSON item missing for keyword "+getName()+"."); } addRecord(record); } else throw std::invalid_argument("The 'items' JSON item missing must be an array in keyword "+getName()+"."); } void ParserKeyword::initDoubleItemDimension( ParserDoubleItemPtr item, const Json::JsonObject itemConfig) { if (itemConfig.has_item("dimension")) { const Json::JsonObject dimensionConfig = itemConfig.get_item("dimension"); if (dimensionConfig.is_string()) item->push_backDimension( dimensionConfig.as_string() ); else if (dimensionConfig.is_array()) { for (size_t idim = 0; idim < dimensionConfig.size(); idim++) { Json::JsonObject dimObject = dimensionConfig.get_array_item( idim ); item->push_backDimension( dimObject.as_string()); } } else throw std::invalid_argument("The 'dimension' attribute of keyword "+getName()+" must be a string or a list of strings"); } } void ParserKeyword::initData(const Json::JsonObject& jsonConfig) { m_fixedSize = 1U; m_keywordSizeType = FIXED; const Json::JsonObject dataConfig = jsonConfig.get_item("data"); if (dataConfig.has_item("value_type")) { ParserValueTypeEnum valueType = ParserValueTypeEnumFromString(dataConfig.get_string("value_type")); const std::string itemName("data"); bool hasDefault = dataConfig.has_item("default"); std::shared_ptr record = std::make_shared(); switch (valueType) { case INT: { ParserIntItemPtr item = ParserIntItemPtr(new ParserIntItem(itemName, ALL)); if (hasDefault) { int defaultValue = dataConfig.get_int("default"); item->setDefault(defaultValue); } record->addDataItem( item ); } break; case STRING: { ParserStringItemPtr item = ParserStringItemPtr(new ParserStringItem(itemName, ALL)); if (hasDefault) { std::string defaultValue = dataConfig.get_string("default"); item->setDefault(defaultValue); } record->addItem( item ); } break; case DOUBLE: { ParserDoubleItemPtr item = ParserDoubleItemPtr(new ParserDoubleItem(itemName, ALL)); if (hasDefault) { double defaultValue = dataConfig.get_double("default"); item->setDefault(defaultValue); } initDoubleItemDimension( item , dataConfig ); record->addDataItem( item ); } break; default: throw std::invalid_argument("While initializing keyword "+getName()+": Values of type "+dataConfig.get_string("value_type")+" are not implemented."); } addDataRecord( record ); } else throw std::invalid_argument("The 'value_type' JSON item of keyword "+getName()+" is missing"); } ParserRecordPtr ParserKeyword::getRecord(size_t recordIndex) const { return m_records.get( recordIndex ); } std::vector::const_iterator ParserKeyword::recordBegin() const { return m_records.begin(); } std::vector::const_iterator ParserKeyword::recordEnd() const { return m_records.end(); } void ParserKeyword::addRecord(std::shared_ptr record) { m_records.push_back( record ); } void ParserKeyword::addDataRecord(std::shared_ptr record) { if ((m_keywordSizeType == FIXED) && (m_fixedSize == 1U)) addRecord(record); else throw std::invalid_argument("When calling addDataRecord() for keyword " + getName() + ", it must be configured with fixed size == 1."); } const std::string ParserKeyword::className() const { return getName(); } const std::string& ParserKeyword::getName() const { return m_name; } void ParserKeyword::clearValidSectionNames() { m_validSectionNames.clear(); } void ParserKeyword::addValidSectionName( const std::string& sectionName ) { m_validSectionNames.insert(sectionName); } bool ParserKeyword::isValidSection(const std::string& sectionName) const { return m_validSectionNames.size() == 0 || m_validSectionNames.count(sectionName) > 0; } ParserKeyword::SectionNameSet::const_iterator ParserKeyword::validSectionNamesBegin() const { return m_validSectionNames.begin(); } ParserKeyword::SectionNameSet::const_iterator ParserKeyword::validSectionNamesEnd() const { return m_validSectionNames.end(); } ParserKeyword::DeckNameSet::const_iterator ParserKeyword::deckNamesBegin() const { return m_deckNames.begin(); } ParserKeyword::DeckNameSet::const_iterator ParserKeyword::deckNamesEnd() const { return m_deckNames.end(); } DeckKeyword ParserKeyword::parse(const ParseContext& parseContext , RawKeywordPtr rawKeyword) const { if (rawKeyword->isFinished()) { DeckKeyword keyword( rawKeyword->getKeywordName() ); keyword.setLocation(rawKeyword->getFilename(), rawKeyword->getLineNR()); keyword.setDataKeyword( isDataKeyword() ); for (size_t i = 0; i < rawKeyword->size(); i++) { auto& rawRecord = rawKeyword->getRecord(i); if(m_records.size() > 0) { keyword.addRecord( getRecord( i )->parse( parseContext, rawRecord ) ); } else { if(rawRecord.size() > 0) { throw std::invalid_argument("Missing item information " + rawKeyword->getKeywordName()); } } } return keyword; } else throw std::invalid_argument("Tried to create a deck keyword from an incomplete raw keyword " + rawKeyword->getKeywordName()); } size_t ParserKeyword::getFixedSize() const { if (!hasFixedSize()) throw std::logic_error("The parser keyword "+getName()+" does not have a fixed size!"); return m_fixedSize; } bool ParserKeyword::hasFixedSize() const { return m_keywordSizeType == FIXED; } enum ParserKeywordSizeEnum ParserKeyword::getSizeType() const { return m_keywordSizeType; } const std::pair& ParserKeyword::getSizeDefinitionPair() const { return m_sizeDefinitionPair; } bool ParserKeyword::isDataKeyword() const { if (m_records.size() > 0) { auto record = m_records.get(0); return record->isDataRecord(); } else return false; } bool ParserKeyword::hasMatchRegex() const { return !m_matchRegexString.empty(); } void ParserKeyword::setMatchRegex(const std::string& deckNameRegexp) { try { #ifdef HAVE_REGEX m_matchRegex = std::regex(deckNameRegexp, std::regex::extended); #else m_matchRegex = boost::regex(deckNameRegexp); #endif m_matchRegexString = deckNameRegexp; } catch (const std::exception &e) { std::cerr << "Warning: Malformed regular expression for keyword '" << getName() << "':\n" << "\n" << e.what() << "\n" << "\n" << "Ignoring expression!\n"; } } bool ParserKeyword::matches(const std::string& deckKeywordName) const { if (!validDeckName(deckKeywordName)) return false; else if (m_deckNames.count(deckKeywordName) > 0) return true; else if (hasMatchRegex()) { #ifdef HAVE_REGEX return std::regex_match(deckKeywordName, m_matchRegex); #else return boost::regex_match(deckKeywordName, m_matchRegex); #endif } return false; } bool ParserKeyword::equal(const ParserKeyword& other) const { // compare the deck names. we don't care about the ordering of the strings. if (m_deckNames != other.m_deckNames) return false; { if ((m_name == other.m_name) && (m_matchRegexString == other.m_matchRegexString) && (m_keywordSizeType == other.m_keywordSizeType) && (isDataKeyword() == other.isDataKeyword()) && (m_isTableCollection == other.m_isTableCollection)) { bool equal_ = false; switch (m_keywordSizeType) { case FIXED: if (m_fixedSize == other.m_fixedSize) equal_ = true; break; case OTHER_KEYWORD_IN_DECK: if ((m_sizeDefinitionPair.first == other.m_sizeDefinitionPair.first) && (m_sizeDefinitionPair.second == other.m_sizeDefinitionPair.second)) equal_ = true; break; default: equal_ = true; break; } for (size_t recordIndex = 0; recordIndex < m_records.size(); recordIndex++) { std::shared_ptr record = getRecord(recordIndex); std::shared_ptr other_record = other.getRecord(recordIndex); equal_ = equal_ && record->equal( *other_record ); } return equal_; } else return false; } } std::string ParserKeyword::createDeclaration(const std::string& indent) const { std::stringstream ss; ss << indent << "class " << className() << " : public ParserKeyword {" << std::endl; ss << indent << "public:" << std::endl; { std::string local_indent = indent + " "; ss << local_indent << className() << "();" << std::endl; ss << local_indent << "static const std::string keywordName;" << std::endl; if (m_records.size() > 0 ) { for (auto iter = recordBegin(); iter != recordEnd(); ++iter) { std::shared_ptr record = *iter; for (size_t i = 0; i < record->size(); i++) { ParserItemConstPtr item = record->get(i); ss << std::endl; item->inlineClass(ss , local_indent ); } } } } ss << indent << "};" << std::endl << std::endl << std::endl; return ss.str(); } std::string ParserKeyword::createDecl() const { return className() + "::" + className() + "()"; } std::string ParserKeyword::createCode() const { std::stringstream ss; const std::string lhs = "keyword"; const std::string indent = " "; ss << className() << "::" << className() << "( ) : ParserKeyword(\"" << m_name << "\") {" << std::endl; { const std::string sizeString(ParserKeywordSizeEnum2String(m_keywordSizeType)); ss << indent; switch (m_keywordSizeType) { case SLASH_TERMINATED: ss << "setSizeType(" << sizeString << ");" << std::endl; break; case UNKNOWN: ss << "setSizeType(" << sizeString << ");" << std::endl; break; case FIXED: ss << "setFixedSize( (size_t) " << m_fixedSize << ");" << std::endl; break; case OTHER_KEYWORD_IN_DECK: ss << "setSizeType(" << sizeString << ");" << std::endl; ss << indent << "initSizeKeyword(\"" << m_sizeDefinitionPair.first << "\",\"" << m_sizeDefinitionPair.second << "\");" << std::endl; if (m_isTableCollection) ss << "setTableCollection( true );" << std::endl; break; } } ss << indent << "setDescription(\"" << getDescription() << "\");" << std::endl; // add the valid sections for the keyword ss << indent << "clearValidSectionNames();\n"; for (auto sectionNameIt = m_validSectionNames.begin(); sectionNameIt != m_validSectionNames.end(); ++sectionNameIt) { ss << indent << "addValidSectionName(\"" << *sectionNameIt << "\");" << std::endl; } // add the deck names ss << indent << "clearDeckNames();\n"; for (auto deckNameIt = m_deckNames.begin(); deckNameIt != m_deckNames.end(); ++deckNameIt) { ss << indent << "addDeckName(\"" << *deckNameIt << "\");" << std::endl; } // set the deck name match regex if (hasMatchRegex()) ss << indent << "setMatchRegex(\"" << m_matchRegexString << "\");" << std::endl; { if (m_records.size() > 0 ) { for (auto iter = recordBegin(); iter != recordEnd(); ++iter) { std::shared_ptr record = *iter; const std::string local_indent = indent + " "; ss << indent << "{" << std::endl; ss << local_indent << "std::shared_ptr record = std::make_shared();" << std::endl; for (size_t i = 0; i < record->size(); i++) { ParserItemConstPtr item = record->get(i); ss << local_indent << "{" << std::endl; { std::string indent3 = local_indent + " "; ss << indent3 << "ParserItemPtr item(" << item->createCode() << ");" << std::endl; ss << indent3 << "item->setDescription(\"" << item->getDescription() << "\");" << std::endl; for (size_t idim=0; idim < item->numDimensions(); idim++) ss << indent3 <<"item->push_backDimension(\"" << item->getDimension( idim ) << "\");" << std::endl; { std::string addItemMethod = "addItem"; if (isDataKeyword()) addItemMethod = "addDataItem"; ss << indent3 << "record->" << addItemMethod << "(item);" << std::endl; } } ss << local_indent << "}" << std::endl; } if (record->isDataRecord()) ss << local_indent << "addDataRecord( record );" << std::endl; else ss << local_indent << "addRecord( record );" << std::endl; ss << indent << "}" << std::endl; } } } ss << "}" << std::endl; ss << "const std::string " << className() << "::keywordName = \"" << getName() << "\";" << std::endl; for (auto iter = recordBegin(); iter != recordEnd(); ++iter) { std::shared_ptr record = *iter; for (size_t i = 0; i < record->size(); i++) { ParserItemConstPtr item = record->get(i); ss << item->inlineClassInit(className()); } } ss << std::endl; return ss.str(); } void ParserKeyword::applyUnitsToDeck(const Deck& deck, DeckKeyword& deckKeyword) const { for (size_t index = 0; index < deckKeyword.size(); index++) { std::shared_ptr parserRecord = getRecord(index); auto& deckRecord = deckKeyword.getRecord(index); parserRecord->applyUnitsToDeck( deck , deckRecord ); } } }