Upstream version 10.38.222.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / declarative / deduping_factory.h
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.
4
5 #ifndef CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DEDUPING_FACTORY_H__
6 #define CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DEDUPING_FACTORY_H__
7
8 #include <list>
9 #include <string>
10
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"
16
17 namespace base {
18 class Value;
19 }  // namespace base
20
21 namespace extensions {
22
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.
28 //
29 // BaseClassT needs to provide a comparison operations. Like the following:
30 //
31 // class BaseClassT {
32 //   virtual bool Equals(const BaseClassT* other) const;
33 // };
34 //
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 */);
49
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
57   };
58
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();
65
66   void RegisterFactoryMethod(const std::string& instance_type,
67                              Parameterized parameterized,
68                              FactoryMethod factory_method);
69
70   scoped_refptr<const BaseClassT> Instantiate(const std::string& instance_type,
71                                               const base::Value* value,
72                                               std::string* error,
73                                               bool* bad_message);
74
75   void ClearPrototypes();
76
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;
85
86   const size_t max_number_prototypes_;
87   ExistingPrototypes prototypes_;
88   FactoryMethods factory_methods_;
89   ParameterizedTypes parameterized_types_;
90
91   DISALLOW_COPY_AND_ASSIGN(DedupingFactory);
92 };
93
94 template<typename BaseClassT>
95 DedupingFactory<BaseClassT>::DedupingFactory(size_t max_number_prototypes)
96     : max_number_prototypes_(max_number_prototypes) {}
97
98 template<typename BaseClassT>
99 DedupingFactory<BaseClassT>::~DedupingFactory() {}
100
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);
110 }
111
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>();
124   }
125
126   FactoryMethod factory_method = factory_method_iter->second;
127
128   PrototypeList& prototypes = prototypes_[instance_type];
129
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);
140     }
141     return prototypes.front();
142   }
143
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>();
149
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;
161     }
162     ++length;
163   }
164
165   if (length >= max_number_prototypes_)
166     prototypes.pop_front();
167   prototypes.push_back(new_object);
168
169   return new_object;
170 }
171
172 template<typename BaseClassT>
173 void DedupingFactory<BaseClassT>::ClearPrototypes() {
174   prototypes_.clear();
175 }
176
177 }  // namespace extensions
178
179 #endif  // CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DEDUPING_FACTORY_H__