Home | History | Annotate | Download | only in declarative
      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__
    180