Change tabs to two spaces.

This commit is contained in:
Lymia Aluysia
2017-01-18 08:43:21 -06:00
parent d7f5f0dc3b
commit d2c635f739
329 changed files with 41307 additions and 41496 deletions
+2 -2
View File
@@ -34,10 +34,10 @@ template <> void GetDefaultMember::handle(const wxDateTime& v) { value = to_sc
// ----------------------------------------------------------------------------- : GetMember
GetMember::GetMember(const String& name)
: target_name(name)
: target_name(name)
{}
// caused by the pattern: if (!tag.isComplex()) { REFLECT_NAMELESS(stuff) }
template <> void GetMember::handle(const String& v) {
throw InternalError(_("GetDefaultMember::handle"));
throw InternalError(_("GetDefaultMember::handle"));
}
+107 -107
View File
@@ -24,42 +24,42 @@ template <typename T> class Scriptable;
/** The member is wrapped in a ScriptValue */
class GetDefaultMember {
public:
/// Tell the reflection code we are not reading
inline bool reading() const { return false; }
inline bool scripting() const { return true; }
inline bool isComplex() const { return false; }
inline void addAlias(int, const Char*, const Char*) {}
inline void handleIgnore(int, const Char*) {}
/// The result, or script_nil if the member was not found
inline ScriptValueP result() { return value; }
// --------------------------------------------------- : Handling objects
/// Handle an object: we don't match things with a name
template <typename T>
void handle(const Char* name, const T& object) {}
/// Don't handle a value
template <typename T>
inline void handleNoScript(const Char* name, T& value) {}
/// Tell the reflection code we are not reading
inline bool reading() const { return false; }
inline bool scripting() const { return true; }
inline bool isComplex() const { return false; }
inline void addAlias(int, const Char*, const Char*) {}
inline void handleIgnore(int, const Char*) {}
/// The result, or script_nil if the member was not found
inline ScriptValueP result() { return value; }
// --------------------------------------------------- : Handling objects
/// Handle an object: we don't match things with a name
template <typename T>
void handle(const Char* name, const T& object) {}
/// Don't handle a value
template <typename T>
inline void handleNoScript(const Char* name, T& value) {}
/// Handle an object: investigate children, or store it if we know how
void handle(const Char *);
template <typename T> void handle(const T&);
/// Handle a Defaultable: investigate children
template <typename T> void handle(const Defaultable<T>&);
template <typename T> void handle(const Scriptable<T>& );
template <typename T> void handle(const vector<T>& c) { value = to_script(&c); }
template <typename K, typename V> void handle(const map<K,V>& c) { value = to_script(&c); }
template <typename K, typename V> void handle(const IndexMap<K,V>& c) { value = to_script(&c); }
template <typename K, typename V> void handle(const DelayedIndexMaps<K,V>&) {}
template <typename K, typename V> void handle(const DelayedIndexMapsData<K,V>& c);
template <typename T> void handle(const intrusive_ptr<T>& p) { value = to_script(p); }
void handle(const ScriptValueP&);
void handle(const ScriptP&);
/// Handle an object: investigate children, or store it if we know how
void handle(const Char *);
template <typename T> void handle(const T&);
/// Handle a Defaultable: investigate children
template <typename T> void handle(const Defaultable<T>&);
template <typename T> void handle(const Scriptable<T>& );
template <typename T> void handle(const vector<T>& c) { value = to_script(&c); }
template <typename K, typename V> void handle(const map<K,V>& c) { value = to_script(&c); }
template <typename K, typename V> void handle(const IndexMap<K,V>& c) { value = to_script(&c); }
template <typename K, typename V> void handle(const DelayedIndexMaps<K,V>&) {}
template <typename K, typename V> void handle(const DelayedIndexMapsData<K,V>& c);
template <typename T> void handle(const intrusive_ptr<T>& p) { value = to_script(p); }
void handle(const ScriptValueP&);
void handle(const ScriptP&);
private:
ScriptValueP value; ///< The value we found (if any)
ScriptValueP value; ///< The value we found (if any)
};
// ----------------------------------------------------------------------------- : GetMember
@@ -68,96 +68,96 @@ class GetDefaultMember {
/** The member is wrapped in a ScriptValue */
class GetMember : private GetDefaultMember {
public:
/// Construct a member getter that looks for the given name
GetMember(const String& name);
/// Tell the reflection code we are not reading
inline bool reading() const { return false; }
inline bool scripting() const { return true; }
inline bool isComplex() const { return true; }
inline void addAlias(int, const Char*, const Char*) {}
inline void handleIgnore(int, const Char*) {}
/// The result, or script_nil if the member was not found
inline ScriptValueP result() { return gdm.result(); }
// --------------------------------------------------- : Handling objects
/// Handle an object: we are done if the name matches
template <typename T>
void handle(const Char* name, const T& object) {
if (!gdm.result() && cannocial_name_compare(target_name, name)) {
gdm.handle(object);
}
}
/// Don't handle a value
template <typename T>
inline void handleNoScript(const Char* name, T& value) {}
/// Handle an object: investigate children
template <typename T> void handle(const T&);
/// Handle an index map: invistigate keys
template <typename K, typename V> void handle(const IndexMap<K,V>& m) {
if (gdm.result()) return;
for (typename IndexMap<K,V>::const_iterator it = m.begin() ; it != m.end() ; ++it) {
if (get_key_name(*it) == target_name) {
gdm.handle(*it);
return;
}
}
}
template <typename K, typename V> void handle(const DelayedIndexMaps<K,V>&);
template <typename K, typename V> void handle(const DelayedIndexMapsData<K,V>&);
/// Construct a member getter that looks for the given name
GetMember(const String& name);
/// Tell the reflection code we are not reading
inline bool reading() const { return false; }
inline bool scripting() const { return true; }
inline bool isComplex() const { return true; }
inline void addAlias(int, const Char*, const Char*) {}
inline void handleIgnore(int, const Char*) {}
/// The result, or script_nil if the member was not found
inline ScriptValueP result() { return gdm.result(); }
// --------------------------------------------------- : Handling objects
/// Handle an object: we are done if the name matches
template <typename T>
void handle(const Char* name, const T& object) {
if (!gdm.result() && cannocial_name_compare(target_name, name)) {
gdm.handle(object);
}
}
/// Don't handle a value
template <typename T>
inline void handleNoScript(const Char* name, T& value) {}
/// Handle an object: investigate children
template <typename T> void handle(const T&);
/// Handle an index map: invistigate keys
template <typename K, typename V> void handle(const IndexMap<K,V>& m) {
if (gdm.result()) return;
for (typename IndexMap<K,V>::const_iterator it = m.begin() ; it != m.end() ; ++it) {
if (get_key_name(*it) == target_name) {
gdm.handle(*it);
return;
}
}
}
template <typename K, typename V> void handle(const DelayedIndexMaps<K,V>&);
template <typename K, typename V> void handle(const DelayedIndexMapsData<K,V>&);
private:
const String& target_name; ///< The name we are looking for
GetDefaultMember gdm; ///< Object to store and retrieve the value
const String& target_name; ///< The name we are looking for
GetDefaultMember gdm; ///< Object to store and retrieve the value
};
// ----------------------------------------------------------------------------- : Reflection
/// Implement reflection as used by GetDefaultMember
#define REFLECT_OBJECT_GET_DEFAULT_MEMBER(Cls) REFLECT_WRITE_YES(Cls,GetDefaultMember)
#define REFLECT_OBJECT_GET_MEMBER(Cls) REFLECT_WRITE_YES(Cls,GetMember)
#define REFLECT_OBJECT_GET_DEFAULT_MEMBER_NOT(Cls) REFLECT_WRITE_NO(Cls,GetDefaultMember)
#define REFLECT_OBJECT_GET_MEMBER_NOT(Cls) REFLECT_WRITE_NO(Cls,GetMember)
#define REFLECT_OBJECT_GET_DEFAULT_MEMBER(Cls) REFLECT_WRITE_YES(Cls,GetDefaultMember)
#define REFLECT_OBJECT_GET_MEMBER(Cls) REFLECT_WRITE_YES(Cls,GetMember)
#define REFLECT_OBJECT_GET_DEFAULT_MEMBER_NOT(Cls) REFLECT_WRITE_NO(Cls,GetDefaultMember)
#define REFLECT_OBJECT_GET_MEMBER_NOT(Cls) REFLECT_WRITE_NO(Cls,GetMember)
#define REFLECT_WRITE_YES(Cls, Tag) \
template<> void Tag::handle<Cls>(const Cls& object) { \
const_cast<Cls&>(object).reflect(*this); \
} \
void Cls::reflect(Tag& tag) { \
reflect_impl(tag); \
}
#define REFLECT_WRITE_YES(Cls, Tag) \
template<> void Tag::handle<Cls>(const Cls& object) { \
const_cast<Cls&>(object).reflect(*this); \
} \
void Cls::reflect(Tag& tag) { \
reflect_impl(tag); \
}
#define REFLECT_WRITE_NO(Cls, Tag) \
template<> void Tag::handle<Cls>(const Cls& object) {} \
void Cls::reflect(Tag& tag) {}
#define REFLECT_WRITE_NO(Cls, Tag) \
template<> void Tag::handle<Cls>(const Cls& object) {} \
void Cls::reflect(Tag& tag) {}
// ----------------------------------------------------------------------------- : Reflection for enumerations
/// Implement enum reflection as used by GetMember
#define REFLECT_ENUM_GET_MEMBER(Enum) \
template<> void GetDefaultMember::handle<Enum>(const Enum& enum_) { \
EnumGetMember egm(*this); \
reflect_ ## Enum(const_cast<Enum&>(enum_), egm); \
}
#define REFLECT_ENUM_GET_MEMBER(Enum) \
template<> void GetDefaultMember::handle<Enum>(const Enum& enum_) { \
EnumGetMember egm(*this); \
reflect_ ## Enum(const_cast<Enum&>(enum_), egm); \
}
/// 'Tag' to be used when reflecting enumerations for GetMember
class EnumGetMember {
public:
inline EnumGetMember(GetDefaultMember& gdm)
: gdm(gdm) {}
/// Handle a possible value for the enum, if the name matches the name in the input
template <typename Enum>
inline void handle(const Char* name, Enum value, Enum enum_) {
if (enum_ == value) {
gdm.handle(String(name));
}
}
inline EnumGetMember(GetDefaultMember& gdm)
: gdm(gdm) {}
/// Handle a possible value for the enum, if the name matches the name in the input
template <typename Enum>
inline void handle(const Char* name, Enum value, Enum enum_) {
if (enum_ == value) {
gdm.handle(String(name));
}
}
private:
GetDefaultMember& gdm; ///< The object to store output in
GetDefaultMember& gdm; ///< The object to store output in
};
// ----------------------------------------------------------------------------- : EOF
+421 -421
View File
File diff suppressed because it is too large Load Diff
+171 -171
View File
@@ -49,154 +49,154 @@ DECLARE_DYNAMIC_ARG(Package*, clipboard_package);
*/
class Package : public IntrusivePtrVirtualBase {
public:
// --------------------------------------------------- : Managing the outside of the package
// --------------------------------------------------- : Managing the outside of the package
/// Creates a new package
Package();
virtual ~Package();
/// Creates a new package
Package();
virtual ~Package();
/// Is a file opened?
bool isOpened() const;
/// Must the package be saved with saveAs()?
/// This is the case if the package has not been saved/opened before
bool needSaveAs() const;
/// Determines the short name of this package: the filename without path or extension
String name() const;
/// Return the relative filename of this file, the name and extension
String relativeFilename() const;
/// Return the absolute filename of this file
const String& absoluteFilename() const;
/// The time this package was last modified
inline wxDateTime lastModified() const { return modified; }
/// Is a file opened?
bool isOpened() const;
/// Must the package be saved with saveAs()?
/// This is the case if the package has not been saved/opened before
bool needSaveAs() const;
/// Determines the short name of this package: the filename without path or extension
String name() const;
/// Return the relative filename of this file, the name and extension
String relativeFilename() const;
/// Return the absolute filename of this file
const String& absoluteFilename() const;
/// The time this package was last modified
inline wxDateTime lastModified() const { return modified; }
/// Open a package
/**
* Should only be called when the package is constructed using the default constructor!
*
* If 'fast' is set, then for directories a full directory listing is not performed.
* This means that the file_infos will not be fully initialized.
*
* @pre open not called before [TODO]
*/
void open(const String& package, bool fast = false);
/// Open a package
/**
* Should only be called when the package is constructed using the default constructor!
*
* If 'fast' is set, then for directories a full directory listing is not performed.
* This means that the file_infos will not be fully initialized.
*
* @pre open not called before [TODO]
*/
void open(const String& package, bool fast = false);
/// Saves the package
/**
* By default saves as a zip file, unless it was already a directory.
*
* If remove_unused=true all files that were in the file and
* are not touched with referenceFile will be deleted from the new archive!
* This is a form of garbage collection, to get rid of old picture files for example.
*/
void save(bool remove_unused = true);
/// Saves the package
/**
* By default saves as a zip file, unless it was already a directory.
*
* If remove_unused=true all files that were in the file and
* are not touched with referenceFile will be deleted from the new archive!
* This is a form of garbage collection, to get rid of old picture files for example.
*/
void save(bool remove_unused = true);
/// Saves the package under a different filename
void saveAs(const String& package, bool remove_unused = true);
/// Saves the package under a different filename
void saveAs(const String& package, bool remove_unused = true);
/// Saves the package under a different filename, but keep the old one open
void saveCopy(const String& package);
/// Saves the package under a different filename, but keep the old one open
void saveCopy(const String& package);
// --------------------------------------------------- : Managing the inside of the package
// --------------------------------------------------- : Managing the inside of the package
/// Open an input stream for a file in the package.
InputStreamP openIn(const String& file);
/// Open an input stream for a file in the package.
InputStreamP openIn(const String& file);
/// Open an output stream for a file in the package.
/// (changes are only committed with save())
OutputStreamP openOut(const String& file);
/// Open an output stream for a file in the package.
/// (changes are only committed with save())
OutputStreamP openOut(const String& file);
/// Get a filename that can be written to to modfify a file in the package
/// (changes are only committed with save())
String nameOut(const String& file);
/// Get a filename that can be written to to modfify a file in the package
/// (changes are only committed with save())
String nameOut(const String& file);
/// Creates a new, unique, filename with the specified prefix and suffix
/// for example newFileName("image/",".jpg") -> "image/1.jpg"
/// Returns the name of a temporary file that can be written to.
FileName newFileName(const String& prefix, const String& suffix);
/// Creates a new, unique, filename with the specified prefix and suffix
/// for example newFileName("image/",".jpg") -> "image/1.jpg"
/// Returns the name of a temporary file that can be written to.
FileName newFileName(const String& prefix, const String& suffix);
/// Signal that a file is still used by this package.
/// Must be called for files not opened using openOut/nameOut
/// If they are to be kept in the package.
void referenceFile(const String& file);
/// Signal that a file is still used by this package.
/// Must be called for files not opened using openOut/nameOut
/// If they are to be kept in the package.
void referenceFile(const String& file);
/// Get an 'absolute filename' for a file in the package.
/// This file can later be opened from anywhere (other process) using openAbsoluteFile()
String absoluteName(const String& file);
/// Get an 'absolute filename' for a file in the package.
/// This file can later be opened from anywhere (other process) using openAbsoluteFile()
String absoluteName(const String& file);
/// Open a file given an absolute filename
static InputStreamP openAbsoluteFile(const String& name);
/// Open a file given an absolute filename
static InputStreamP openAbsoluteFile(const String& name);
// --------------------------------------------------- : Managing the inside of the package : Reader/writer
// --------------------------------------------------- : Managing the inside of the package : Reader/writer
template <typename T>
void readFile(const String& file, T& obj);
template <typename T>
void readFile(const String& file, T& obj);
template <typename T>
T readFile(const String& file) {
T obj;
readFile(file, obj);
return obj;
}
template <typename T>
T readFile(const String& file) {
T obj;
readFile(file, obj);
return obj;
}
template <typename T>
void writeFile(const String& file, const T& obj, Version file_version) {
Writer writer(openOut(file), file_version);
writer.handle(obj);
}
template <typename T>
void writeFile(const String& file, const T& obj, Version file_version) {
Writer writer(openOut(file), file_version);
writer.handle(obj);
}
protected:
// TODO: I dislike putting this here very much. There ought to be a better way.
virtual VCSP getVCS() { return intrusive(new VCS()); }
// TODO: I dislike putting this here very much. There ought to be a better way.
virtual VCSP getVCS() { return intrusive(new VCS()); }
/// true if this is a zip file, false if a directory
bool isZipfile() const { return !wxDirExists(filename); }
/// true if this is a zip file, false if a directory
bool isZipfile() const { return !wxDirExists(filename); }
// --------------------------------------------------- : Private stuff
// --------------------------------------------------- : Private stuff
private:
/// Information about a file in the package
struct FileInfo {
FileInfo();
~FileInfo();
bool keep; ///< Should this file be kept in the package? (as opposed to deleting it)
bool created; ///< Was this file just created (e.g. should the VCS add it?)
String tempName; ///< Name of the temporary file where new contents of this file are placed
wxZipEntry* zipEntry; ///< Entry in the zip file for this file
/// Is this file changed, and therefore written to a temporary file?
inline bool wasWritten() const { return !tempName.empty(); }
};
/// Information about a file in the package
struct FileInfo {
FileInfo();
~FileInfo();
bool keep; ///< Should this file be kept in the package? (as opposed to deleting it)
bool created; ///< Was this file just created (e.g. should the VCS add it?)
String tempName; ///< Name of the temporary file where new contents of this file are placed
wxZipEntry* zipEntry; ///< Entry in the zip file for this file
/// Is this file changed, and therefore written to a temporary file?
inline bool wasWritten() const { return !tempName.empty(); }
};
/// Filename of the package
String filename;
/// Last modified time
DateTime modified;
/// Filename of the package
String filename;
/// Last modified time
DateTime modified;
public:
/// Information on files in the package
/** Note: must be public for DECLARE_TYPEOF to work */
typedef map<String, FileInfo> FileInfos;
inline const FileInfos& getFileInfos() const { return files; }
/// When was a file last modified?
DateTime modificationTime(const pair<String, FileInfo>& fi) const;
/// Information on files in the package
/** Note: must be public for DECLARE_TYPEOF to work */
typedef map<String, FileInfo> FileInfos;
inline const FileInfos& getFileInfos() const { return files; }
/// When was a file last modified?
DateTime modificationTime(const pair<String, FileInfo>& fi) const;
private:
/// All files in the package
FileInfos files;
/// Filestream for reading zip files
wxFileInputStream* fileStream;
/// Filestream for reading zip files
wxZipInputStream* zipStream;
/// All files in the package
FileInfos files;
/// Filestream for reading zip files
wxFileInputStream* fileStream;
/// Filestream for reading zip files
wxZipInputStream* zipStream;
void loadZipStream();
void openDirectory(bool fast = false);
void openSubdir(const String&);
void openZipfile();
void reopen();
void removeTempFiles(bool remove_unused);
void clearKeepFlag();
void saveToZipfile(const String&, bool remove_unused, bool is_copy);
void saveToDirectory(const String&, bool remove_unused, bool is_copy);
FileInfos::iterator addFile(const String& file);
void loadZipStream();
void openDirectory(bool fast = false);
void openSubdir(const String&);
void openZipfile();
void reopen();
void removeTempFiles(bool remove_unused);
void clearKeepFlag();
void saveToZipfile(const String&, bool remove_unused, bool is_copy);
void saveToDirectory(const String&, bool remove_unused, bool is_copy);
FileInfos::iterator addFile(const String& file);
};
// ----------------------------------------------------------------------------- : Packaged
@@ -204,11 +204,11 @@ class Package : public IntrusivePtrVirtualBase {
/// Dependencies of a package
class PackageDependency : public IntrusivePtrBase<PackageDependency> {
public:
String package; ///< Name of the package someone depends on
vector<String> suggests; ///< Packages suggested to fulfill this dependency
Version version; ///< Minimal required version of that package
String package; ///< Name of the package someone depends on
vector<String> suggests; ///< Packages suggested to fulfill this dependency
Version version; ///< Minimal required version of that package
DECLARE_REFLECTION();
DECLARE_REFLECTION();
};
/// Utility class for data types that are always stored in a package.
@@ -216,53 +216,53 @@ class PackageDependency : public IntrusivePtrBase<PackageDependency> {
*/
class Packaged : public Package {
public:
Packaged();
virtual ~Packaged() {}
Packaged();
virtual ~Packaged() {}
Version version; ///< Version number of this package
Version compatible_version; ///< Earliest version number this package is compatible with
String installer_group; ///< Group to place this package in in the installer
String short_name; ///< Short name of this package
String full_name; ///< Name of this package, for menus etc.
String icon_filename; ///< Filename of icon to use in package lists
vector<PackageDependencyP> dependencies; ///< Dependencies of this package
int position_hint; ///< A hint for the package list
Version version; ///< Version number of this package
Version compatible_version; ///< Earliest version number this package is compatible with
String installer_group; ///< Group to place this package in in the installer
String short_name; ///< Short name of this package
String full_name; ///< Name of this package, for menus etc.
String icon_filename; ///< Filename of icon to use in package lists
vector<PackageDependencyP> dependencies; ///< Dependencies of this package
int position_hint; ///< A hint for the package list
/// Get an input stream for the package icon, if there is any
InputStreamP openIconFile();
/// Get an input stream for the package icon, if there is any
InputStreamP openIconFile();
/// Open a package, and read the data
/** if just_header is true, then the package is not fully parsed.
*/
void open(const String& package, bool just_header = false);
/// Ensure the package is fully loaded.
void loadFully();
void save();
void saveAs(const String& package, bool remove_unused = true);
void saveCopy(const String& package);
/// Open a package, and read the data
/** if just_header is true, then the package is not fully parsed.
*/
void open(const String& package, bool just_header = false);
/// Ensure the package is fully loaded.
void loadFully();
void save();
void saveAs(const String& package, bool remove_unused = true);
void saveCopy(const String& package);
/// Check if this package lists a dependency on the given package
/** This is done to force people to fill in the dependencies */
void requireDependency(Packaged* package);
/// Check if this package lists a dependency on the given package
/** This is done to force people to fill in the dependencies */
void requireDependency(Packaged* package);
inline bool isFullyLoaded () const {
return fully_loaded;
}
inline bool isFullyLoaded () const {
return fully_loaded;
}
protected:
/// filename of the data file, and extension of the package file
virtual String typeName() const = 0;
/// Can be overloaded to do validation after loading
virtual void validate(Version file_app_version);
/// What file version should be used for writing files?
virtual Version fileVersion() const = 0;
/// filename of the data file, and extension of the package file
virtual String typeName() const = 0;
/// Can be overloaded to do validation after loading
virtual void validate(Version file_app_version);
/// What file version should be used for writing files?
virtual Version fileVersion() const = 0;
DECLARE_REFLECTION_VIRTUAL();
DECLARE_REFLECTION_VIRTUAL();
private:
bool fully_loaded; ///< Is the package fully loaded?
friend struct JustAsPackageProxy;
friend class Installer;
bool fully_loaded; ///< Is the package fully loaded?
friend struct JustAsPackageProxy;
friend class Installer;
};
// ----------------------------------------------------------------------------- : IncludePackage
@@ -270,9 +270,9 @@ class Packaged : public Package {
/// A package that just contains a bunch of files that are used from other packages
class IncludePackage : public Packaged {
protected:
String typeName() const;
Version fileVersion() const;
DECLARE_REFLECTION();
String typeName() const;
Version fileVersion() const;
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Utility
@@ -280,9 +280,9 @@ class IncludePackage : public Packaged {
/// Open a package with the given filename
template <typename T>
intrusive_ptr<T> open_package(const String& filename) {
intrusive_ptr<T> package(new T);
package->open(filename);
return package;
intrusive_ptr<T> package(new T);
package->open(filename);
return package;
}
// ----------------------------------------------------------------------------- : readFile definition
@@ -291,12 +291,12 @@ intrusive_ptr<T> open_package(const String& filename) {
template <typename T>
inline void Package::readFile(const String& file, T& obj)
{
Reader reader(openIn(file), dynamic_cast<Packaged*>(this), absoluteFilename() + _("/") + file);
try {
reader.handle_greedy(obj);
} catch (const ParseError& err) {
throw FileParseError(err.what(), absoluteFilename() + _("/") + file); // more detailed message
}
Reader reader(openIn(file), dynamic_cast<Packaged*>(this), absoluteFilename() + _("/") + file);
try {
reader.handle_greedy(obj);
} catch (const ParseError& err) {
throw FileParseError(err.what(), absoluteFilename() + _("/") + file); // more detailed message
}
}
// ----------------------------------------------------------------------------- : EOF
+371 -371
View File
@@ -29,471 +29,471 @@ PackageManager package_manager;
void PackageManager::init() {
local.init(true);
global.init(false);
if (!(local.valid() || global.valid()))
throw Error(_("The MSE data files can not be found, there should be a directory called 'data' with these files. ")
_("The expected place to find it in was either ") + wxStandardPaths::Get().GetDataDir() + _(" or ") +
wxStandardPaths::Get().GetUserDataDir());
local.init(true);
global.init(false);
if (!(local.valid() || global.valid()))
throw Error(_("The MSE data files can not be found, there should be a directory called 'data' with these files. ")
_("The expected place to find it in was either ") + wxStandardPaths::Get().GetDataDir() + _(" or ") +
wxStandardPaths::Get().GetUserDataDir());
}
void PackageManager::destroy() {
loaded_packages.clear();
loaded_packages.clear();
}
void PackageManager::reset() {
loaded_packages.clear();
loaded_packages.clear();
}
PackagedP PackageManager::openAny(const String& name_, bool just_header) {
String name = trim(name_);
if (starts_with(name,_("/"))) name = name.substr(1);
if (starts_with(name,_(":NO-WARN-DEP:"))) name = name.substr(13);
// Attempt to load local data first.
String filename;
if (wxFileName(name).IsRelative()) {
// local data dir?
filename = normalize_filename(local.name(name));
if (!wxFileExists(filename) && !wxDirExists(filename)) {
// global data dir
filename = normalize_filename(global.name(name));
}
} else { // Absolute filename
filename = normalize_filename(name);
}
String name = trim(name_);
if (starts_with(name,_("/"))) name = name.substr(1);
if (starts_with(name,_(":NO-WARN-DEP:"))) name = name.substr(13);
// Attempt to load local data first.
String filename;
if (wxFileName(name).IsRelative()) {
// local data dir?
filename = normalize_filename(local.name(name));
if (!wxFileExists(filename) && !wxDirExists(filename)) {
// global data dir
filename = normalize_filename(global.name(name));
}
} else { // Absolute filename
filename = normalize_filename(name);
}
// Is this package already loaded?
PackagedP& p = loaded_packages[filename];
if (!p) {
// load with the right type, based on extension
wxFileName fn(filename);
if (fn.GetExt() == _("mse-game")) p = intrusive(new Game);
else if (fn.GetExt() == _("mse-style")) p = intrusive(new StyleSheet);
else if (fn.GetExt() == _("mse-locale")) p = intrusive(new Locale);
else if (fn.GetExt() == _("mse-include")) p = intrusive(new IncludePackage);
else if (fn.GetExt() == _("mse-symbol-font")) p = intrusive(new SymbolFont);
else if (fn.GetExt() == _("mse-export-template")) p = intrusive(new ExportTemplate);
else {
throw PackageError(_("Unrecognized package type: '") + fn.GetExt() + _("'\nwhile trying to open: ") + name);
}
p->open(filename, just_header);
} else if (!just_header) {
p->loadFully();
}
return p;
// Is this package already loaded?
PackagedP& p = loaded_packages[filename];
if (!p) {
// load with the right type, based on extension
wxFileName fn(filename);
if (fn.GetExt() == _("mse-game")) p = intrusive(new Game);
else if (fn.GetExt() == _("mse-style")) p = intrusive(new StyleSheet);
else if (fn.GetExt() == _("mse-locale")) p = intrusive(new Locale);
else if (fn.GetExt() == _("mse-include")) p = intrusive(new IncludePackage);
else if (fn.GetExt() == _("mse-symbol-font")) p = intrusive(new SymbolFont);
else if (fn.GetExt() == _("mse-export-template")) p = intrusive(new ExportTemplate);
else {
throw PackageError(_("Unrecognized package type: '") + fn.GetExt() + _("'\nwhile trying to open: ") + name);
}
p->open(filename, just_header);
} else if (!just_header) {
p->loadFully();
}
return p;
}
void PackageManager::findMatching(const String& pattern, vector<PackagedP>& out) {
// first find local packages
String file = local.findFirstMatching(pattern);
while (!file.empty()) {
out.push_back(openAny(file, true));
file = wxFindNextFile();
}
// then global packages not already in the list
file = global.findFirstMatching(pattern);
while (!file.empty()) {
PackagedP p = openAny(file, true);
if (find(out.begin(), out.end(), p) == out.end()) {
out.push_back(p);
}
file = wxFindNextFile();
}
// first find local packages
String file = local.findFirstMatching(pattern);
while (!file.empty()) {
out.push_back(openAny(file, true));
file = wxFindNextFile();
}
// then global packages not already in the list
file = global.findFirstMatching(pattern);
while (!file.empty()) {
PackagedP p = openAny(file, true);
if (find(out.begin(), out.end(), p) == out.end()) {
out.push_back(p);
}
file = wxFindNextFile();
}
}
InputStreamP PackageManager::openFileFromPackage(Packaged*& package, const String& name) {
if (!name.empty() && name.GetChar(0) == _('/')) {
// absolute name; break name
size_t start = name.find_first_not_of(_("/\\"), 1); // allow "//package/name" from incorrect scripts
size_t pos = name.find_first_of(_("/\\"), start);
if (start < pos && pos != String::npos) {
// open package
PackagedP p = openAny(name.substr(start, pos-start));
if (package && !is_substr(name,start,_(":NO-WARN-DEP:"))) {
package->requireDependency(p.get());
}
package = p.get();
return p->openIn(name.substr(pos + 1));
}
} else if (package) {
// relative name
return package->openIn(name);
}
throw FileNotFoundError(name, _("No package name specified, use '/package/filename'"));
if (!name.empty() && name.GetChar(0) == _('/')) {
// absolute name; break name
size_t start = name.find_first_not_of(_("/\\"), 1); // allow "//package/name" from incorrect scripts
size_t pos = name.find_first_of(_("/\\"), start);
if (start < pos && pos != String::npos) {
// open package
PackagedP p = openAny(name.substr(start, pos-start));
if (package && !is_substr(name,start,_(":NO-WARN-DEP:"))) {
package->requireDependency(p.get());
}
package = p.get();
return p->openIn(name.substr(pos + 1));
}
} else if (package) {
// relative name
return package->openIn(name);
}
throw FileNotFoundError(name, _("No package name specified, use '/package/filename'"));
}
String PackageManager::openFilenameFromPackage(Packaged*& package, const String& name) {
if (!name.empty() && name.GetChar(0) == _('/')) {
// absolute name; break name
size_t start = name.find_first_not_of(_("/\\"), 1); // allow "//package/name" from incorrect scripts
size_t pos = name.find_first_of(_("/\\"), start);
if (start < pos && pos != String::npos) {
// open package
PackagedP p = openAny(name.substr(start, pos-start));
if (package && !is_substr(name,start,_(":NO-WARN-DEP:"))) {
package->requireDependency(p.get());
}
package = p.get();
return p->absoluteFilename() + _("/") + name.substr(pos + 1);
}
} else if (package) {
// relative name
return package->absoluteFilename() + _("/") + name;
}
throw FileNotFoundError(name, _("No package name specified, use '/package/filename'"));
if (!name.empty() && name.GetChar(0) == _('/')) {
// absolute name; break name
size_t start = name.find_first_not_of(_("/\\"), 1); // allow "//package/name" from incorrect scripts
size_t pos = name.find_first_of(_("/\\"), start);
if (start < pos && pos != String::npos) {
// open package
PackagedP p = openAny(name.substr(start, pos-start));
if (package && !is_substr(name,start,_(":NO-WARN-DEP:"))) {
package->requireDependency(p.get());
}
package = p.get();
return p->absoluteFilename() + _("/") + name.substr(pos + 1);
}
} else if (package) {
// relative name
return package->absoluteFilename() + _("/") + name;
}
throw FileNotFoundError(name, _("No package name specified, use '/package/filename'"));
}
String PackageManager::getDictionaryDir(bool l) const {
String dir = (l ? local : global).getDirectory();
if (dir.empty()) return wxEmptyString;
else return dir + _("/dictionaries/");
String dir = (l ? local : global).getDirectory();
if (dir.empty()) return wxEmptyString;
else return dir + _("/dictionaries/");
}
// ----------------------------------------------------------------------------- : PackageManager : on disk
bool PackageManager::checkDependency(const PackageDependency& dep, bool report_errors) {
// mse package?
if (dep.package == mse_package) {
if (app_version < dep.version) {
queue_message(MESSAGE_WARNING, _ERROR_3_("package out of date", _("Magic Set Editor"), app_version.toString(), dep.version.toString()));
}
return true;
}
// does the package exist?
if (!local.exists(dep.package) && !global.exists(dep.package)) {
if (report_errors)
queue_message(MESSAGE_WARNING, _ERROR_1_("package not found", dep.package));
return false;
}
PackagedP package = openAny(dep.package, true);
if (package->version < dep.version) {
if (report_errors)
queue_message(MESSAGE_WARNING, _ERROR_3_("package out of date", dep.package, package->version.toString(), dep.version.toString()));
return false;
}
return true;
// mse package?
if (dep.package == mse_package) {
if (app_version < dep.version) {
queue_message(MESSAGE_WARNING, _ERROR_3_("package out of date", _("Magic Set Editor"), app_version.toString(), dep.version.toString()));
}
return true;
}
// does the package exist?
if (!local.exists(dep.package) && !global.exists(dep.package)) {
if (report_errors)
queue_message(MESSAGE_WARNING, _ERROR_1_("package not found", dep.package));
return false;
}
PackagedP package = openAny(dep.package, true);
if (package->version < dep.version) {
if (report_errors)
queue_message(MESSAGE_WARNING, _ERROR_3_("package out of date", dep.package, package->version.toString(), dep.version.toString()));
return false;
}
return true;
}
bool PackageManager::installedVersion(const String& package_name, Version& version_out) {
if (package_name == mse_package) {
version_out = app_version;
return true;
} else {
if (!local.exists(package_name) && !global.exists(package_name)) return false;
PackagedP package = openAny(package_name, true);
version_out = package->version;
return true;
}
if (package_name == mse_package) {
version_out = app_version;
return true;
} else {
if (!local.exists(package_name) && !global.exists(package_name)) return false;
PackagedP package = openAny(package_name, true);
version_out = package->version;
return true;
}
}
void PackageManager::findAllInstalledPackages(vector<InstallablePackageP>& packages) {
// from directories
vector<InstallablePackageP> more_packages;
global.installedPackages(packages);
local.installedPackages(more_packages);
merge(packages, more_packages);
// the magic appliation package
packages.push_back(mse_installable_package());
// invariant: sorted:
sort(packages);
// from directories
vector<InstallablePackageP> more_packages;
global.installedPackages(packages);
local.installedPackages(more_packages);
merge(packages, more_packages);
// the magic appliation package
packages.push_back(mse_installable_package());
// invariant: sorted:
sort(packages);
}
bool PackageManager::install(const InstallablePackage& package) {
bool install_local = package.has(PACKAGE_ACT_LOCAL);
return (install_local ? local : global).install(package);
bool install_local = package.has(PACKAGE_ACT_LOCAL);
return (install_local ? local : global).install(package);
}
// ----------------------------------------------------------------------------- : PackageDirectory
void PackageDirectory::init(bool local) {
is_local = local;
if (local) {
init(wxStandardPaths::Get().GetUserDataDir() + _("/data"));
} else {
// determine data directory
String dir = wxStandardPaths::Get().GetDataDir();
// check if this is the actual data directory, especially during debugging,
// the data may be higher up:
// exe path = mse/build/debug/mse.exe
// data path = mse/data
while (!wxDirExists(dir + _("/data"))) {
String d = dir;
dir = wxPathOnly(dir);
if (d == dir) {
// we are at the root -> 'data' not found anywhere in the path
dir = wxStandardPaths::Get().GetDataDir();
break;
}
}
init(dir + _("/data"));
}
is_local = local;
if (local) {
init(wxStandardPaths::Get().GetUserDataDir() + _("/data"));
} else {
// determine data directory
String dir = wxStandardPaths::Get().GetDataDir();
// check if this is the actual data directory, especially during debugging,
// the data may be higher up:
// exe path = mse/build/debug/mse.exe
// data path = mse/data
while (!wxDirExists(dir + _("/data"))) {
String d = dir;
dir = wxPathOnly(dir);
if (d == dir) {
// we are at the root -> 'data' not found anywhere in the path
dir = wxStandardPaths::Get().GetDataDir();
break;
}
}
init(dir + _("/data"));
}
}
void PackageDirectory::init(const String& dir) {
if (wxDirExists(dir))
directory = dir;
else
directory.clear();
if (wxDirExists(dir))
directory = dir;
else
directory.clear();
}
String PackageDirectory::name(const String& name) const {
return directory + _("/") + name;
return directory + _("/") + name;
}
bool PackageDirectory::exists(const String& filename) const {
String fn = name(filename);
return wxFileExists(fn) || wxDirExists(fn);
String fn = name(filename);
return wxFileExists(fn) || wxDirExists(fn);
}
String PackageDirectory::findFirstMatching(const String& pattern) const {
if (!wxDirExists(directory)) return String();
return wxFindFirstFile(directory + _("/") + pattern, 0);
if (!wxDirExists(directory)) return String();
return wxFindFirstFile(directory + _("/") + pattern, 0);
}
bool compare_name(const PackageVersionP& a, const PackageVersionP& b) {
return a->name < b->name;
return a->name < b->name;
}
void PackageDirectory::installedPackages(vector<InstallablePackageP>& packages_out) {
loadDatabase();
// find all package files
vector<String> in_dir;
for (String s = findFirstMatching(_("*.mse-*")) ; !s.empty() ; s = wxFindNextFile()) {
size_t pos = s.find_last_of(_("/\\"));
if (pos != String::npos) s = s.substr(pos+1);
// TODO : check for valid package names
in_dir.push_back(s);
}
sort(in_dir.begin(), in_dir.end());
// merge with package database
bool db_changed = false;
vector<PackageVersionP>::const_iterator it1 = packages.begin();
vector<String>::const_iterator it2 = in_dir.begin();
while (it2 != in_dir.end()) {
if (it1 == packages.end() || (*it1)->name > *it2) {
// add new package to db
try {
PackagedP pack = package_manager.openAny(*it2, true);
db_changed = true;
PackageVersionP ver(new PackageVersion(
is_local ? PackageVersion::STATUS_LOCAL : PackageVersion::STATUS_GLOBAL));
ver->check_status(*pack);
packages_out.push_back(intrusive(new InstallablePackage(intrusive(new PackageDescription(*pack)), ver)));
} catch (const Error&) {}
++it2;
} else if ((*it1)->name < *it2) {
// delete package from db
db_changed = true;
++it1;
} else {
// ok, a package already in the db
try {
PackagedP pack = package_manager.openAny(*it2, true);
(*it1)->check_status(*pack);
packages_out.push_back(intrusive(new InstallablePackage(intrusive(new PackageDescription(*pack)), *it1)));
} catch (const Error&) { db_changed = true; }
++it1, ++it2;
}
}
if (it1 != packages.end()) db_changed = true;
// has the database of installed packages changed?
if (db_changed) {
packages.clear();
FOR_EACH(p, packages_out) {
if (p->installed) packages.push_back(p->installed);
}
saveDatabase();
}
loadDatabase();
// find all package files
vector<String> in_dir;
for (String s = findFirstMatching(_("*.mse-*")) ; !s.empty() ; s = wxFindNextFile()) {
size_t pos = s.find_last_of(_("/\\"));
if (pos != String::npos) s = s.substr(pos+1);
// TODO : check for valid package names
in_dir.push_back(s);
}
sort(in_dir.begin(), in_dir.end());
// merge with package database
bool db_changed = false;
vector<PackageVersionP>::const_iterator it1 = packages.begin();
vector<String>::const_iterator it2 = in_dir.begin();
while (it2 != in_dir.end()) {
if (it1 == packages.end() || (*it1)->name > *it2) {
// add new package to db
try {
PackagedP pack = package_manager.openAny(*it2, true);
db_changed = true;
PackageVersionP ver(new PackageVersion(
is_local ? PackageVersion::STATUS_LOCAL : PackageVersion::STATUS_GLOBAL));
ver->check_status(*pack);
packages_out.push_back(intrusive(new InstallablePackage(intrusive(new PackageDescription(*pack)), ver)));
} catch (const Error&) {}
++it2;
} else if ((*it1)->name < *it2) {
// delete package from db
db_changed = true;
++it1;
} else {
// ok, a package already in the db
try {
PackagedP pack = package_manager.openAny(*it2, true);
(*it1)->check_status(*pack);
packages_out.push_back(intrusive(new InstallablePackage(intrusive(new PackageDescription(*pack)), *it1)));
} catch (const Error&) { db_changed = true; }
++it1, ++it2;
}
}
if (it1 != packages.end()) db_changed = true;
// has the database of installed packages changed?
if (db_changed) {
packages.clear();
FOR_EACH(p, packages_out) {
if (p->installed) packages.push_back(p->installed);
}
saveDatabase();
}
}
void PackageDirectory::bless(const String& package_name) {
PackagedP pack = package_manager.openAny(package_name, true);
// already have this package?
FOR_EACH(ver, packages) {
if (ver->name == package_name) {
ver->check_status(*pack);
ver->bless();
return;
}
}
// a new package
PackageVersionP ver(new PackageVersion(
is_local ? PackageVersion::STATUS_LOCAL : PackageVersion::STATUS_GLOBAL));
ver->check_status(*pack);
ver->bless();
packages.push_back(ver);
sort(packages.begin(), packages.end(), compare_name);
PackagedP pack = package_manager.openAny(package_name, true);
// already have this package?
FOR_EACH(ver, packages) {
if (ver->name == package_name) {
ver->check_status(*pack);
ver->bless();
return;
}
}
// a new package
PackageVersionP ver(new PackageVersion(
is_local ? PackageVersion::STATUS_LOCAL : PackageVersion::STATUS_GLOBAL));
ver->check_status(*pack);
ver->bless();
packages.push_back(ver);
sort(packages.begin(), packages.end(), compare_name);
}
void PackageDirectory::removeFromDatabase(const String& package_name) {
size_t i = 0, j = 0;
for ( ; i < packages.size() ; ++i) {
if (packages[i]->name != package_name) {
packages[j++] = packages[i];
}
}
packages.resize(j);
size_t i = 0, j = 0;
for ( ; i < packages.size() ; ++i) {
if (packages[i]->name != package_name) {
packages[j++] = packages[i];
}
}
packages.resize(j);
}
IMPLEMENT_REFLECTION(PackageDirectory) {
REFLECT(packages);
REFLECT(packages);
}
void PackageDirectory::loadDatabase() {
if (!packages.empty()) return;
String filename = databaseFile();
if (wxFileExists(filename)) {
// packages file not existing is not an error
shared_ptr<wxFileInputStream> file = shared(new wxFileInputStream(filename));
if (!file->Ok()) return; // failure is not an error
Reader reader(file, nullptr, filename);
reader.handle_greedy(*this);
sort(packages.begin(), packages.end(), compare_name);
}
if (!packages.empty()) return;
String filename = databaseFile();
if (wxFileExists(filename)) {
// packages file not existing is not an error
shared_ptr<wxFileInputStream> file = shared(new wxFileInputStream(filename));
if (!file->Ok()) return; // failure is not an error
Reader reader(file, nullptr, filename);
reader.handle_greedy(*this);
sort(packages.begin(), packages.end(), compare_name);
}
}
void PackageDirectory::saveDatabase() {
Writer writer(shared(new wxFileOutputStream(databaseFile())), app_version);
writer.handle(*this);
Writer writer(shared(new wxFileOutputStream(databaseFile())), app_version);
writer.handle(*this);
}
String PackageDirectory::databaseFile() {
return name(_("packages"));
return name(_("packages"));
}
// ----------------------------------------------------------------------------- : PackageDirectory : installing
bool PackageDirectory::install(const InstallablePackage& package) {
String n = name(package.description->name);
if (package.action & PACKAGE_ACT_REMOVE) {
if (!remove_file_or_dir(n)) return false;
removeFromDatabase(package.description->name);
} else if (package.action & PACKAGE_ACT_INSTALL) {
if (!remove_file_or_dir(n + _(".new"))) return false;
bool ok = actual_install(package, n + _(".new"));
if (!ok) return false;
move_ignored_files(n, n + _(".new")); // copy over files from the old installed version to the new one
if (!remove_file_or_dir(n)) return false;
if (!rename_file_or_dir(n + _(".new"), n)) return false;
bless(package.description->name);
}
saveDatabase();
return true;
String n = name(package.description->name);
if (package.action & PACKAGE_ACT_REMOVE) {
if (!remove_file_or_dir(n)) return false;
removeFromDatabase(package.description->name);
} else if (package.action & PACKAGE_ACT_INSTALL) {
if (!remove_file_or_dir(n + _(".new"))) return false;
bool ok = actual_install(package, n + _(".new"));
if (!ok) return false;
move_ignored_files(n, n + _(".new")); // copy over files from the old installed version to the new one
if (!remove_file_or_dir(n)) return false;
if (!rename_file_or_dir(n + _(".new"), n)) return false;
bless(package.description->name);
}
saveDatabase();
return true;
}
bool PackageDirectory::actual_install(const InstallablePackage& package, const String& install_dir) {
String name = package.description->name;
if (!package.installer->installer) {
queue_message(MESSAGE_ERROR, _("Installer not found for package: ") + name);
return false;
}
Installer& installer = *package.installer->installer;
// install files
const Packaged::FileInfos& file_infos = installer.getFileInfos();
for (Packaged::FileInfos::const_iterator it = file_infos.begin() ; it != file_infos.end() ; ++it) {
String file = it->first;
if (!is_substr_i(file,0,name)) continue; // not the right package
// correct filename
String local_file = install_dir + file.substr(name.length());
create_parent_dirs(local_file);
// copy file
InputStreamP is = installer.openIn(file);
wxFileOutputStream os (local_file);
if (!os.IsOk()) {
int act = wxMessageBox(_ERROR_1_("cannot create file", file), _TITLE_("cannot create file"), wxICON_ERROR | wxYES_NO);
if (act == wxNO) return false;
}
os.Write(*is);
}
// update package database
// TODO: bless the package?
return true;
String name = package.description->name;
if (!package.installer->installer) {
queue_message(MESSAGE_ERROR, _("Installer not found for package: ") + name);
return false;
}
Installer& installer = *package.installer->installer;
// install files
const Packaged::FileInfos& file_infos = installer.getFileInfos();
for (Packaged::FileInfos::const_iterator it = file_infos.begin() ; it != file_infos.end() ; ++it) {
String file = it->first;
if (!is_substr_i(file,0,name)) continue; // not the right package
// correct filename
String local_file = install_dir + file.substr(name.length());
create_parent_dirs(local_file);
// copy file
InputStreamP is = installer.openIn(file);
wxFileOutputStream os (local_file);
if (!os.IsOk()) {
int act = wxMessageBox(_ERROR_1_("cannot create file", file), _TITLE_("cannot create file"), wxICON_ERROR | wxYES_NO);
if (act == wxNO) return false;
}
os.Write(*is);
}
// update package database
// TODO: bless the package?
return true;
}
// ----------------------------------------------------------------------------- : PackageVersion
template <> void Writer::handle(const PackageVersion::FileInfo& f) {
if (f.status == PackageVersion::FILE_DELETED) {
handle(_("D ") + f.file);
} else {
handle(format_string(_("%s%s %s"),
f.status == PackageVersion::FILE_ADDED ? _("A")
: f.status == PackageVersion::FILE_MODIFIED ? _("M") : _(""),
f.time.Format(_("%Y%m%dT%H%M%S")),
f.file));
}
if (f.status == PackageVersion::FILE_DELETED) {
handle(_("D ") + f.file);
} else {
handle(format_string(_("%s%s %s"),
f.status == PackageVersion::FILE_ADDED ? _("A")
: f.status == PackageVersion::FILE_MODIFIED ? _("M") : _(""),
f.time.Format(_("%Y%m%dT%H%M%S")),
f.file));
}
}
template <> void Reader::handle(PackageVersion::FileInfo& f) {
String s; handle(s);
// read status
if (s.size() < 2) {f.status = PackageVersion::FILE_ADDED; return; }
f.status = s.GetChar(0) == _('M') ? PackageVersion::FILE_MODIFIED
: s.GetChar(0) == _('A') ? PackageVersion::FILE_ADDED
: s.GetChar(0) == _('D') ? PackageVersion::FILE_DELETED
: PackageVersion::FILE_UNCHANGED;
if (f.status == PackageVersion::FILE_DELETED) {
if (s.GetChar(1) != _(' ')) {f.status = PackageVersion::FILE_ADDED; return; }
f.file = s.substr(2);
return;
} else if (f.status != PackageVersion::FILE_UNCHANGED) {
s = s.substr(1);
}
if (s.size() < 8+1+6+1) {f.status = PackageVersion::FILE_ADDED; return; }
if (s.GetChar(8+1+6) != _(' ')) {f.status = PackageVersion::FILE_ADDED; return; } // invalid format
// read time, filename
f.time.ParseFormat(s, _("%Y%m%dT%H%M%S"));
f.file = s.substr(8+1+6+1);
String s; handle(s);
// read status
if (s.size() < 2) {f.status = PackageVersion::FILE_ADDED; return; }
f.status = s.GetChar(0) == _('M') ? PackageVersion::FILE_MODIFIED
: s.GetChar(0) == _('A') ? PackageVersion::FILE_ADDED
: s.GetChar(0) == _('D') ? PackageVersion::FILE_DELETED
: PackageVersion::FILE_UNCHANGED;
if (f.status == PackageVersion::FILE_DELETED) {
if (s.GetChar(1) != _(' ')) {f.status = PackageVersion::FILE_ADDED; return; }
f.file = s.substr(2);
return;
} else if (f.status != PackageVersion::FILE_UNCHANGED) {
s = s.substr(1);
}
if (s.size() < 8+1+6+1) {f.status = PackageVersion::FILE_ADDED; return; }
if (s.GetChar(8+1+6) != _(' ')) {f.status = PackageVersion::FILE_ADDED; return; } // invalid format
// read time, filename
f.time.ParseFormat(s, _("%Y%m%dT%H%M%S"));
f.file = s.substr(8+1+6+1);
}
IMPLEMENT_REFLECTION_NO_SCRIPT(PackageVersion) {
REFLECT_NO_SCRIPT(name);
REFLECT_NO_SCRIPT(version);
REFLECT_NO_SCRIPT(status);
REFLECT_NO_SCRIPT(files);
REFLECT_NO_SCRIPT(name);
REFLECT_NO_SCRIPT(version);
REFLECT_NO_SCRIPT(status);
REFLECT_NO_SCRIPT(files);
}
void PackageVersion::check_status(Packaged& package) {
status &= ~STATUS_MODIFIED;
if (!(status & STATUS_BLESSED)) status |= STATUS_MODIFIED;
name = package.relativeFilename();
version = package.version;
// Merge our files list with the list from the package
vector<FileInfo> new_files;
Package::FileInfos fis = package.getFileInfos();
Package::FileInfos::const_iterator it1 = fis.begin();
vector<FileInfo>::iterator it2 = files.begin();
//% size_t it2 = 0, size = files.size();
//% bool need_sort = false;
while(it1 != fis.end() || it2 != files.end()) {
if (it1 != fis.end() && it2 != files.end() && it1->first == it2->file) {
DateTime mtime = package.modificationTime(*it1);
if (mtime != it2->time) {
it2->time = mtime;
it2->status = FILE_MODIFIED;
new_files.push_back(*it2);
status |= STATUS_MODIFIED;
}
++it1; ++it2;
} else if (it1 != fis.end() && (it2 == files.end() || it1->first < it2->file)) {
// this is a new file
DateTime mtime = package.modificationTime(*it1);
new_files.push_back(FileInfo(it1->first, mtime, FILE_ADDED));
status |= STATUS_MODIFIED;
++it1;
} else {
// this file is no longer in the package, it was deleted
if (it2->status != FILE_ADDED) {
it2->status = FILE_DELETED;
new_files.push_back(*it2);
status |= STATUS_MODIFIED;
}
++it2;
}
}
swap(files,new_files);
status &= ~STATUS_MODIFIED;
if (!(status & STATUS_BLESSED)) status |= STATUS_MODIFIED;
name = package.relativeFilename();
version = package.version;
// Merge our files list with the list from the package
vector<FileInfo> new_files;
Package::FileInfos fis = package.getFileInfos();
Package::FileInfos::const_iterator it1 = fis.begin();
vector<FileInfo>::iterator it2 = files.begin();
//% size_t it2 = 0, size = files.size();
//% bool need_sort = false;
while(it1 != fis.end() || it2 != files.end()) {
if (it1 != fis.end() && it2 != files.end() && it1->first == it2->file) {
DateTime mtime = package.modificationTime(*it1);
if (mtime != it2->time) {
it2->time = mtime;
it2->status = FILE_MODIFIED;
new_files.push_back(*it2);
status |= STATUS_MODIFIED;
}
++it1; ++it2;
} else if (it1 != fis.end() && (it2 == files.end() || it1->first < it2->file)) {
// this is a new file
DateTime mtime = package.modificationTime(*it1);
new_files.push_back(FileInfo(it1->first, mtime, FILE_ADDED));
status |= STATUS_MODIFIED;
++it1;
} else {
// this file is no longer in the package, it was deleted
if (it2->status != FILE_ADDED) {
it2->status = FILE_DELETED;
new_files.push_back(*it2);
status |= STATUS_MODIFIED;
}
++it2;
}
}
swap(files,new_files);
}
inline bool is_deleted(PackageVersion::FileInfo f) {
return f.status == PackageVersion::FILE_DELETED;
return f.status == PackageVersion::FILE_DELETED;
}
void PackageVersion::bless() {
files.erase(remove_if(files.begin(),files.end(),is_deleted),files.end());
FOR_EACH(f,files) {
f.status = FILE_UNCHANGED;
}
status &= ~STATUS_MODIFIED;
status |= STATUS_BLESSED;
files.erase(remove_if(files.begin(),files.end(),is_deleted),files.end());
FOR_EACH(f,files) {
f.status = FILE_UNCHANGED;
}
status &= ~STATUS_MODIFIED;
status |= STATUS_BLESSED;
}
+169 -169
View File
@@ -25,33 +25,33 @@ class PackageDependency;
/// Information on a package in a repository
class PackageVersionData : public IntrusivePtrVirtualBase {
public:
PackageVersionData() {}
String name; ///< Name of the package
String short_name; ///< Name to show on package list.
String description; ///< html description
bool hidden; ///< Not shown in the package list (installed automatically as a dependency)
PackageVersionP base_version; ///< The locally installed version (if installed)
PackageVersionP local_version; ///< Modifications made to the locally installed version?
PackageVersionP remote_version; ///< The version available from the server (if available)
bool global; ///< The installed package is in the global location, not the user-local one
bool new_global; ///<
bool source_local; ///< Is the source
String source; ///< Where can the package be downloaded/found?
DECLARE_REFLECTION();
PackageVersionData() {}
String name; ///< Name of the package
String short_name; ///< Name to show on package list.
String description; ///< html description
bool hidden; ///< Not shown in the package list (installed automatically as a dependency)
PackageVersionP base_version; ///< The locally installed version (if installed)
PackageVersionP local_version; ///< Modifications made to the locally installed version?
PackageVersionP remote_version; ///< The version available from the server (if available)
bool global; ///< The installed package is in the global location, not the user-local one
bool new_global; ///<
bool source_local; ///< Is the source
String source; ///< Where can the package be downloaded/found?
DECLARE_REFLECTION();
};
class UpdateData {
vector<PackageDependencyP> packages; ///< package/latest-version-number pairs
String new_updates_url; ///< updates url has changed?
vector<PackageDependencyP> packages; ///< package/latest-version-number pairs
String new_updates_url; ///< updates url has changed?
};
IMPLEMENT_REFLECTION_NO_SCRIPT(UpdateData) {
REFLECT_NO_SCRIPT(packages);
REFLECT_NO_SCRIPT(new_updates_url);
REFLECT_NO_SCRIPT(packages);
REFLECT_NO_SCRIPT(new_updates_url);
}
*/
@@ -60,45 +60,45 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(UpdateData) {
/// A directory for packages
class PackageDirectory {
public:
void init(bool local);
void init(const String& dir);
bool valid() const { return !directory.empty(); }
/// Name of a package in this directory
String name(const String& name) const;
/// Does a package with the given name exist?
bool exists(const String& name) const;
/// Get the package directory
inline String getDirectory() const { return directory; }
/// Find all packages that match a filename pattern (using wxFindFirst)
String findFirstMatching(const String& pattern) const;
/// Get all installed packages
void installedPackages(vector<InstallablePackageP>& packages);
/// Install/uninstall a package
bool install(const InstallablePackage& package);
/// Bless a package
void bless(const String& package_name);
/// Remove a package from the database
void removeFromDatabase(const String& package_name);
void loadDatabase();
void saveDatabase();
void init(bool local);
void init(const String& dir);
bool valid() const { return !directory.empty(); }
/// Name of a package in this directory
String name(const String& name) const;
/// Does a package with the given name exist?
bool exists(const String& name) const;
/// Get the package directory
inline String getDirectory() const { return directory; }
/// Find all packages that match a filename pattern (using wxFindFirst)
String findFirstMatching(const String& pattern) const;
/// Get all installed packages
void installedPackages(vector<InstallablePackageP>& packages);
/// Install/uninstall a package
bool install(const InstallablePackage& package);
/// Bless a package
void bless(const String& package_name);
/// Remove a package from the database
void removeFromDatabase(const String& package_name);
void loadDatabase();
void saveDatabase();
private:
bool is_local;
String directory;
vector<PackageVersionP> packages; // sorted by name
String databaseFile();
// Do the actual installation of a package
bool actual_install(const InstallablePackage& package, const String& install_dir);
DECLARE_REFLECTION();
bool is_local;
String directory;
vector<PackageVersionP> packages; // sorted by name
String databaseFile();
// Do the actual installation of a package
bool actual_install(const InstallablePackage& package, const String& install_dir);
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : PackageManager
@@ -109,77 +109,77 @@ class PackageDirectory {
*/
class PackageManager {
public:
/// Initialize the package manager
void init();
/// Empty the list of packages.
/** This function MUST be called before the program terminates, otherwise
* we could get into fights with pool allocators used by ScriptValues */
void destroy();
/// Empty the list of packages, they will all be reloaded
void reset();
// --------------------------------------------------- : Packages in memory
/// Open a package with the specified name (including extension)
template <typename T>
intrusive_ptr<T> open(const String& name) {
PackagedP p = openAny(name);
intrusive_ptr<T> typedP = dynamic_pointer_cast<T>(p);
if (typedP) {
return typedP;
} else {
throw InternalError(format_string(_("Package %s loaded as wrong type"),name));
}
}
/// Open a package with the specified name, the type of package is determined by its extension!
/** @param if just_header is true, then the package is not fully parsed.
*/
PackagedP openAny(const String& name, bool just_header = false);
/// Find all packages that match a filename pattern, store them in out
/** Only reads the package headers */
void findMatching(const String& pattern, vector<PackagedP>& out);
/// Open a file from a package, with a name encoded as "/package/file"
/** If 'package' is set then:
* - tries to open a relative file from the package if the name is "file"
* - verifies a dependency from that package if an absolute filename is used
* this is to force people to fill in the dependencies
* Afterwards, package will be set to the package the file is opened from
*/
InputStreamP openFileFromPackage(Packaged*& package, const String& name);
/// Get a filename to open from a package
/** WARNING: this is a bit of a hack, since not all package types support names in this way.
* It is needed for third party libraries (i.e. hunspell) that load stuff from files.
*/
String openFilenameFromPackage(Packaged*& package, const String& name);
// --------------------------------------------------- : Packages on disk
/// Check if the given dependency is currently installed
bool checkDependency(const PackageDependency& dep, bool report_errors = true);
/// Determine the latest version of the given package
/** If it is not installed, returns false */
bool installedVersion(const String& pkg, Version& version_out);
/// Get all installed packages
void findAllInstalledPackages(vector<InstallablePackageP>& packages);
/// Install/uninstall a package, returns success
bool install(const InstallablePackage& package);
// --------------------------------------------------- : Other package like things
/// Get the directory for dictionary files
String getDictionaryDir(bool local) const;
// --------------------------------------------------- : Packages on a server
/// Initialize the package manager
void init();
/// Empty the list of packages.
/** This function MUST be called before the program terminates, otherwise
* we could get into fights with pool allocators used by ScriptValues */
void destroy();
/// Empty the list of packages, they will all be reloaded
void reset();
// --------------------------------------------------- : Packages in memory
/// Open a package with the specified name (including extension)
template <typename T>
intrusive_ptr<T> open(const String& name) {
PackagedP p = openAny(name);
intrusive_ptr<T> typedP = dynamic_pointer_cast<T>(p);
if (typedP) {
return typedP;
} else {
throw InternalError(format_string(_("Package %s loaded as wrong type"),name));
}
}
/// Open a package with the specified name, the type of package is determined by its extension!
/** @param if just_header is true, then the package is not fully parsed.
*/
PackagedP openAny(const String& name, bool just_header = false);
/// Find all packages that match a filename pattern, store them in out
/** Only reads the package headers */
void findMatching(const String& pattern, vector<PackagedP>& out);
/// Open a file from a package, with a name encoded as "/package/file"
/** If 'package' is set then:
* - tries to open a relative file from the package if the name is "file"
* - verifies a dependency from that package if an absolute filename is used
* this is to force people to fill in the dependencies
* Afterwards, package will be set to the package the file is opened from
*/
InputStreamP openFileFromPackage(Packaged*& package, const String& name);
/// Get a filename to open from a package
/** WARNING: this is a bit of a hack, since not all package types support names in this way.
* It is needed for third party libraries (i.e. hunspell) that load stuff from files.
*/
String openFilenameFromPackage(Packaged*& package, const String& name);
// --------------------------------------------------- : Packages on disk
/// Check if the given dependency is currently installed
bool checkDependency(const PackageDependency& dep, bool report_errors = true);
/// Determine the latest version of the given package
/** If it is not installed, returns false */
bool installedVersion(const String& pkg, Version& version_out);
/// Get all installed packages
void findAllInstalledPackages(vector<InstallablePackageP>& packages);
/// Install/uninstall a package, returns success
bool install(const InstallablePackage& package);
// --------------------------------------------------- : Other package like things
/// Get the directory for dictionary files
String getDictionaryDir(bool local) const;
// --------------------------------------------------- : Packages on a server
private:
map<String, PackagedP> loaded_packages;
PackageDirectory local, global;
map<String, PackagedP> loaded_packages;
PackageDirectory local, global;
};
/// The global PackageManager instance
@@ -190,48 +190,48 @@ extern PackageManager package_manager;
/// Version information for an installed package
class PackageVersion : public IntrusivePtrBase<PackageVersion> {
public:
PackageVersion() : status(0) {}
PackageVersion(int status) : status(status) {}
String name;
Version version;
vector<PackageDependencyP> dependencies;
enum Status
{ STATUS_LOCAL = 0x01 ///< Package is installed in the local package dir
, STATUS_GLOBAL = 0x02 ///< Package is installed in the global package dir
, STATUS_BLESSED = 0x10 ///< Package is an official version, installed with the installer
, STATUS_MODIFIED = 0x20 ///< Package has been modified after installing
, STATUS_FIXED = 0x40 ///< The package can not be uninstalled
};
int status;
/// Check the status of the files in this package
void check_status(Packaged& package);
/// Set blessed status to true
void bless();
PackageVersion() : status(0) {}
PackageVersion(int status) : status(status) {}
String name;
Version version;
vector<PackageDependencyP> dependencies;
enum Status
{ STATUS_LOCAL = 0x01 ///< Package is installed in the local package dir
, STATUS_GLOBAL = 0x02 ///< Package is installed in the global package dir
, STATUS_BLESSED = 0x10 ///< Package is an official version, installed with the installer
, STATUS_MODIFIED = 0x20 ///< Package has been modified after installing
, STATUS_FIXED = 0x40 ///< The package can not be uninstalled
};
int status;
/// Check the status of the files in this package
void check_status(Packaged& package);
/// Set blessed status to true
void bless();
public:
/// Status of a single file
enum FileStatus
{ FILE_UNCHANGED
, FILE_MODIFIED
, FILE_ADDED
, FILE_DELETED
};
/// Information on files in this package
struct FileInfo {
inline FileInfo() {}
inline FileInfo(const String& file, const DateTime& time, FileStatus status)
: file(file), time(time), status(status)
{}
String file;
DateTime time;
FileStatus status;
inline bool operator < (const FileInfo& f) const { return file < f.file; }
};
/// Status of a single file
enum FileStatus
{ FILE_UNCHANGED
, FILE_MODIFIED
, FILE_ADDED
, FILE_DELETED
};
/// Information on files in this package
struct FileInfo {
inline FileInfo() {}
inline FileInfo(const String& file, const DateTime& time, FileStatus status)
: file(file), time(time), status(status)
{}
String file;
DateTime time;
FileStatus status;
inline bool operator < (const FileInfo& f) const { return file < f.file; }
};
private:
vector<FileInfo> files; // sorted by filename
DECLARE_REFLECTION();
vector<FileInfo> files; // sorted by filename
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : EOF
+332 -332
View File
@@ -22,112 +22,112 @@ IMPLEMENT_DYNAMIC_ARG(ReaderPragmaHandler,reader_pragma_handler,nullptr);
// ----------------------------------------------------------------------------- : Reader
Reader::Reader(const InputStreamP& input, Packaged* package, const String& filename, bool ignore_invalid)
: indent(0), expected_indent(0), state(OUTSIDE)
, ignore_invalid(ignore_invalid)
, filename(filename), package(package), line_number(0), previous_line_number(0)
, input(input)
: indent(0), expected_indent(0), state(OUTSIDE)
, ignore_invalid(ignore_invalid)
, filename(filename), package(package), line_number(0), previous_line_number(0)
, input(input)
{
moveNext();
handleAppVersion();
moveNext();
handleAppVersion();
}
Reader::Reader(Reader* parent, Packaged* pkg, const String& filename, bool ignore_invalid)
: indent(0), expected_indent(0), state(OUTSIDE)
, ignore_invalid(ignore_invalid)
, filename(filename), package(pkg), line_number(0), previous_line_number(0)
, input(package_manager.openFileFromPackage(package, filename))
: indent(0), expected_indent(0), state(OUTSIDE)
, ignore_invalid(ignore_invalid)
, filename(filename), package(pkg), line_number(0), previous_line_number(0)
, input(package_manager.openFileFromPackage(package, filename))
{
moveNext();
// in an included file, use the app version of the parent if we have none
handleAppVersion();
if (file_app_version == 0) {
file_app_version = parent->file_app_version;
}
moveNext();
// in an included file, use the app version of the parent if we have none
handleAppVersion();
if (file_app_version == 0) {
file_app_version = parent->file_app_version;
}
}
void Reader::addAlias(Version end_version, const Char* a, const Char* b) {
Alias& alias = aliasses[a];
alias.new_key = b;
alias.end_version = end_version;
Alias& alias = aliasses[a];
alias.new_key = b;
alias.end_version = end_version;
}
void Reader::handleIgnore(int end_version, const Char* a) {
if (file_app_version < end_version) {
if (enterBlock(a)) exitBlock();
}
if (file_app_version < end_version) {
if (enterBlock(a)) exitBlock();
}
}
void Reader::handleAppVersion() {
if (enterBlock(_("mse_version"))) {
handle(file_app_version);
if (app_version < file_app_version) {
queue_message(MESSAGE_WARNING, _ERROR_2_("newer version", filename, file_app_version.toString()));
}
exitBlock();
}
if (enterBlock(_("mse_version"))) {
handle(file_app_version);
if (app_version < file_app_version) {
queue_message(MESSAGE_WARNING, _ERROR_2_("newer version", filename, file_app_version.toString()));
}
exitBlock();
}
}
void Reader::warning(const String& msg, int line_number_delta, bool warn_on_previous_line) {
warnings += String(_("\nOn line "))
<< ((warn_on_previous_line ? previous_line_number : line_number) + line_number_delta)
<< _(": \t") << msg;
warnings += String(_("\nOn line "))
<< ((warn_on_previous_line ? previous_line_number : line_number) + line_number_delta)
<< _(": \t") << msg;
}
void Reader::showWarnings() {
if (!warnings.empty()) {
queue_message(MESSAGE_WARNING, _("Warnings while reading file:\n") + filename + _("\n") + warnings);
warnings.clear();
}
if (!warnings.empty()) {
queue_message(MESSAGE_WARNING, _("Warnings while reading file:\n") + filename + _("\n") + warnings);
warnings.clear();
}
}
bool Reader::enterAnyBlock() {
if (state == ENTERED) moveNext(); // on the key of the parent block, first move inside it
if (indent != expected_indent) return false; // not enough indentation
state = ENTERED;
expected_indent += 1; // the indent inside the block must be at least this much
return true;
if (state == ENTERED) moveNext(); // on the key of the parent block, first move inside it
if (indent != expected_indent) return false; // not enough indentation
state = ENTERED;
expected_indent += 1; // the indent inside the block must be at least this much
return true;
}
bool Reader::enterBlock(const Char* name) {
if (state == ENTERED) moveNext(); // on the key of the parent block, first move inside it
if (indent != expected_indent) return false; // not enough indentation
if (cannocial_name_compare(key, name)) {
state = ENTERED;
expected_indent += 1; // the indent inside the block must be at least this much
return true;
} else {
return false;
}
if (state == ENTERED) moveNext(); // on the key of the parent block, first move inside it
if (indent != expected_indent) return false; // not enough indentation
if (cannocial_name_compare(key, name)) {
state = ENTERED;
expected_indent += 1; // the indent inside the block must be at least this much
return true;
} else {
return false;
}
}
void Reader::exitBlock() {
assert(expected_indent > 0);
expected_indent -= 1;
assert(state != UNHANDLED);
previous_value.clear();
if (state == ENTERED) moveNext(); // leave this key
// Dump the remainder of the block
// TODO: issue warnings?
while (indent > expected_indent) {
moveNext();
}
state = HANDLED;
assert(expected_indent > 0);
expected_indent -= 1;
assert(state != UNHANDLED);
previous_value.clear();
if (state == ENTERED) moveNext(); // leave this key
// Dump the remainder of the block
// TODO: issue warnings?
while (indent > expected_indent) {
moveNext();
}
state = HANDLED;
}
void Reader::moveNext() {
previous_line_number = line_number;
state = HANDLED;
key.clear();
indent = -1; // if no line is read it never has the expected indentation
// repeat until we have a good line
while (key.empty() && !input->Eof()) {
readLine();
}
// did we reach the end of the file?
if (key.empty() && input->Eof()) {
line_number += 1;
indent = -1;
}
previous_line_number = line_number;
state = HANDLED;
key.clear();
indent = -1; // if no line is read it never has the expected indentation
// repeat until we have a good line
while (key.empty() && !input->Eof()) {
readLine();
}
// did we reach the end of the file?
if (key.empty() && input->Eof()) {
line_number += 1;
indent = -1;
}
}
/// Faster vector, uses large local storage
@@ -138,28 +138,28 @@ void Reader::moveNext() {
*/
template <typename T> class LocalVector {
public:
LocalVector() : the_size(0), alloced(SMALL_SIZE), buffer(small) {}
~LocalVector() { if (buffer != small) free(buffer); }
void push_back(T t) {
if (the_size >= alloced) {
// double buffer size
if (buffer != small) {
buffer = (T*)realloc(buffer, sizeof(T) * alloced * 2);
} else {
buffer = (T*)malloc(sizeof(T) * alloced * 2);
memcpy(buffer, small, alloced * sizeof(T));
}
alloced *= 2;
}
buffer[the_size++] = t;
}
inline const T* get() const { return buffer; }
inline size_t size() const { return the_size; }
LocalVector() : the_size(0), alloced(SMALL_SIZE), buffer(small) {}
~LocalVector() { if (buffer != small) free(buffer); }
void push_back(T t) {
if (the_size >= alloced) {
// double buffer size
if (buffer != small) {
buffer = (T*)realloc(buffer, sizeof(T) * alloced * 2);
} else {
buffer = (T*)malloc(sizeof(T) * alloced * 2);
memcpy(buffer, small, alloced * sizeof(T));
}
alloced *= 2;
}
buffer[the_size++] = t;
}
inline const T* get() const { return buffer; }
inline size_t size() const { return the_size; }
private:
static const int SMALL_SIZE = 1024;
size_t the_size, alloced;
T* buffer;
T small[SMALL_SIZE];
static const int SMALL_SIZE = 1024;
size_t the_size, alloced;
T* buffer;
T small[SMALL_SIZE];
};
/// Read an UTF-8 encoded line from an input stream
@@ -167,290 +167,290 @@ template <typename T> class LocalVector {
*/
String read_utf8_line(wxInputStream& input, bool eat_bom = true, bool until_eof = false);
String read_utf8_line(wxInputStream& input, bool eat_bom, bool until_eof) {
LocalVector<char> buffer;
while (!input.Eof()) {
Byte c = input.GetC(); if (input.LastRead() <= 0) break;
if (!until_eof) {
if (c == '\n') break;
if (c == '\r') {
if (input.Eof()) break;
c = input.GetC(); if (input.LastRead() <= 0) break;
if (c != '\n') {
input.Ungetch(c); // \r but not \r\n
}
break;
}
}
buffer.push_back(c);
}
// convert to string
buffer.push_back('\0');
size_t size = wxConvUTF8.MB2WC(nullptr, buffer.get(), 0);
if (size == size_t(-1)) {
throw ParseError(_("Invalid UTF-8 sequence"));
} else if (size == 0) {
return _("");
}
#ifdef UNICODE
#if wxVERSION_NUMBER >= 2900
String result = wxString::FromUTF8(buffer.get(), buffer.size());
return eat_bom ? decodeUTF8BOM(result) : result;
#else
// NOTE: wx doc is wrong, parameter to GetWritableChar is numer of characters, not bytes
String result;
Char* result_buf = result.GetWriteBuf(size + 1);
wxConvUTF8.MB2WC(result_buf, buffer.get(), size + 1);
result.UngetWriteBuf(size);
return eat_bom ? decodeUTF8BOM(result) : result;
#endif
#else
String result;
// first to wchar, then back to local
vector<wchar_t> buf2; buf2.resize(size+1);
wxConvUTF8.MB2WC(&buf2[0], buffer.get(), size + 1);
// eat BOM?
if (eat_bom && buf2[0]==0xFEFF ) {
buf2.erase(buf2.begin()); // remove BOM
}
// convert
#ifdef __WXMSW__
// size includes null terminator
size = ::WideCharToMultiByte(CP_ACP, 0, &buf2[0], -1, nullptr, 0, nullptr, nullptr);
Char* result_buf = result.GetWriteBuf(size);
::WideCharToMultiByte(CP_ACP, 0, &buf2[0], -1, result_buf, (int)size, nullptr, nullptr);
result.UngetWriteBuf(size - 1);
#else
for (size_t i = 0 ; i < size ; ++i) {
wchar_t wc = buf2[i];
if (wc < 0xFF) {
result += (Char)wc;
} else {
// not valid in Latin1
result += '?';
}
}
#endif
return result;
#endif
LocalVector<char> buffer;
while (!input.Eof()) {
Byte c = input.GetC(); if (input.LastRead() <= 0) break;
if (!until_eof) {
if (c == '\n') break;
if (c == '\r') {
if (input.Eof()) break;
c = input.GetC(); if (input.LastRead() <= 0) break;
if (c != '\n') {
input.Ungetch(c); // \r but not \r\n
}
break;
}
}
buffer.push_back(c);
}
// convert to string
buffer.push_back('\0');
size_t size = wxConvUTF8.MB2WC(nullptr, buffer.get(), 0);
if (size == size_t(-1)) {
throw ParseError(_("Invalid UTF-8 sequence"));
} else if (size == 0) {
return _("");
}
#ifdef UNICODE
#if wxVERSION_NUMBER >= 2900
String result = wxString::FromUTF8(buffer.get(), buffer.size());
return eat_bom ? decodeUTF8BOM(result) : result;
#else
// NOTE: wx doc is wrong, parameter to GetWritableChar is numer of characters, not bytes
String result;
Char* result_buf = result.GetWriteBuf(size + 1);
wxConvUTF8.MB2WC(result_buf, buffer.get(), size + 1);
result.UngetWriteBuf(size);
return eat_bom ? decodeUTF8BOM(result) : result;
#endif
#else
String result;
// first to wchar, then back to local
vector<wchar_t> buf2; buf2.resize(size+1);
wxConvUTF8.MB2WC(&buf2[0], buffer.get(), size + 1);
// eat BOM?
if (eat_bom && buf2[0]==0xFEFF ) {
buf2.erase(buf2.begin()); // remove BOM
}
// convert
#ifdef __WXMSW__
// size includes null terminator
size = ::WideCharToMultiByte(CP_ACP, 0, &buf2[0], -1, nullptr, 0, nullptr, nullptr);
Char* result_buf = result.GetWriteBuf(size);
::WideCharToMultiByte(CP_ACP, 0, &buf2[0], -1, result_buf, (int)size, nullptr, nullptr);
result.UngetWriteBuf(size - 1);
#else
for (size_t i = 0 ; i < size ; ++i) {
wchar_t wc = buf2[i];
if (wc < 0xFF) {
result += (Char)wc;
} else {
// not valid in Latin1
result += '?';
}
}
#endif
return result;
#endif
}
void Reader::readLine(bool in_string) {
line_number += 1;
// We have to do our own line reading, because wxTextInputStream is insane
try {
line = read_utf8_line(*input, line_number == 1);
} catch (const ParseError& e) {
throw ParseError(e.what() + String(_(" on line ")) << line_number);
}
// pragma handler
if (reader_pragma_handler()) reader_pragma_handler()(line);
// read indentation
indent = 0;
while ((UInt)indent < line.size() && line.GetChar(indent) == _('\t')) {
indent += 1;
}
// read key / value
if (line.find_first_not_of(_(" \t")) == String::npos || line.GetChar(indent) == _('#')) {
// empty line or comment
key.clear();
return;
}
size_t pos = line.find_first_of(_(':'), indent);
key = line.substr(indent, pos - indent);
if (!ignore_invalid && !in_string && starts_with(key, _(" "))) {
warning(_("key: '") + key + _("' starts with a space; only use TABs for indentation!"), 0, false);
// try to fix up: 8 spaces is a tab
while (starts_with(key, _(" "))) {
key = key.substr(8);
indent += 1;
}
}
key = canonical_name_form(trim(key));
value = pos == String::npos ? _("") : trim_left(line.substr(pos+1));
if (key.empty() && pos!=String::npos) key = _(" "); // we don't want an empty key if there was a colon
line_number += 1;
// We have to do our own line reading, because wxTextInputStream is insane
try {
line = read_utf8_line(*input, line_number == 1);
} catch (const ParseError& e) {
throw ParseError(e.what() + String(_(" on line ")) << line_number);
}
// pragma handler
if (reader_pragma_handler()) reader_pragma_handler()(line);
// read indentation
indent = 0;
while ((UInt)indent < line.size() && line.GetChar(indent) == _('\t')) {
indent += 1;
}
// read key / value
if (line.find_first_not_of(_(" \t")) == String::npos || line.GetChar(indent) == _('#')) {
// empty line or comment
key.clear();
return;
}
size_t pos = line.find_first_of(_(':'), indent);
key = line.substr(indent, pos - indent);
if (!ignore_invalid && !in_string && starts_with(key, _(" "))) {
warning(_("key: '") + key + _("' starts with a space; only use TABs for indentation!"), 0, false);
// try to fix up: 8 spaces is a tab
while (starts_with(key, _(" "))) {
key = key.substr(8);
indent += 1;
}
}
key = canonical_name_form(trim(key));
value = pos == String::npos ? _("") : trim_left(line.substr(pos+1));
if (key.empty() && pos!=String::npos) key = _(" "); // we don't want an empty key if there was a colon
}
void Reader::unknownKey() {
// ignore?
if (ignore_invalid) {
do {
moveNext();
} while (indent > expected_indent);
return;
}
// aliasses?
map<String,Alias>::const_iterator it = aliasses.find(key);
if (it != aliasses.end()) {
if (aliasses.find(it->second.new_key) != aliasses.end()) {
// alias points to another alias, don't follow it, there is the risk of infinite loops
} else if (it->second.end_version <= file_app_version) {
// alias not used for this version, use in warning
if (indent == expected_indent) {
warning(_("Unexpected key: '") + key + _("' use '") + it->second.new_key + _("'"), 0, false);
do {
moveNext();
} while (indent > expected_indent);
return;
}
} else {
// try this key instead
key = it->second.new_key;
return;
}
}
if (indent >= expected_indent) {
warning(_("Unexpected key: '") + key + _("'"), 0, false);
do {
moveNext();
} while (indent > expected_indent);
}
// else: could be a nameless value, which doesn't call exitBlock to move past its own key
// ignore?
if (ignore_invalid) {
do {
moveNext();
} while (indent > expected_indent);
return;
}
// aliasses?
map<String,Alias>::const_iterator it = aliasses.find(key);
if (it != aliasses.end()) {
if (aliasses.find(it->second.new_key) != aliasses.end()) {
// alias points to another alias, don't follow it, there is the risk of infinite loops
} else if (it->second.end_version <= file_app_version) {
// alias not used for this version, use in warning
if (indent == expected_indent) {
warning(_("Unexpected key: '") + key + _("' use '") + it->second.new_key + _("'"), 0, false);
do {
moveNext();
} while (indent > expected_indent);
return;
}
} else {
// try this key instead
key = it->second.new_key;
return;
}
}
if (indent >= expected_indent) {
warning(_("Unexpected key: '") + key + _("'"), 0, false);
do {
moveNext();
} while (indent > expected_indent);
}
// else: could be a nameless value, which doesn't call exitBlock to move past its own key
}
// ----------------------------------------------------------------------------- : Handling basic types
void Reader::unhandle() {
assert(state == HANDLED);
state = UNHANDLED;
assert(state == HANDLED);
state = UNHANDLED;
}
const String& Reader::getValue() {
assert(state != HANDLED); // don't try to handle things twice
if (state == UNHANDLED) {
state = HANDLED;
return previous_value;
} else if (value.empty()) {
// a multiline string
previous_value.clear();
int pending_newlines = 0;
// read all lines that are indented enough
readLine(true);
previous_line_number = line_number;
while (indent >= expected_indent && !input->Eof()) {
previous_value.resize(previous_value.size() + pending_newlines, _('\n'));
pending_newlines = 0;
previous_value += line.substr(expected_indent); // strip expected indent
do {
readLine(true);
pending_newlines++;
// skip empty lines that are not indented enough
} while(trim(line).empty() && indent < expected_indent && !input->Eof());
}
// moveNext(), but without the initial readLine()
state = HANDLED;
while (key.empty() && !input->Eof()) {
readLine();
}
// did we reach the end of the file?
if (key.empty() && input->Eof()) {
line_number += 1;
indent = -1;
}
if (indent >= expected_indent) {
warning(_("Blank line or comment in text block, that is insufficiently indented.\n")
_("\t\tEither indent the comment/blank line, or add a 'key:' after it.\n")
_("\t\tThis could cause more more error messages.\n"), -1, false);
}
return previous_value;
} else {
previous_value = value;
moveNext();
return previous_value;
}
assert(state != HANDLED); // don't try to handle things twice
if (state == UNHANDLED) {
state = HANDLED;
return previous_value;
} else if (value.empty()) {
// a multiline string
previous_value.clear();
int pending_newlines = 0;
// read all lines that are indented enough
readLine(true);
previous_line_number = line_number;
while (indent >= expected_indent && !input->Eof()) {
previous_value.resize(previous_value.size() + pending_newlines, _('\n'));
pending_newlines = 0;
previous_value += line.substr(expected_indent); // strip expected indent
do {
readLine(true);
pending_newlines++;
// skip empty lines that are not indented enough
} while(trim(line).empty() && indent < expected_indent && !input->Eof());
}
// moveNext(), but without the initial readLine()
state = HANDLED;
while (key.empty() && !input->Eof()) {
readLine();
}
// did we reach the end of the file?
if (key.empty() && input->Eof()) {
line_number += 1;
indent = -1;
}
if (indent >= expected_indent) {
warning(_("Blank line or comment in text block, that is insufficiently indented.\n")
_("\t\tEither indent the comment/blank line, or add a 'key:' after it.\n")
_("\t\tThis could cause more more error messages.\n"), -1, false);
}
return previous_value;
} else {
previous_value = value;
moveNext();
return previous_value;
}
}
template <> void Reader::handle(String& s) {
s = getValue();
s = getValue();
}
template <> void Reader::handle(int& i) {
long l = 0;
if (!getValue().ToLong(&l)) {
warning(_("Expected integer instead of '") + previous_value + _("'"));
}
i = l;
long l = 0;
if (!getValue().ToLong(&l)) {
warning(_("Expected integer instead of '") + previous_value + _("'"));
}
i = l;
}
template <> void Reader::handle(unsigned int& i) {
long l = 0;
if (!getValue().ToLong(&l)) {
warning(_("Expected non-negative integer instead of '") + previous_value + _("'"));
} else if (l < 0) {
warning(wxString::Format(_("Expected non-negative integer instead of %d"),(int)l));
}
i = abs(l); // abs, because it will seem strange if -1 comes out as MAX_INT
long l = 0;
if (!getValue().ToLong(&l)) {
warning(_("Expected non-negative integer instead of '") + previous_value + _("'"));
} else if (l < 0) {
warning(wxString::Format(_("Expected non-negative integer instead of %d"),(int)l));
}
i = abs(l); // abs, because it will seem strange if -1 comes out as MAX_INT
}
template <> void Reader::handle(double& d) {
if (!getValue().ToDouble(&d)) {
warning(_("Expected floating point number instead of '") + previous_value + _("'"));
}
if (!getValue().ToDouble(&d)) {
warning(_("Expected floating point number instead of '") + previous_value + _("'"));
}
}
template <> void Reader::handle(bool& b) {
const String& v = getValue();
if (v==_("true") || v==_("1") || v==_("yes")) {
b = true;
} else if (v==_("false") || v==_("0") || v==_("no")) {
b = false;
} else {
warning(_("Expected boolean ('true' or 'false') instead of '") + v + _("'"));
}
const String& v = getValue();
if (v==_("true") || v==_("1") || v==_("yes")) {
b = true;
} else if (v==_("false") || v==_("0") || v==_("no")) {
b = false;
} else {
warning(_("Expected boolean ('true' or 'false') instead of '") + v + _("'"));
}
}
template <> void Reader::handle(tribool& tb) {
bool b;
handle(b);
tb = b;
bool b;
handle(b);
tb = b;
}
// ----------------------------------------------------------------------------- : Handling less basic util types
template <> void Reader::handle(wxDateTime& date) {
if (!date.ParseDateTime(getValue().c_str())) {
throw ParseError(_("Expected a date and time"));
}
if (!date.ParseDateTime(getValue().c_str())) {
throw ParseError(_("Expected a date and time"));
}
}
template <> void Reader::handle(Vector2D& vec) {
if (!wxSscanf(getValue().c_str(), _("(%lf,%lf)"), &vec.x, &vec.y)) {
throw ParseError(_("Expected (x,y)"));
}
if (!wxSscanf(getValue().c_str(), _("(%lf,%lf)"), &vec.x, &vec.y)) {
throw ParseError(_("Expected (x,y)"));
}
}
template <> void Reader::handle(FileName& f) {
if (clipboard_package()) {
String str = getValue();
if (!str.empty()) {
// copy file into current package
try {
String packaged_name = clipboard_package()->newFileName(_("image"),_("")); // a new unique name in the package, assume it's an image
OutputStreamP out = clipboard_package()->openOut(packaged_name);
InputStreamP in = Package::openAbsoluteFile(str);
out->Write(*in); // copy
f.assign(packaged_name);
} catch (Error) {
// ignore errors
}
} else {
f.assign(str);
}
} else {
handle(static_cast<String&>(f));
}
if (clipboard_package()) {
String str = getValue();
if (!str.empty()) {
// copy file into current package
try {
String packaged_name = clipboard_package()->newFileName(_("image"),_("")); // a new unique name in the package, assume it's an image
OutputStreamP out = clipboard_package()->openOut(packaged_name);
InputStreamP in = Package::openAbsoluteFile(str);
out->Write(*in); // copy
f.assign(packaged_name);
} catch (Error) {
// ignore errors
}
} else {
f.assign(str);
}
} else {
handle(static_cast<String&>(f));
}
}
// ----------------------------------------------------------------------------- : EnumReader
String EnumReader::notDoneErrorMessage() const {
if (!first) throw InternalError(_("No first value in EnumReader"));
return _ERROR_2_("unrecognized value", read, first);
if (!first) throw InternalError(_("No first value in EnumReader"));
return _ERROR_2_("unrecognized value", read, first);
}
void EnumReader::warnIfNotDone(Reader* errors_to) {
if (!done) {
// warning: unknown value
errors_to->warning(notDoneErrorMessage());
}
if (!done) {
// warning: unknown value
errors_to->warning(notDoneErrorMessage());
}
}
void EnumReader::errorIfNotDone() {
if (!done) {
throw ParseError(notDoneErrorMessage());
}
if (!done) {
throw ParseError(notDoneErrorMessage());
}
}
+216 -216
View File
@@ -33,163 +33,163 @@ typedef shared_ptr<wxInputStream> InputStreamP;
*/
class Reader {
private:
/// Construct a reader that reads a file in a package
/** Used for "include file" keys.
* package can be nullptr
*/
Reader(Reader* parent, Packaged* package, const String& filename, bool ignore_invalid = false);
/// Construct a reader that reads a file in a package
/** Used for "include file" keys.
* package can be nullptr
*/
Reader(Reader* parent, Packaged* package, const String& filename, bool ignore_invalid = false);
public:
/// Construct a reader that reads from the given input stream
/** filename is used only for error messages
*/
Reader(const InputStreamP& input, Packaged* package = nullptr, const String& filename = wxEmptyString, bool ignore_invalid = false);
~Reader() { showWarnings(); }
/// Tell the reflection code we are reading
inline bool reading() const { return true; }
/// Tell the reflection code we are not related to scripting
inline bool scripting() const { return false; }
/// Is the thing currently being read 'complex', i.e. does it have children
inline bool isComplex() const { return indent != expected_indent - 1 || value.empty(); }
/// Add a as an alias for b, all keys a will be replaced with b, only if file_app_version < end_version
void addAlias(Version end_version, const Char* a, const Char* b);
/// Ignore old keys
void handleIgnore(int, const Char*);
/// Read and check the application version
void handleAppVersion();
/// Add a warning message, but continue reading
void warning(const String& msg, int line_number_delta = 0, bool warn_on_previous_line = true);
/// Show all warning messages, but continue reading
void showWarnings();
// --------------------------------------------------- : Handling objects
/// Handle an object that can read as much as it can eat
template <typename T>
void handle_greedy(T& object) {
do {
handle(object);
if (state != HANDLED) unknownKey(object);
state = OUTSIDE;
} while (indent >= expected_indent);
}
/// Handle an object: read it if it's name matches
template <typename T>
void handle(const Char* name, T& object) {
if (enterBlock(name)) {
handle_greedy(object);
exitBlock();
}
}
/// Handle a value
template <typename T>
inline void handleNoScript(const Char* name, T& value) { handle(name,value); }
/// Reads a vector from the input stream
template <typename T>
void handle(const Char* name, vector<T>& vector);
/// Reads an object of type T from the input stream
template <typename T> void handle(T&);
/// Reads a intrusive_ptr from the input stream
template <typename T> void handle(intrusive_ptr<T>&);
/// Reads a map from the input stream
template <typename V> void handle(map<String,V>&);
/// Reads an IndexMap from the input stream, reads only keys that already exist in the map
template <typename K, typename V> void handle(IndexMap<K,V>&);
template <typename K, typename V> void handle(DelayedIndexMaps<K,V>&);
template <typename K, typename V> void handle(DelayedIndexMapsData<K,V>&);
/// Reads a Defaultable from the input stream
template <typename T> void handle(Defaultable<T>&);
/// Reads a Scriptable from the input stream
template <typename T> void handle(Scriptable<T>&);
// special behaviour
void handle(GameP&);
void handle(StyleSheetP&);
/// Indicate that the last value from getValue() was not handled, allowing it to be handled again
void unhandle();
/// The package being read from
inline Packaged* getPackage() const { return package; }
// --------------------------------------------------- : Data
/// App version this file was made with
Version file_app_version;
/// Construct a reader that reads from the given input stream
/** filename is used only for error messages
*/
Reader(const InputStreamP& input, Packaged* package = nullptr, const String& filename = wxEmptyString, bool ignore_invalid = false);
~Reader() { showWarnings(); }
/// Tell the reflection code we are reading
inline bool reading() const { return true; }
/// Tell the reflection code we are not related to scripting
inline bool scripting() const { return false; }
/// Is the thing currently being read 'complex', i.e. does it have children
inline bool isComplex() const { return indent != expected_indent - 1 || value.empty(); }
/// Add a as an alias for b, all keys a will be replaced with b, only if file_app_version < end_version
void addAlias(Version end_version, const Char* a, const Char* b);
/// Ignore old keys
void handleIgnore(int, const Char*);
/// Read and check the application version
void handleAppVersion();
/// Add a warning message, but continue reading
void warning(const String& msg, int line_number_delta = 0, bool warn_on_previous_line = true);
/// Show all warning messages, but continue reading
void showWarnings();
// --------------------------------------------------- : Handling objects
/// Handle an object that can read as much as it can eat
template <typename T>
void handle_greedy(T& object) {
do {
handle(object);
if (state != HANDLED) unknownKey(object);
state = OUTSIDE;
} while (indent >= expected_indent);
}
/// Handle an object: read it if it's name matches
template <typename T>
void handle(const Char* name, T& object) {
if (enterBlock(name)) {
handle_greedy(object);
exitBlock();
}
}
/// Handle a value
template <typename T>
inline void handleNoScript(const Char* name, T& value) { handle(name,value); }
/// Reads a vector from the input stream
template <typename T>
void handle(const Char* name, vector<T>& vector);
/// Reads an object of type T from the input stream
template <typename T> void handle(T&);
/// Reads a intrusive_ptr from the input stream
template <typename T> void handle(intrusive_ptr<T>&);
/// Reads a map from the input stream
template <typename V> void handle(map<String,V>&);
/// Reads an IndexMap from the input stream, reads only keys that already exist in the map
template <typename K, typename V> void handle(IndexMap<K,V>&);
template <typename K, typename V> void handle(DelayedIndexMaps<K,V>&);
template <typename K, typename V> void handle(DelayedIndexMapsData<K,V>&);
/// Reads a Defaultable from the input stream
template <typename T> void handle(Defaultable<T>&);
/// Reads a Scriptable from the input stream
template <typename T> void handle(Scriptable<T>&);
// special behaviour
void handle(GameP&);
void handle(StyleSheetP&);
/// Indicate that the last value from getValue() was not handled, allowing it to be handled again
void unhandle();
/// The package being read from
inline Packaged* getPackage() const { return package; }
// --------------------------------------------------- : Data
/// App version this file was made with
Version file_app_version;
private:
/// The line we read
String line;
/// The key and value of the last line we read
String key, value;
/// Value of the *previous* line, only valid in state==HANDLED
String previous_value;
/// Indentation of the last line we read
int indent;
/// Indentation of the block we are in
int expected_indent;
/// State of the reader
enum State {
OUTSIDE, ///< We have not entered the block of the current key
ENTERED, ///< We just entered the block of the current key
HANDLED, ///< We have handled a value, and moved to the next line, previous_value is the value we just handled
UNHANDLED, ///< Something has been 'unhandled()'
} state;
/// Aliasses for compatability
struct Alias {
String new_key;
Version end_version;
};
/// Aliasses for compatability
map<String, Alias> aliasses;
/// Should all invalid keys be ignored?
bool ignore_invalid;
/// Filename for error messages
String filename;
/// Package this file is from, if any
Packaged* package;
/// Line number of the current line for error messages
int line_number;
/// Line number of the previous_line
int previous_line_number;
/// Input stream we are reading from
InputStreamP input;
/// Accumulated warning messages
String warnings;
// --------------------------------------------------- : Reading the stream
/// Is there a block with the given key under the current cursor? if so, enter it
bool enterBlock(const Char* name);
/// Enter any block, no matter what the key
bool enterAnyBlock();
/// 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(bool in_string = false);
/// Return the value on the current line
const String& getValue();
/// No line was read, because nothing mathes the current key
/** Maybe the key is "include file" */
template <typename T>
void unknownKey(T& v) {
if (key == _("include file")) {
Reader reader(this, package, value, ignore_invalid);
reader.handle_greedy(v);
moveNext();
} else {
unknownKey();
}
}
void unknownKey();
/// The line we read
String line;
/// The key and value of the last line we read
String key, value;
/// Value of the *previous* line, only valid in state==HANDLED
String previous_value;
/// Indentation of the last line we read
int indent;
/// Indentation of the block we are in
int expected_indent;
/// State of the reader
enum State {
OUTSIDE, ///< We have not entered the block of the current key
ENTERED, ///< We just entered the block of the current key
HANDLED, ///< We have handled a value, and moved to the next line, previous_value is the value we just handled
UNHANDLED, ///< Something has been 'unhandled()'
} state;
/// Aliasses for compatability
struct Alias {
String new_key;
Version end_version;
};
/// Aliasses for compatability
map<String, Alias> aliasses;
/// Should all invalid keys be ignored?
bool ignore_invalid;
/// Filename for error messages
String filename;
/// Package this file is from, if any
Packaged* package;
/// Line number of the current line for error messages
int line_number;
/// Line number of the previous_line
int previous_line_number;
/// Input stream we are reading from
InputStreamP input;
/// Accumulated warning messages
String warnings;
// --------------------------------------------------- : Reading the stream
/// Is there a block with the given key under the current cursor? if so, enter it
bool enterBlock(const Char* name);
/// Enter any block, no matter what the key
bool enterAnyBlock();
/// 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(bool in_string = false);
/// Return the value on the current line
const String& getValue();
/// No line was read, because nothing mathes the current key
/** Maybe the key is "include file" */
template <typename T>
void unknownKey(T& v) {
if (key == _("include file")) {
Reader reader(this, package, value, ignore_invalid);
reader.handle_greedy(v);
moveNext();
} else {
unknownKey();
}
}
void unknownKey();
};
// ----------------------------------------------------------------------------- : Container types
@@ -200,7 +200,7 @@ class Reader {
*/
template <typename T>
intrusive_ptr<T> read_new(Reader& reader) {
return intrusive(new T());
return intrusive(new T());
}
/// Update the 'index' member of a value for use by IndexMap
@@ -208,92 +208,92 @@ template <typename T> void update_index(T&, size_t index) {}
template <typename T>
void Reader::handle(const Char* name, vector<T>& vector) {
String vectorKey = singular_form(name);
while (enterBlock(vectorKey)) {
vector.resize(vector.size() + 1);
handle_greedy(vector.back());
update_index(vector.back(), vector.size() - 1); // update index for IndexMap
exitBlock();
}
String vectorKey = singular_form(name);
while (enterBlock(vectorKey)) {
vector.resize(vector.size() + 1);
handle_greedy(vector.back());
update_index(vector.back(), vector.size() - 1); // update index for IndexMap
exitBlock();
}
}
template <typename T>
void Reader::handle(intrusive_ptr<T>& pointer) {
if (!pointer) pointer = read_new<T>(*this);
handle(*pointer);
if (!pointer) pointer = read_new<T>(*this);
handle(*pointer);
}
template <typename V>
void Reader::handle(map<String, V>& m) {
while (enterAnyBlock()) {
handle_greedy(m[key]);
exitBlock();
}
while (enterAnyBlock()) {
handle_greedy(m[key]);
exitBlock();
}
}
template <typename K, typename V>
void Reader::handle(IndexMap<K,V>& m) {
for (typename IndexMap<K,V>::iterator it = m.begin() ; it != m.end() ; ++it) {
handle(get_key_name(*it).c_str(), *it);
}
for (typename IndexMap<K,V>::iterator it = m.begin() ; it != m.end() ; ++it) {
handle(get_key_name(*it).c_str(), *it);
}
}
// ----------------------------------------------------------------------------- : Reflection
/// Implement reflection as used by Reader
#define REFLECT_OBJECT_READER(Cls) \
template<> void Reader::handle<Cls>(Cls& object) { \
object.reflect(*this); \
} \
void Cls::reflect(Reader& reader) { \
reflect_impl(reader); \
}
#define REFLECT_OBJECT_READER(Cls) \
template<> void Reader::handle<Cls>(Cls& object) { \
object.reflect(*this); \
} \
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& enum_) { \
EnumReader reader(getValue()); \
reflect_ ## Enum(enum_, reader); \
reader.warnIfNotDone(this); \
} \
void parse_enum(const String& value, Enum& out) { \
EnumReader reader(value); \
reflect_ ## Enum(out, reader); \
reader.errorIfNotDone(); \
}
#define REFLECT_ENUM_READER(Enum) \
template<> void Reader::handle<Enum>(Enum& enum_) { \
EnumReader reader(getValue()); \
reflect_ ## Enum(enum_, reader); \
reader.warnIfNotDone(this); \
} \
void parse_enum(const String& value, Enum& out) { \
EnumReader reader(value); \
reflect_ ## Enum(out, reader); \
reader.errorIfNotDone(); \
}
/// 'Tag' to be used when reflecting enumerations for Reader
class EnumReader {
public:
inline EnumReader(String read)
: read(read), first(nullptr), done(false) {}
/// Handle a possible value for the enum, if the name matches the name in the input
template <typename Enum>
inline void handle(const Char* name, Enum value, Enum& enum_) {
if (!done) {
if (read == name) {
done = true;
enum_ = value;
} else if (!first) {
first = name;
enum_ = value;
}
}
}
inline bool isDone() const { return done; }
void warnIfNotDone(Reader* errors_to);
void errorIfNotDone();
inline EnumReader(String read)
: read(read), first(nullptr), done(false) {}
/// Handle a possible value for the enum, if the name matches the name in the input
template <typename Enum>
inline void handle(const Char* name, Enum value, Enum& enum_) {
if (!done) {
if (read == name) {
done = true;
enum_ = value;
} else if (!first) {
first = name;
enum_ = value;
}
}
}
inline bool isDone() const { return done; }
void warnIfNotDone(Reader* errors_to);
void errorIfNotDone();
private:
String read; ///< The string to match to a value name
Char const* first; ///< Has the first (default) value been handled? If so, what is its name.
bool done; ///< Was anything matched?
String notDoneErrorMessage() const;
String read; ///< The string to match to a value name
Char const* first; ///< Has the first (default) value been handled? If so, what is its name.
bool done; ///< Was anything matched?
String notDoneErrorMessage() const;
};
// ----------------------------------------------------------------------------- : EOF
+86 -86
View File
@@ -18,133 +18,133 @@ using boost::tribool;
// ----------------------------------------------------------------------------- : Writer
Writer::Writer(const OutputStreamP& output, Version file_app_version)
: indentation(0)
, output(output), stream(*output)
: indentation(0)
, output(output), stream(*output)
{
stream.WriteString(BYTE_ORDER_MARK);
handle(_("mse_version"), file_app_version);
stream.WriteString(BYTE_ORDER_MARK);
handle(_("mse_version"), file_app_version);
}
void Writer::enterBlock(const Char* name) {
// don't write the key yet
pending_opened.push_back(name);
// don't write the key yet
pending_opened.push_back(name);
}
void Writer::exitBlock() {
if (pending_opened.empty()) {
assert(indentation > 0);
indentation -= 1;
} else {
// this block was apparently empty, ignore it
pending_opened.pop_back();
}
if (pending_opened.empty()) {
assert(indentation > 0);
indentation -= 1;
} else {
// this block was apparently empty, ignore it
pending_opened.pop_back();
}
}
void Writer::writePending() {
// In enterBlock we have delayed the actual writing of the keys until this point
// here we write all the pending keys, and increase indentation along the way.
for (size_t i = 0 ; i < pending_opened.size() ; ++i) {
if (i > 0) {
// before entering a sub-block, write a colon after the parent's name
stream.WriteString(_(":\n"));
}
indentation += 1;
writeIndentation();
writeUTF8(stream, canonical_name_form(pending_opened[i]));
}
pending_opened.clear();
// In enterBlock we have delayed the actual writing of the keys until this point
// here we write all the pending keys, and increase indentation along the way.
for (size_t i = 0 ; i < pending_opened.size() ; ++i) {
if (i > 0) {
// before entering a sub-block, write a colon after the parent's name
stream.WriteString(_(":\n"));
}
indentation += 1;
writeIndentation();
writeUTF8(stream, canonical_name_form(pending_opened[i]));
}
pending_opened.clear();
}
void Writer::writeIndentation() {
for(int i = 1 ; i < indentation ; ++i) {
stream.PutChar(_('\t'));
}
for(int i = 1 ; i < indentation ; ++i) {
stream.PutChar(_('\t'));
}
}
// ----------------------------------------------------------------------------- : Handling basic types
void Writer::handle(const String& value) {
if (pending_opened.empty()) {
throw InternalError(_("Can only write a value in a key that was just opened"));
}
writePending();
// write indentation and key
if (value.find_first_of(_('\n')) != String::npos || (!value.empty() && isSpace(value.GetChar(0)))) {
// multiline string, or contains leading whitespace
stream.WriteString(_(":\n"));
indentation += 1;
// split lines, and write each line
size_t start = 0, end, size = value.size();
while (start < size) {
end = value.find_first_of(_("\n\r"), start); // until end of line
// write the line
writeIndentation();
writeUTF8(stream, value.substr(start, end - start));
// Skip \r and \n
if (end == String::npos) break;
stream.PutChar(_('\n'));
start = end + 1;
if (start < size) {
Char c1 = value.GetChar(start - 1);
Char c2 = value.GetChar(start);
// skip second character of \r\n or \n\r
if (c1 != c2 && (c2 == _('\r') || c2 == _('\n'))) start += 1;
}
}
indentation -= 1;
} else {
stream.WriteString(_(": "));
writeUTF8(stream, value);
}
stream.PutChar(_('\n'));
if (pending_opened.empty()) {
throw InternalError(_("Can only write a value in a key that was just opened"));
}
writePending();
// write indentation and key
if (value.find_first_of(_('\n')) != String::npos || (!value.empty() && isSpace(value.GetChar(0)))) {
// multiline string, or contains leading whitespace
stream.WriteString(_(":\n"));
indentation += 1;
// split lines, and write each line
size_t start = 0, end, size = value.size();
while (start < size) {
end = value.find_first_of(_("\n\r"), start); // until end of line
// write the line
writeIndentation();
writeUTF8(stream, value.substr(start, end - start));
// Skip \r and \n
if (end == String::npos) break;
stream.PutChar(_('\n'));
start = end + 1;
if (start < size) {
Char c1 = value.GetChar(start - 1);
Char c2 = value.GetChar(start);
// skip second character of \r\n or \n\r
if (c1 != c2 && (c2 == _('\r') || c2 == _('\n'))) start += 1;
}
}
indentation -= 1;
} else {
stream.WriteString(_(": "));
writeUTF8(stream, value);
}
stream.PutChar(_('\n'));
}
template <> void Writer::handle(const int& value) {
handle(String() << value);
handle(String() << value);
}
template <> void Writer::handle(const unsigned int& value) {
handle(String() << value);
handle(String() << value);
}
template <> void Writer::handle(const double& value) {
handle(String() << value);
handle(String() << value);
}
template <> void Writer::handle(const bool& value) {
handle(value ? _("true") : _("false"));
handle(value ? _("true") : _("false"));
}
template <> void Writer::handle(const tribool& value) {
if (!indeterminate(value)) {
handle(value ? _("true") : _("false"));
}
if (!indeterminate(value)) {
handle(value ? _("true") : _("false"));
}
}
// ----------------------------------------------------------------------------- : Handling less basic util types
template <> void Writer::handle(const wxDateTime& date) {
if (date.IsValid()) {
handle(date.Format(_("%Y-%m-%d %H:%M:%S")));
}
if (date.IsValid()) {
handle(date.Format(_("%Y-%m-%d %H:%M:%S")));
}
}
template <> void Writer::handle(const Vector2D& vec) {
handle(String::Format(_("(%.10lf,%.10lf)"), vec.x, vec.y));
handle(String::Format(_("(%.10lf,%.10lf)"), vec.x, vec.y));
}
template <> void Writer::handle(const Color& col) {
handle(String::Format(_("rgb(%u,%u,%u)"), col.Red(), col.Green(), col.Blue()));
handle(String::Format(_("rgb(%u,%u,%u)"), col.Red(), col.Green(), col.Blue()));
}
template <> void Writer::handle(const FileName& value) {
if (clipboard_package() && !value.empty()) {
// use absolute names on clipboard
try {
handle(clipboard_package()->absoluteName(value));
} catch (const Error&) {
// ignore errors
}
} else {
handle(static_cast<const String&>(value));
if (writing_package()) {
writing_package()->referenceFile(value);
}
}
if (clipboard_package() && !value.empty()) {
// use absolute names on clipboard
try {
handle(clipboard_package()->absoluteName(value));
} catch (const Error&) {
// ignore errors
}
} else {
handle(static_cast<const String&>(value));
if (writing_package()) {
writing_package()->referenceFile(value);
}
}
}
+105 -105
View File
@@ -25,145 +25,145 @@ typedef shared_ptr<wxOutputStream> OutputStreamP;
/// The Writer can be used for writing (serializing) objects
class Writer {
public:
/// Construct a writer that writes to the given output stream
Writer(const OutputStreamP& output, Version file_app_version);
/// Tell the reflection code we are not reading
inline bool reading() const { return false; }
inline bool scripting() const { return false; }
inline bool isComplex() const { return true; }
inline void addAlias(int, const Char*, const Char*) {}
inline void handleIgnore(int, const Char*) {}
// --------------------------------------------------- : Handling objects
/// Handle an object: write it under the given name
template <typename T>
void handle(const Char* name, const T& object) {
enterBlock(name);
handle(object);
exitBlock();
}
/// Handle a value
template <typename T>
inline void handleNoScript(const Char* name, T& value) { handle(name,value); }
/// Write a vector to the output stream
template <typename T>
void handle(const Char* name, const vector<T>& vector);
/// Write a string to the output stream
void handle(const String& str);
void handle(const Char* str) { handle(String(str)); }
/// Write an object of type T to the output stream
template <typename T> void handle(const T&);
/// Write a intrusive_ptr to the output stream
template <typename T> void handle(const intrusive_ptr<T>&);
/// Write a map to the output stream
template <typename K, typename V> void handle(const map<K,V>&);
/// Write an IndexMap to the output stream
template <typename K, typename V> void handle(const IndexMap<K,V>&);
template <typename K, typename V> void handle(const DelayedIndexMaps<K,V>&);
template <typename K, typename V> void handle(const DelayedIndexMapsData<K,V>&);
/// Write an object of type Defaultable<T> to the output stream
template <typename T> void handle(const Defaultable<T>&);
/// Write an object of type Scriptable<T> to the output stream
template <typename T> void handle(const Scriptable<T>&);
// special behaviour
void handle(const GameP&);
void handle(const StyleSheetP&);
/// Construct a writer that writes to the given output stream
Writer(const OutputStreamP& output, Version file_app_version);
/// Tell the reflection code we are not reading
inline bool reading() const { return false; }
inline bool scripting() const { return false; }
inline bool isComplex() const { return true; }
inline void addAlias(int, const Char*, const Char*) {}
inline void handleIgnore(int, const Char*) {}
// --------------------------------------------------- : Handling objects
/// Handle an object: write it under the given name
template <typename T>
void handle(const Char* name, const T& object) {
enterBlock(name);
handle(object);
exitBlock();
}
/// Handle a value
template <typename T>
inline void handleNoScript(const Char* name, T& value) { handle(name,value); }
/// Write a vector to the output stream
template <typename T>
void handle(const Char* name, const vector<T>& vector);
/// Write a string to the output stream
void handle(const String& str);
void handle(const Char* str) { handle(String(str)); }
/// Write an object of type T to the output stream
template <typename T> void handle(const T&);
/// Write a intrusive_ptr to the output stream
template <typename T> void handle(const intrusive_ptr<T>&);
/// Write a map to the output stream
template <typename K, typename V> void handle(const map<K,V>&);
/// Write an IndexMap to the output stream
template <typename K, typename V> void handle(const IndexMap<K,V>&);
template <typename K, typename V> void handle(const DelayedIndexMaps<K,V>&);
template <typename K, typename V> void handle(const DelayedIndexMapsData<K,V>&);
/// Write an object of type Defaultable<T> to the output stream
template <typename T> void handle(const Defaultable<T>&);
/// Write an object of type Scriptable<T> to the output stream
template <typename T> void handle(const Scriptable<T>&);
// special behaviour
void handle(const GameP&);
void handle(const StyleSheetP&);
private:
// --------------------------------------------------- : Data
/// Indentation of the current block
int indentation;
/// Blocks opened to which nothing has been written
vector<const Char*> pending_opened;
/// Output stream we are writing to
OutputStreamP output;
/// Text stream wrapping the output stream
wxTextOutputStream stream;
// --------------------------------------------------- : Writing to the stream
/// Start a new block with the given name
void enterBlock(const Char* name);
/// Leave the block we are in
void exitBlock();
/// Write the pending_opened with the required indentation
void writePending();
/// Output some taps to represent the indentation level
void writeIndentation();
// --------------------------------------------------- : Data
/// Indentation of the current block
int indentation;
/// Blocks opened to which nothing has been written
vector<const Char*> pending_opened;
/// Output stream we are writing to
OutputStreamP output;
/// Text stream wrapping the output stream
wxTextOutputStream stream;
// --------------------------------------------------- : Writing to the stream
/// Start a new block with the given name
void enterBlock(const Char* name);
/// Leave the block we are in
void exitBlock();
/// Write the pending_opened with the required indentation
void writePending();
/// Output some taps to represent the indentation level
void writeIndentation();
};
// ----------------------------------------------------------------------------- : Container types
template <typename T>
void Writer::handle(const Char* name, const vector<T>& vec) {
String vectorKey = singular_form(name);
for (typename vector<T>::const_iterator it = vec.begin() ; it != vec.end() ; ++it) {
handle(vectorKey, *it);
}
String vectorKey = singular_form(name);
for (typename vector<T>::const_iterator it = vec.begin() ; it != vec.end() ; ++it) {
handle(vectorKey, *it);
}
}
template <typename T>
void Writer::handle(const intrusive_ptr<T>& pointer) {
if (pointer) handle(*pointer);
if (pointer) handle(*pointer);
}
template <typename K, typename V>
void Writer::handle(const map<K,V>& m) {
for (typename map<K,V>::const_iterator it = m.begin() ; it != m.end() ; ++it) {
handle(it->first.c_str(), it->second);
}
for (typename map<K,V>::const_iterator it = m.begin() ; it != m.end() ; ++it) {
handle(it->first.c_str(), it->second);
}
}
template <typename K, typename V>
void Writer::handle(const IndexMap<K,V>& m) {
for (typename IndexMap<K,V>::const_iterator it = m.begin() ; it != m.end() ; ++it) {
handle(get_key_name(*it).c_str(), *it);
}
for (typename IndexMap<K,V>::const_iterator it = m.begin() ; it != m.end() ; ++it) {
handle(get_key_name(*it).c_str(), *it);
}
}
// ----------------------------------------------------------------------------- : Reflection
/// Implement reflection as used by Writer
#define REFLECT_OBJECT_WRITER(Cls) \
template<> void Writer::handle<Cls>(const Cls& object) { \
const_cast<Cls&>(object).reflect(*this); \
} \
void Cls::reflect(Writer& writer) { \
reflect_impl(writer); \
}
#define REFLECT_OBJECT_WRITER(Cls) \
template<> void Writer::handle<Cls>(const Cls& object) { \
const_cast<Cls&>(object).reflect(*this); \
} \
void Cls::reflect(Writer& writer) { \
reflect_impl(writer); \
}
// ----------------------------------------------------------------------------- : Reflection for enumerations
/// Implement enum reflection as used by Writer
#define REFLECT_ENUM_WRITER(Enum) \
template<> void Writer::handle<Enum>(const Enum& enum_) { \
EnumWriter writer(*this); \
reflect_ ## Enum(const_cast<Enum&>(enum_), writer); \
}
#define REFLECT_ENUM_WRITER(Enum) \
template<> void Writer::handle<Enum>(const Enum& enum_) { \
EnumWriter writer(*this); \
reflect_ ## Enum(const_cast<Enum&>(enum_), writer); \
}
/// 'Tag' to be used when reflecting enumerations for Writer
class EnumWriter {
public:
inline EnumWriter(Writer& writer)
: writer(writer) {}
/// Handle a possible value for the enum, if the name matches the name in the input
template <typename Enum>
inline void handle(const Char* name, Enum value, Enum enum_) {
if (enum_ == value) {
writer.handle(name);
}
}
inline EnumWriter(Writer& writer)
: writer(writer) {}
/// Handle a possible value for the enum, if the name matches the name in the input
template <typename Enum>
inline void handle(const Char* name, Enum value, Enum enum_) {
if (enum_ == value) {
writer.handle(name);
}
}
private:
Writer& writer; ///< The writer to write output to
Writer& writer; ///< The writer to write output to
};
// ----------------------------------------------------------------------------- : EOF