Skip to content

Commit

Permalink
feat(config): config compiler plugins that port legacy features to th…
Browse files Browse the repository at this point in the history
…e new YAML syntax
  • Loading branch information
lotem committed Sep 16, 2017
1 parent 4ecae44 commit a7d253e
Show file tree
Hide file tree
Showing 9 changed files with 248 additions and 4 deletions.
42 changes: 42 additions & 0 deletions src/rime/config/auto_patch_config_plugin.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// Copyright RIME Developers
// Distributed under the BSD License
//
#include <boost/algorithm/string.hpp>
#include <rime/config/config_compiler_impl.h>
#include <rime/config/plugins.h>

namespace rime {

static string remove_suffix(const string& input, const string& suffix) {
return boost::ends_with(input, suffix) ?
input.substr(0, input.length() - suffix.length()) : input;
}

// auto-patch applies to all loaded config resources, including dependencies.
// therefore it's done at the end of Compile phase.
bool AutoPatchConfigPlugin::ReviewCompileOutput(ConfigCompiler* compiler,
an<ConfigResource> resource) {
if (boost::ends_with(resource->resource_id, ".custom"))
return true;
// skip auto-patch if there is already an explicit `__patch` at the root node
auto root_deps = compiler->GetDependencies(resource->resource_id + ":");
if (!root_deps.empty() && root_deps.back()->priority() >= kPatch)
return true;
auto patch_resource_id =
remove_suffix(resource->resource_id, ".schema") + ".custom";
LOG(INFO) << "auto-patch " << resource->resource_id << ":/__patch: "
<< patch_resource_id << ":/patch?";
compiler->Push(resource);
compiler->AddDependency(
New<PatchReference>(Reference{patch_resource_id, "patch", true}));
compiler->Pop();
return true;
}

bool AutoPatchConfigPlugin::ReviewLinkOutput(ConfigCompiler* compiler,
an<ConfigResource> resource) {
return true;
}

} // namespace rime
10 changes: 8 additions & 2 deletions src/rime/config/config_compiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <rime/config/config_compiler_impl.h>
#include <rime/config/config_data.h>
#include <rime/config/config_types.h>
#include <rime/config/plugins.h>

namespace rime {

Expand Down Expand Up @@ -243,8 +244,10 @@ void ConfigDependencyGraph::Add(an<Dependency> dependency) {
}
}

ConfigCompiler::ConfigCompiler(ResourceResolver* resource_resolver)
ConfigCompiler::ConfigCompiler(ResourceResolver* resource_resolver,
ConfigCompilerPlugin* plugin)
: resource_resolver_(resource_resolver),
plugin_(plugin),
graph_(new ConfigDependencyGraph) {
}

Expand Down Expand Up @@ -303,6 +306,8 @@ an<ConfigResource> ConfigCompiler::Compile(const string& file_name) {
resource->loaded = resource->data->LoadFromFile(
resource_resolver_->ResolvePath(resource_id).string(), this);
Pop();
if (plugin_)
plugin_->ReviewCompileOutput(this, resource);
return resource;
}

Expand Down Expand Up @@ -483,7 +488,8 @@ bool ConfigCompiler::Link(an<ConfigResource> target) {
LOG(ERROR) << "resource not found: " << target->resource_id;
return false;
}
return ResolveDependencies(found->first + ":");
return ResolveDependencies(found->first + ":") &&
(plugin_ ? plugin_->ReviewLinkOutput(this, target) : true);
}

