Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / extensions / browser / api / declarative / deduping_factory.h
blob57fbdd7773fa58daa026ccedf912576c91e21a74
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifndef EXTENSIONS_BROWSER_API_DECLARATIVE_DEDUPING_FACTORY_H__
6 #define EXTENSIONS_BROWSER_API_DECLARATIVE_DEDUPING_FACTORY_H__
8 #include <list>
9 #include <string>
11 #include "base/compiler_specific.h"
12 #include "base/containers/hash_tables.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/stl_util.h"
17 namespace base {
18 class Value;
19 } // namespace base
21 namespace extensions {
23 // Factory class that stores a cache of the last |N| created objects of each
24 // kind. These objects need to be immutable, refcounted objects that are derived
25 // from BaseClassT. The objects do not need to be RefCountedThreadSafe. If a new
26 // instance of an object is created that is identical to a pre-existing object,
27 // it is discarded and the pre-existing object is recycled.
29 // BaseClassT needs to provide a comparison operations. Like the following:
31 // class BaseClassT {
32 // virtual bool Equals(const BaseClassT* other) const;
33 // };
35 // The unit test shows an example.
36 template<typename BaseClassT>
37 class DedupingFactory {
38 public:
39 // Factory methods for BaseClass instances. |value| contains e.g. the json
40 // dictionary that describes the object to be instantiated. |error| is used
41 // to return error messages in case the extension passed an action that was
42 // syntactically correct but semantically incorrect. |bad_message| is set to
43 // true in case |dict| does not confirm to the validated JSON specification.
44 typedef scoped_refptr<const BaseClassT>
45 (* FactoryMethod)(const std::string& instance_type,
46 const base::Value* /* value */ ,
47 std::string* /* error */,
48 bool* /* bad_message */);
50 enum Parameterized {
51 // Two instantiated objects may be different and we need to check for
52 // equality to see whether we can recycle one.
53 IS_PARAMETERIZED,
54 // The objects are not parameterized, i.e. all created instances are the
55 // same and it is sufficient to create a single one.
56 IS_NOT_PARAMETERIZED
59 // Creates a DedupingFactory with a MRU cache of size |max_number_prototypes|
60 // per instance_type. If we find a match within the cache, the factory reuses
61 // that instance instead of creating a new one. The cache size should not be
62 // too large because we probe linearly whether an element is in the cache.
63 explicit DedupingFactory(size_t max_number_prototypes);
64 ~DedupingFactory();
66 void RegisterFactoryMethod(const std::string& instance_type,
67 Parameterized parameterized,
68 FactoryMethod factory_method);
70 scoped_refptr<const BaseClassT> Instantiate(const std::string& instance_type,
71 const base::Value* value,
72 std::string* error,
73 bool* bad_message);
75 void ClearPrototypes();
77 private:
78 typedef std::string InstanceType;
79 // Cache of previous prototypes in most-recently-used order. Most recently
80 // used objects are at the end.
81 typedef std::list<scoped_refptr<const BaseClassT> > PrototypeList;
82 typedef base::hash_map<InstanceType, PrototypeList> ExistingPrototypes;
83 typedef base::hash_map<InstanceType, FactoryMethod> FactoryMethods;
84 typedef base::hash_set<InstanceType> ParameterizedTypes;
86 const size_t max_number_prototypes_;
87 ExistingPrototypes prototypes_;
88 FactoryMethods factory_methods_;
89 ParameterizedTypes parameterized_types_;
91 DISALLOW_COPY_AND_ASSIGN(DedupingFactory);
94 template<typename BaseClassT>
95 DedupingFactory<BaseClassT>::DedupingFactory(size_t max_number_prototypes)
96 : max_number_prototypes_(max_number_prototypes) {}
98 template<typename BaseClassT>
99 DedupingFactory<BaseClassT>::~DedupingFactory() {}
101 template<typename BaseClassT>
102 void DedupingFactory<BaseClassT>::RegisterFactoryMethod(
103 const std::string& instance_type,
104 typename DedupingFactory<BaseClassT>::Parameterized parameterized,
105 FactoryMethod factory_method) {
106 DCHECK(!ContainsKey(factory_methods_, instance_type));
107 factory_methods_[instance_type] = factory_method;
108 if (parameterized == IS_PARAMETERIZED)
109 parameterized_types_.insert(instance_type);
112 template<typename BaseClassT>
113 scoped_refptr<const BaseClassT> DedupingFactory<BaseClassT>::Instantiate(
114 const std::string& instance_type,
115 const base::Value* value,
116 std::string* error,
117 bool* bad_message) {
118 typename FactoryMethods::const_iterator factory_method_iter =
119 factory_methods_.find(instance_type);
120 if (factory_method_iter == factory_methods_.end()) {
121 *error = "Invalid instance type " + instance_type;
122 *bad_message = true;
123 return scoped_refptr<const BaseClassT>();
126 FactoryMethod factory_method = factory_method_iter->second;
128 PrototypeList& prototypes = prototypes_[instance_type];
130 // We can take a shortcut for objects that are not parameterized. For those
131 // only a single instance may ever exist so we can simplify the creation
132 // logic.
133 if (!ContainsKey(parameterized_types_, instance_type)) {
134 if (prototypes.empty()) {
135 scoped_refptr<const BaseClassT> new_object =
136 (*factory_method)(instance_type, value, error, bad_message);
137 if (!new_object.get() || !error->empty() || *bad_message)
138 return scoped_refptr<const BaseClassT>();
139 prototypes.push_back(new_object);
141 return prototypes.front();
144 // Handle parameterized objects.
145 scoped_refptr<const BaseClassT> new_object =
146 (*factory_method)(instance_type, value, error, bad_message);
147 if (!new_object.get() || !error->empty() || *bad_message)
148 return scoped_refptr<const BaseClassT>();
150 size_t length = 0;
151 for (typename PrototypeList::iterator i = prototypes.begin();
152 i != prototypes.end();
153 ++i) {
154 if ((*i)->Equals(new_object.get())) {
155 // Move the old object to the end of the queue so that it gets
156 // discarded later.
157 scoped_refptr<const BaseClassT> old_object = *i;
158 prototypes.erase(i);
159 prototypes.push_back(old_object);
160 return old_object;
162 ++length;
165 if (length >= max_number_prototypes_)
166 prototypes.pop_front();
167 prototypes.push_back(new_object);
169 return new_object;
172 template<typename BaseClassT>
173 void DedupingFactory<BaseClassT>::ClearPrototypes() {
174 prototypes_.clear();
177 } // namespace extensions
179 #endif // EXTENSIONS_BROWSER_API_DECLARATIVE_DEDUPING_FACTORY_H__