// Copyright (c) 2010, Amar Takhar // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // $Id$ /// @file option.cpp /// @brief Option interface. /// @ingroup libaegisub #include "libaegisub/option.h" #ifndef LAGI_PRE #include #include #include #include #endif #include "libaegisub/cajun/reader.h" #include "libaegisub/cajun/writer.h" #include "libaegisub/cajun/elements.h" #include "libaegisub/access.h" #include "libaegisub/io.h" #include "libaegisub/log.h" #include "libaegisub/option_value.h" #include "option_visit.h" namespace agi { Options::Options(const std::string &file, const std::string& default_config, const OptionSetting setting) : config_file(file), config_default(default_config), config_loaded(false), setting(setting) { LOG_D("agi/options") << "New Options object"; std::istringstream stream(default_config); LoadConfig(stream); } Options::~Options() { if ((setting & FLUSH_SKIP) != FLUSH_SKIP) { Flush(); } for (OptionValueMap::iterator i = values.begin(); i != values.end(); i++) { delete i->second; } } void Options::ConfigNext(std::istream& stream) { LoadConfig(stream); } void Options::ConfigUser() { std::auto_ptr stream; try { stream.reset(agi::io::Open(config_file)); } catch (const acs::AcsNotFound&) { return; } /// @todo Handle other errors such as parsing and notifying the user. LoadConfig(*stream); config_loaded = true; } void Options::LoadConfig(std::istream& stream) { /// @todo Store all previously loaded configs in an array for bug report purposes, /// this is just a temp stub. json::UnknownElement config_root; try { json::Reader::Read(config_root, stream); } catch (json::Reader::ParseException& e) { LOG_E("option/load") << "json::ParseException: " << e.what() << ", Line/offset: " << e.m_locTokenBegin.m_nLine + 1 << '/' << e.m_locTokenBegin.m_nLineOffset + 1; } catch (json::Exception& e) { /// @todo Do something better here, maybe print the exact error LOG_E("option/load") << "json::Exception: " << e.what(); } ConfigVisitor config_visitor(values, ""); config_root.Accept(config_visitor); } OptionValue* Options::Get(const std::string &name) { OptionValueMap::iterator index; if ((index = values.find(name)) != values.end()) return index->second; LOG_E("option/get") << "agi::Options::Get Option not found: (" << name << ")"; throw OptionErrorNotFound("Option value not found: " + name); } void Options::Flush() { json::Object obj_out; for (OptionValueMap::const_iterator i = values.begin(); i != values.end(); ++i) { switch (i->second->GetType()) { case OptionValue::Type_String: PutOption(obj_out, i->first, (json::String)i->second->GetString()); break; case OptionValue::Type_Int: PutOption(obj_out, i->first, (json::Number)(const double)i->second->GetInt()); break; case OptionValue::Type_Double: PutOption(obj_out, i->first, (json::Number)i->second->GetDouble()); break; case OptionValue::Type_Colour: PutOption(obj_out, i->first, (json::String)i->second->GetColour()); break; case OptionValue::Type_Bool: PutOption(obj_out, i->first, (json::Boolean)i->second->GetBool()); break; case OptionValue::Type_List_String: { std::vector array_string; i->second->GetListString(array_string); json::Array array; for (std::vector::const_iterator i_str = array_string.begin(); i_str != array_string.end(); ++i_str) { json::Object obj; obj["string"] = json::String(*i_str); array.push_back(obj); } PutOption(obj_out, i->first, (json::Array)array); } break; case OptionValue::Type_List_Int: { std::vector array_int; i->second->GetListInt(array_int); json::Array array; for (std::vector::const_iterator i_int = array_int.begin(); i_int != array_int.end(); ++i_int) { json::Object obj; obj["int"] = json::Number((const double)*i_int); array.push_back(obj); } PutOption(obj_out, i->first, (json::Array)array); } break; case OptionValue::Type_List_Double: { std::vector array_double; i->second->GetListDouble(array_double); json::Array array; for (std::vector::const_iterator i_double = array_double.begin(); i_double != array_double.end(); ++i_double) { json::Object obj; obj["double"] = json::Number(*i_double); array.push_back(obj); } PutOption(obj_out, i->first, (json::Array)array); } break; case OptionValue::Type_List_Colour: { std::vector array_colour; i->second->GetListColour(array_colour); json::Array array; for (std::vector::const_iterator i_colour = array_colour.begin(); i_colour != array_colour.end(); ++i_colour) { json::Object obj; Colour col = *i_colour; std::string str = std::string(col); obj["colour"] = json::String(str); array.push_back(obj); } PutOption(obj_out, i->first, (json::Array)array); } break; case OptionValue::Type_List_Bool: { std::vector array_bool; json::Array array; i->second->GetListBool(array_bool); for (std::vector::const_iterator i_bool = array_bool.begin(); i_bool != array_bool.end(); ++i_bool) { json::Object obj; obj["bool"] = json::Boolean(*i_bool); array.push_back(obj); } PutOption(obj_out, i->first, (json::Array)array); } break; } } io::Save file(config_file); json::Writer::Write(obj_out, file.Get()); } bool Options::PutOption(json::Object &obj, const std::string &path, const json::UnknownElement &value) { // Having a '/' denotes it is a leaf. if (path.find('/') == std::string::npos) { json::Object::iterator pos = obj.find(path); // Fail if a key of the same name already exists. if (pos != obj.end()) throw OptionErrorDuplicateKey("Key already exists"); obj[path] = value; return true; } else { std::string thispart = path.substr(0, path.find("/")); std::string restpart = path.substr(path.find("/")+1, path.size()); json::Object::iterator pos = obj.find(thispart); // New key, make object. if (pos == obj.end()) pos = obj.insert(json::Object::Member(thispart, json::Object())); PutOptionVisitor visitor(restpart, value); pos->element.Accept(visitor); return visitor.result; } } } // namespace agi