| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "properties/PropertiesFile.h" |
| |
| #include <algorithm> |
| #include <fstream> |
| #include <utility> |
| #include "utils/StringUtils.h" |
| |
| namespace org::apache::nifi::minifi { |
| |
| PropertiesFile::Line::Line(std::string line) : line_(line) { |
| line = utils::string::trim(line); |
| if (line.empty() || line[0] == '#') { return; } |
| |
| size_t index_of_first_equals_sign = line.find('='); |
| if (index_of_first_equals_sign == std::string::npos) { return; } |
| |
| std::string key = utils::string::trim(line.substr(0, index_of_first_equals_sign)); |
| if (key.empty()) { return; } |
| |
| key_ = key; |
| value_ = utils::string::trim(line.substr(index_of_first_equals_sign + 1)); |
| } |
| |
| PropertiesFile::Line::Line(std::string key, std::string value) |
| : line_{utils::string::join_pack(key, "=", value)}, key_{std::move(key)}, value_{std::move(value)} { |
| } |
| |
| bool PropertiesFile::Line::isValidKey(const std::string &key) { |
| return !key.empty(); |
| } |
| |
| void PropertiesFile::Line::updateValue(const std::string& value) { |
| auto pos = line_.find('='); |
| if (pos != std::string::npos) { |
| line_.replace(pos + 1, std::string::npos, value); |
| value_ = value; |
| } else { |
| throw std::invalid_argument{"Cannot update value in config line: it does not contain an = sign!"}; |
| } |
| } |
| |
| PropertiesFile::PropertiesFile(std::istream& input_stream) { |
| std::string line; |
| while (std::getline(input_stream, line)) { |
| lines_.push_back(Line{line}); |
| } |
| } |
| |
| PropertiesFile::Lines::const_iterator PropertiesFile::findKey(const std::string& key) const { |
| if (!Line::isValidKey(key)) { |
| return lines_.cend(); |
| } |
| return std::find_if(lines_.cbegin(), lines_.cend(), [&key](const Line& line) { |
| return line.getKey() == key; |
| }); |
| } |
| |
| PropertiesFile::Lines::iterator PropertiesFile::findKey(const std::string& key) { |
| if (!Line::isValidKey(key)) { |
| return lines_.end(); |
| } |
| return std::find_if(lines_.begin(), lines_.end(), [&key](const Line& line) { |
| return line.getKey() == key; |
| }); |
| } |
| |
| bool PropertiesFile::hasValue(const std::string& key) const { |
| return findKey(key) != lines_.end(); |
| } |
| |
| std::optional<std::string> PropertiesFile::getValue(const std::string& key) const { |
| const auto it = findKey(key); |
| if (it != lines_.end()) { |
| return it->getValue(); |
| } else { |
| return std::nullopt; |
| } |
| } |
| |
| void PropertiesFile::update(const std::string& key, const std::string& value) { |
| auto it = findKey(key); |
| if (it != lines_.end()) { |
| it->updateValue(value); |
| } else { |
| throw std::invalid_argument{"Key " + key + " not found in the config file!"}; |
| } |
| } |
| |
| void PropertiesFile::insertAfter(const std::string& after_key, const std::string& key, const std::string& value) { |
| auto it = findKey(after_key); |
| if (it != lines_.end()) { |
| ++it; |
| lines_.emplace(it, key, value); |
| } else { |
| throw std::invalid_argument{"Key " + after_key + " not found in the config file!"}; |
| } |
| } |
| |
| void PropertiesFile::append(const std::string& key, const std::string& value) { |
| lines_.emplace_back(key, value); |
| } |
| |
| int PropertiesFile::erase(const std::string& key) { |
| if (!Line::isValidKey(key)) { |
| return 0; |
| } |
| auto has_this_key = [&key](const Line& line) { return line.getKey() == key; }; |
| auto new_end = std::remove_if(lines_.begin(), lines_.end(), has_this_key); |
| auto num_removed = std::distance(new_end, lines_.end()); |
| lines_.erase(new_end, lines_.end()); |
| return gsl::narrow<int>(num_removed); |
| } |
| |
| void PropertiesFile::writeTo(const std::filesystem::path& file_path) const { |
| try { |
| std::ofstream file{file_path}; |
| file.exceptions(std::ios::failbit | std::ios::badbit); |
| |
| for (const auto& line : lines_) { |
| file << line.getLine() << '\n'; |
| } |
| } catch (const std::exception&) { |
| throw std::runtime_error{"Could not write to file " + file_path.string()}; |
| } |
| } |
| |
| PropertiesFile::Lines::const_iterator PropertiesFile::begin() const { |
| return lines_.begin(); |
| } |
| |
| PropertiesFile::Lines::const_iterator PropertiesFile::end() const { |
| return lines_.end(); |
| } |
| |
| } // namespace org::apache::nifi::minifi |