bool ConfigCompiler::ResolveDependencies(const string& path) {
Expand Down
5 changes: 4 additions & 1 deletion src/rime/config/config_compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ struct Reference {
string repr() const;
};

class ConfigCompilerPlugin;
class ResourceResolver;
struct Dependency;
struct ConfigDependencyGraph;
Expand All @@ -46,7 +47,8 @@ class ConfigCompiler {
static constexpr const char* APPEND_DIRECTIVE = "__append";
static constexpr const char* MERGE_DIRECTIVE = "__merge";

explicit ConfigCompiler(ResourceResolver* resource_resolver);
ConfigCompiler(ResourceResolver* resource_resolver,
ConfigCompilerPlugin* plugin);
virtual ~ConfigCompiler();

Reference CreateReference(const string& qualified_path);
Expand All @@ -69,6 +71,7 @@ class ConfigCompiler {

private:
ResourceResolver* resource_resolver_;
ConfigCompilerPlugin* plugin_;
the<ConfigDependencyGraph> graph_;
};

Expand Down
44 changes: 43 additions & 1 deletion src/rime/config/config_component.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <rime/config/config_compiler.h>
#include <rime/config/config_data.h>
#include <rime/config/config_types.h>
#include <rime/config/plugins.h>

namespace rime {

Expand Down Expand Up @@ -160,12 +161,53 @@ Config* ConfigComponent::Create(const string& file_name) {
return new Config(GetConfigData(file_name));
}

void ConfigComponent::InstallPlugin(ConfigCompilerPlugin* plugin) {
plugins_.push_back(the<ConfigCompilerPlugin>(plugin));
}

template <class Container>
struct MultiplePlugins : ConfigCompilerPlugin {
Container& plugins;

MultiplePlugins(Container& _plugins)
: plugins(_plugins) {
}
bool ReviewCompileOutput(ConfigCompiler* compiler,
an<ConfigResource> resource) override {
return ReviewedByAll(&ConfigCompilerPlugin::ReviewCompileOutput,
compiler, resource);
}
bool ReviewLinkOutput(ConfigCompiler* compiler,
an<ConfigResource> resource) override {
return ReviewedByAll(&ConfigCompilerPlugin::ReviewLinkOutput,
compiler, resource);
}

typedef bool (ConfigCompilerPlugin::*Reviewer)(ConfigCompiler* compiler,
an<ConfigResource> resource);
bool ReviewedByAll(Reviewer reviewer,
ConfigCompiler* compiler,
an<ConfigResource> resource);
};

template <class Container>
bool MultiplePlugins<Container>::ReviewedByAll(Reviewer reviewer,
ConfigCompiler* compiler,
an<ConfigResource> resource) {
for (const auto& plugin : plugins) {
if(!((*plugin).*reviewer)(compiler, resource))
return false;
}
return true;
}

an<ConfigData> ConfigComponent::GetConfigData(const string& file_name) {
auto config_id = resource_resolver_->ToResourceId(file_name);
// keep a weak reference to the shared config data in the component
weak<ConfigData>& wp(cache_[config_id]);
if (wp.expired()) { // create a new copy and load it
ConfigCompiler compiler(resource_resolver_.get());
MultiplePlugins<decltype(plugins_)> multiple_plugins(plugins_);
ConfigCompiler compiler(resource_resolver_.get(), &multiple_plugins);
auto resource = compiler.Compile(file_name);
if (resource->loaded && !compiler.Link(resource)) {
LOG(ERROR) << "error loading config from: " << file_name;
Expand Down
6 changes: 6 additions & 0 deletions src/rime/config/config_component.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,24 @@ class Config : public Class<Config, const string&>, public ConfigItemRef {
void SetItem(an<ConfigItem> item);
};

class ConfigCompiler;
class ConfigCompilerPlugin;
class ResourceResolver;
struct ConfigResource;

class ConfigComponent : public Config::Component {
public:
ConfigComponent();
~ConfigComponent();
Config* Create(const string& file_name);
void InstallPlugin(ConfigCompilerPlugin *plugin);
bool ApplyPlugins(ConfigCompiler* compiler, an<ConfigResource> resource);

private:
an<ConfigData> GetConfigData(const string& file_name);
map<string, weak<ConfigData>> cache_;
the<ResourceResolver> resource_resolver_;
vector<the<ConfigCompilerPlugin>> plugins_;
};

} // namespace rime
Expand Down
22 changes: 22 additions & 0 deletions src/rime/config/legacy_dictionary_config_plugin.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// Copyright RIME Developers
// Distributed under the BSD License
//
#include <rime/config/config_compiler_impl.h>
#include <rime/config/plugins.h>

namespace rime {

bool LegacyDictionaryConfigPlugin::ReviewCompileOutput(
ConfigCompiler* compiler, an<ConfigResource> resource) {
// TODO: unimplemented
return true;
}

bool LegacyDictionaryConfigPlugin::ReviewLinkOutput(
ConfigCompiler* compiler, an<ConfigResource> resource) {
// TODO: unimplemented
return true;
}

} // namespace rime
75 changes: 75 additions & 0 deletions src/rime/config/legacy_preset_config_plugin.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// Copyright RIME Developers
// Distributed under the BSD License
//
#include <boost/algorithm/string.hpp>
#include <rime/config/config_compiler_impl.h>
#include <rime/config/config_cow_ref.h>
#include <rime/config/plugins.h>

namespace rime {

bool LegacyPresetConfigPlugin::ReviewCompileOutput(
ConfigCompiler* compiler, an<ConfigResource> resource) {
return true;
}

bool LegacyPresetConfigPlugin::ReviewLinkOutput(
ConfigCompiler* compiler, an<ConfigResource> resource) {
if (!boost::ends_with(resource->resource_id, ".schema"))
return true;
if (auto preset = resource->data->Traverse("key_binder/import_preset")) {
if (!Is<ConfigValue>(preset))
return false;
auto preset_config_id = As<ConfigValue>(preset)->str();
LOG(INFO) << "interpreting key_binder/import_preset: " << preset_config_id;
auto target = Cow(resource, "key_binder");
auto map = As<ConfigMap>(**target);
if (map && map->HasKey("bindings")) {
// append to included list `key_binder/bindings/+` instead of overwriting
auto appended = map->Get("bindings");
*Cow(target, "bindings/+") = appended;
// `*target` is already referencing a copied map, safe to edit directly
(*target)["bindings"] = nullptr;
}
Reference reference{preset_config_id, "key_binder", false};
if (!IncludeReference{reference}
.TargetedAt(target).Resolve(compiler)) {
LOG(ERROR) << "failed to include section " << reference;
return false;
}
}
// NOTE: in the following cases, Cow() is not strictly necessary because
// we know for sure that no other ConfigResource is going to reference the
// root map node that will be modified. But other than the root node of the
// resource being linked, it's possbile a map or list has multiple references
// in the node tree, therefore Cow() is recommended to make sure the
// modifications only happen to one place.
if (auto preset = resource->data->Traverse("punctuator/import_preset")) {
if (!Is<ConfigValue>(preset))
return false;
auto preset_config_id = As<ConfigValue>(preset)->str();
LOG(INFO) << "interpreting punctuator/import_preset: " << preset_config_id;
Reference reference{preset_config_id, "punctuator", false};
if (!IncludeReference{reference}
.TargetedAt(Cow(resource, "punctuator")).Resolve(compiler)) {
LOG(ERROR) << "failed to include section " << reference;
return false;
}
}
if (auto preset = resource->data->Traverse("recognizer/import_preset")) {
if (!Is<ConfigValue>(preset))
return false;
auto preset_config_id = As<ConfigValue>(preset)->str();
LOG(INFO) << "interpreting recognizer/import_preset: " << preset_config_id;
Reference reference{preset_config_id, "recognizer", false};
if (!IncludeReference{reference}
.TargetedAt(Cow(resource, "recognizer")).Resolve(compiler)) {
LOG(ERROR) << "failed to include section " << reference;
return false;
}
}
return true;
}

} // namespace rime
44 changes: 44 additions & 0 deletions src/rime/config/plugins.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// Copyright RIME Developers
// Distributed under the BSD License
//
#ifndef RIME_CONFIG_PLUGINS_H_
#define RIME_CONFIG_PLUGINS_H_

#include <rime/common.h>

namespace rime {

class ConfigCompiler;
struct ConfigResource;

class ConfigCompilerPlugin {
public:
typedef bool Review(ConfigCompiler* compiler,
an<ConfigResource> resource);

virtual Review ReviewCompileOutput = 0;
virtual Review ReviewLinkOutput = 0;
};

class AutoPatchConfigPlugin : public ConfigCompilerPlugin {
public:
Review ReviewCompileOutput;
Review ReviewLinkOutput;
};

class LegacyPresetConfigPlugin : public ConfigCompilerPlugin {
public:
Review ReviewCompileOutput;
Review ReviewLinkOutput;
};

class LegacyDictionaryConfigPlugin : public ConfigCompilerPlugin {
public:
Review ReviewCompileOutput;
Review ReviewLinkOutput;
};

} // namespace rime

#endif // RIME_CONFIG_PLUGINS_H_
4 changes: 4 additions & 0 deletions src/rime/core_module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

// built-in components
#include <rime/config.h>
#include <rime/config/plugins.h>
#include <rime/schema.h>

using namespace rime;
Expand All @@ -20,6 +21,9 @@ static void rime_core_initialize() {
Registry& r = Registry::instance();

auto config = new ConfigComponent;
config->InstallPlugin(new AutoPatchConfigPlugin);
config->InstallPlugin(new LegacyPresetConfigPlugin);
config->InstallPlugin(new LegacyDictionaryConfigPlugin);
r.Register("config", config);
r.Register("schema", new SchemaComponent(config));
}
Expand Down

0 comments on commit a7d253e

Please sign in to comment.