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