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__
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"
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:
32 // virtual bool Equals(const BaseClassT* other) const;
35 // The unit test shows an example.
36 template<typename BaseClassT
>
37 class DedupingFactory
{
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 */);
51 // Two instantiated objects may be different and we need to check for
52 // equality to see whether we can recycle one.
54 // The objects are not parameterized, i.e. all created instances are the
55 // same and it is sufficient to create a single one.
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
);
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
,
75 void ClearPrototypes();
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
,
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
;
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
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
>();
151 for (typename
PrototypeList::iterator i
= prototypes
.begin();
152 i
!= prototypes
.end();
154 if ((*i
)->Equals(new_object
.get())) {
155 // Move the old object to the end of the queue so that it gets
157 scoped_refptr
<const BaseClassT
> old_object
= *i
;
159 prototypes
.push_back(old_object
);
165 if (length
>= max_number_prototypes_
)
166 prototypes
.pop_front();
167 prototypes
.push_back(new_object
);
172 template<typename BaseClassT
>
173 void DedupingFactory
<BaseClassT
>::ClearPrototypes() {
177 } // namespace extensions
179 #endif // EXTENSIONS_BROWSER_API_DECLARATIVE_DEDUPING_FACTORY_H__