//+----------------------------------------------------------------------------+ //| Description: Magic Set Editor - Program to make Magic (tm) cards | //| Copyright: (C) 2001 - 2006 Twan van Laarhoven | //| License: GNU General Public License 2 or later (see file COPYING) | //+----------------------------------------------------------------------------+ #ifndef HEADER_UTIL_IO_READER #define HEADER_UTIL_IO_READER // ----------------------------------------------------------------------------- : Includes #include #include #include template class Defaultable; template class Scriptable; DECLARE_POINTER_TYPE(Game); DECLARE_POINTER_TYPE(StyleSheet); // ----------------------------------------------------------------------------- : Reader typedef wxInputStream InputStream; typedef shared_ptr InputStreamP; /// The Reader can be used for reading (deserializing) objects /** This class makes use of the reflection functionality, in effect * an object tells the Reader what fields it would like to read. * The reader then sees if the requested field is currently available. * * The handle functions ensure that afterwards the reader is at the line after the * object that was just read. */ class Reader { public: /// Construct a reader that reads from the given input stream /** filename is used only for error messages */ Reader(const InputStreamP& input, const String& filename = wxEmptyString); /// Construct a reader that reads a file in a package /** Used for "include file" keys. */ Reader(const String& filename); /// Tell the reflection code we are reading inline bool reading() const { return true; } /// Is the thing currently being read 'complex', i.e. does it have children inline bool isComplex() const { return value.empty(); } /// Read and check the application version void handleAppVersion(); /// Show a warning message, but continue reading void warning(const String& msg); // --------------------------------------------------- : Handling objects /// Handle an object: read it if it's name matches template void handle(const Char* name, T& object) { if (enterBlock(name)) { handle(object); exitBlock(); } } /// Reads a vector from the input stream template void handle(const Char* name, vector& vector); /// Reads an object of type T from the input stream template void handle(T& object); /// Reads a shared_ptr from the input stream template void handle(shared_ptr& pointer); /// Reads a map from the input stream template void handle(map& m); /// Reads an IndexMap from the input stream, reads only keys that already exist in the map template void handle(IndexMap& m); /// Reads a Defaultable from the input stream template void handle(Defaultable&); /// Reads a Scriptable from the input stream template void handle(Scriptable&); // special behaviour void handle(GameP&); void handle(StyleSheet&); // --------------------------------------------------- : Data /// App version this file was made with Version app_version; private: /// The line we read String line; /// The key and value of the last line we read String key, value; /// A string spanning multiple lines String multi_line_str; /// Indentation of the last line we read int indent; /// Indentation of the block we are in int expected_indent; /// Did we just open a block (i.e. not read any more lines of it)? bool just_opened; /// Filename for error messages String filename; /// Line number for error messages UInt line_number; /// Input stream we are reading from InputStreamP input; /// Text stream wrapping the input stream wxTextInputStream stream; // --------------------------------------------------- : Reading the stream /// Is there a block with the given key under the current cursor? bool enterBlock(const Char* name); /// Leave the block we are in void exitBlock(); /// Move to the next non empty line void moveNext(); /// Reads the next line from the input, and stores it in line/key/value/indent void readLine(); /// No line was read, because nothing mathes the current key /** Maybe the key is "include file" */ template void unknownKey(T& v) { if (key == _("include_file")) { Reader reader(value); reader.handle(v); moveNext(); } else { unknownKey(); } } void unknownKey(); }; // ----------------------------------------------------------------------------- : Container types /// Construct a new type, possibly reading something in the process. /** By default just creates a new object. * This function can be overloaded to provide different behaviour */ template shared_ptr read_new(Reader& reader) { return new_shared(); } /// Update the 'index' member of a value for use by IndexMap template void update_index(T&, size_t index) {} template void Reader::handle(const Char* name, vector& vector) { String vectorKey = singular_form(name); while (enterBlock(vectorKey)) { vector.resize(vector.size() + 1); handle(vector.back()); update_index(vector.back(), vector.size() - 1); // update index for IndexMap exitBlock(); } } template void Reader::handle(shared_ptr& pointer) { if (!pointer) pointer = read_new(*this); handle(*pointer); } template void Reader::handle(map& m) { // TODO } template void Reader::handle(IndexMap& m) { do { UInt l = line_number; for (typename IndexMap::iterator it = m.begin() ; it != m.end() ; ++it) { handle(get_key_name(*it).c_str(), *it); } if (l == line_number) unknownKey(m); } while (indent >= expected_indent); } // ----------------------------------------------------------------------------- : Reflection /// Implement reflection as used by Reader #define REFLECT_OBJECT_READER(Cls) \ template<> void Reader::handle(Cls& object) { \ do { \ UInt l = line_number; \ object.reflect(*this); \ if (l == line_number) unknownKey(object); \ } while (indent >= expected_indent); \ } \ void Cls::reflect(Reader& reader) { \ reflect_impl(reader); \ } // ----------------------------------------------------------------------------- : Reflection for enumerations /// Implement enum reflection as used by Reader #define REFLECT_ENUM_READER(Enum) \ template<> void Reader::handle(Enum& enum_) { \ EnumReader reader(value); \ reflect_ ## Enum(enum_, reader); \ if (!reader.isDone()) { \ /* warning: unknown value */ \ warning(_("Unrecognized value: ") + value); \ } \ } /// 'Tag' to be used when reflecting enumerations for Reader class EnumReader { public: inline EnumReader(String read) : read(read), first(true), done(false) {} /// Handle a possible value for the enum, if the name matches the name in the input template inline void handle(const Char* name, Enum value, Enum& enum_) { if (!done && read == name) { done = true; first = false; enum_ = value; } else if (first) { first = false; enum_ = value; } } inline bool isDone() const { return done; } private: String read; ///< The string to match to a value name bool first; ///< Has the first (default) value been matched? bool done; ///< Was anything matched? }; // ----------------------------------------------------------------------------- : EOF #endif