#ifndef EXTENSIONS_BROWSER_API_DECLARATIVE_DEDUPING_FACTORY_H__
#define EXTENSIONS_BROWSER_API_DECLARATIVE_DEDUPING_FACTORY_H__
#include <stddef.h>
#include <list>
#include <string>
#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/containers/contains.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/memory/ref_counted.h"
namespace extensions {
template <typename BaseClassT, typename ValueT>
class DedupingFactory {
public:
typedef scoped_refptr<const BaseClassT> (*FactoryMethod)(
const std::string& instance_type,
ValueT ,
std::string* ,
bool* );
enum Parameterized {
IS_PARAMETERIZED,
IS_NOT_PARAMETERIZED
};
explicit DedupingFactory(size_t max_number_prototypes);
DedupingFactory(const DedupingFactory&) = delete;
DedupingFactory& operator=(const DedupingFactory&) = delete;
~DedupingFactory();
void RegisterFactoryMethod(const std::string& instance_type,
Parameterized parameterized,
FactoryMethod factory_method);
scoped_refptr<const BaseClassT> Instantiate(const std::string& instance_type,
ValueT value,
std::string* error,
bool* bad_message);
void ClearPrototypes();
private:
typedef std::string InstanceType;
using PrototypeList = std::list<scoped_refptr<const BaseClassT>>;
using ExistingPrototypes = base::flat_map<InstanceType, PrototypeList>;
using FactoryMethods = base::flat_map<InstanceType, FactoryMethod>;
using ParameterizedTypes = base::flat_set<InstanceType>;
const size_t max_number_prototypes_;
ExistingPrototypes prototypes_;
FactoryMethods factory_methods_;
ParameterizedTypes parameterized_types_;
};
template <typename BaseClassT, typename ValueT>
DedupingFactory<BaseClassT, ValueT>::DedupingFactory(
size_t max_number_prototypes)
: max_number_prototypes_(max_number_prototypes) {}
template <typename BaseClassT, typename ValueT>
DedupingFactory<BaseClassT, ValueT>::~DedupingFactory() {}
template <typename BaseClassT, typename ValueT>
void DedupingFactory<BaseClassT, ValueT>::RegisterFactoryMethod(
const std::string& instance_type,
typename DedupingFactory<BaseClassT, ValueT>::Parameterized parameterized,
FactoryMethod factory_method) {
DCHECK(!base::Contains(factory_methods_, instance_type));
factory_methods_[instance_type] = factory_method;
if (parameterized == IS_PARAMETERIZED) {
parameterized_types_.insert(instance_type);
}
}
template <typename BaseClassT, typename ValueT>
scoped_refptr<const BaseClassT>
DedupingFactory<BaseClassT, ValueT>::Instantiate(
const std::string& instance_type,
ValueT value,
std::string* error,
bool* bad_message) {
typename FactoryMethods::const_iterator factory_method_iter =
factory_methods_.find(instance_type);
if (factory_method_iter == factory_methods_.end()) {
*error = "Invalid instance type " + instance_type;
*bad_message = true;
return scoped_refptr<const BaseClassT>();
}
FactoryMethod factory_method = factory_method_iter->second;
PrototypeList& prototypes = prototypes_[instance_type];
if (!base::Contains(parameterized_types_, instance_type)) {
if (prototypes.empty()) {
scoped_refptr<const BaseClassT> new_object =
(*factory_method)(instance_type, value, error, bad_message);
if (!new_object.get() || !error->empty() || *bad_message) {
return scoped_refptr<const BaseClassT>();
}
prototypes.push_back(new_object);
}
return prototypes.front();
}
scoped_refptr<const BaseClassT> new_object =
(*factory_method)(instance_type, value, error, bad_message);
if (!new_object.get() || !error->empty() || *bad_message) {
return scoped_refptr<const BaseClassT>();
}
size_t length = 0;
for (typename PrototypeList::iterator i = prototypes.begin();
i != prototypes.end();
++i) {
if ((*i)->Equals(new_object.get())) {
scoped_refptr<const BaseClassT> old_object = *i;
prototypes.erase(i);
prototypes.push_back(old_object);
return old_object;
}
++length;
}
if (length >= max_number_prototypes_)
prototypes.pop_front();
prototypes.push_back(new_object);
return new_object;
}
template <typename BaseClassT, typename ValueT>
void DedupingFactory<BaseClassT, ValueT>::ClearPrototypes() {
prototypes_.clear();
}
}
#endif