diff options
author | brettw@google.com <brettw@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-09 23:29:51 +0000 |
---|---|---|
committer | brettw@google.com <brettw@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-09 23:29:51 +0000 |
commit | 78f346a6f58cd39450c86e65fe85eca4bb458e05 (patch) | |
tree | 2b6a7881447a3bff0ad5f55328f5623cf4289058 /chrome | |
parent | 5534be7842eeeb47365e189e94e6d352e7de329e (diff) | |
download | chromium_src-78f346a6f58cd39450c86e65fe85eca4bb458e05.zip chromium_src-78f346a6f58cd39450c86e65fe85eca4bb458e05.tar.gz chromium_src-78f346a6f58cd39450c86e65fe85eca4bb458e05.tar.bz2 |
Add a property bag which is a typesafe list of arbitrary data that can be
associated with any object.
Review URL: http://codereview.chromium.org/13676
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@6654 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/common/property_bag.cc | 48 | ||||
-rw-r--r-- | chrome/common/property_bag.h | 165 | ||||
-rw-r--r-- | chrome/common/property_bag_unittest.cc | 62 |
3 files changed, 275 insertions, 0 deletions
diff --git a/chrome/common/property_bag.cc b/chrome/common/property_bag.cc new file mode 100644 index 0000000..f7c92a3 --- /dev/null +++ b/chrome/common/property_bag.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/property_bag.h" + +PropertyBag::PropertyBag() { +} + +PropertyBag::PropertyBag(const PropertyBag& other) { + operator=(other); +} + +PropertyBag::~PropertyBag() { +} + +PropertyBag& PropertyBag::operator=(const PropertyBag& other) { + props_.clear(); + + // We need to make copies of each property using the virtual copy() method. + for (PropertyMap::const_iterator i = other.props_.begin(); + i != other.props_.end(); ++i) + props_[i->first] = linked_ptr<Prop>(i->second->copy()); + return *this; +} + +void PropertyBag::SetProperty(PropID id, Prop* prop) { + props_[id] = linked_ptr<Prop>(prop); +} + +PropertyBag::Prop* PropertyBag::GetProperty(PropID id) { + PropertyMap::const_iterator found = props_.find(id); + if (found == props_.end()) + return NULL; + return found->second.get(); +} + +void PropertyBag::DeleteProperty(PropID id) { + PropertyMap::iterator found = props_.find(id); + if (found == props_.end()) + return; // Not found, nothing to do. + props_.erase(found); +} + +PropertyAccessorBase::PropertyAccessorBase() { + static PropertyBag::PropID next_id = 1; + prop_id_ = next_id++; +} diff --git a/chrome/common/property_bag.h b/chrome/common/property_bag.h new file mode 100644 index 0000000..cf9ad41 --- /dev/null +++ b/chrome/common/property_bag.h @@ -0,0 +1,165 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_PROPERTY_BAG_H_ +#define CHROME_COMMON_PROPERTY_BAG_H_ + +#include <map> + +#include "base/basictypes.h" +#include "base/linked_ptr.h" + +class PropertyAccessorBase; + +// A property bag holds a generalized list of arbitrary metadata called +// properties. Each property is a class type derived from PropertyBag::Prop +// that can bet set and retrieved. +// +// The property bag is not read or written directly. Instead, callers go +// through a PropertyAccessor. The Accessor generates the unique IDs that +// identify different properties, so uniquely identify a property. The Accessor +// is templatized to also provide typesafety to the callers. +// +// Example: +// // Note: you don't want to use Singleton for your Accessor if you're using +// // a simple type like int or string as the data, since it will enforce that +// // there is only one singleton for that type, which will conflict. If +// // you're putting in some struct that's unique to you, go ahead. +// PropertyAccessor<int>* my_accessor() const { +// static PropertyAccessor<int>* accessor = NULL; +// if (!accessor) accessor = new PropertyAccessor<int>; +// return accessor; +// } +// +// void doit(SomeObjectThatImplementsPropertyBag* object) { +// PropertyAccessor<int>* accessor = my_accessor(); +// int* property = accessor.GetProperty(object); +// if (property) +// ... use property ... +// +// accessor.SetProperty(object, 22); +// } +class PropertyBag { + public: + // The type that uniquely identifies a property type. + typedef int PropID; + enum { NULL_PROP_ID = -1 }; // Invalid property ID. + + // Properties are all derived from this class. They must be deletable and + // copyable + class Prop { + public: + virtual ~Prop() {} + + // Copies the property and returns a pointer to the new one. The caller is + // responsible for managing the lifetime. + virtual Prop* copy() = 0; + }; + + PropertyBag(); + PropertyBag(const PropertyBag& other); + virtual ~PropertyBag(); + + PropertyBag& operator=(const PropertyBag& other); + + private: + friend class PropertyAccessorBase; + + typedef std::map<PropID, linked_ptr<Prop> > PropertyMap; + + // Used by the PropertyAccessor to set the property with the given ID. + // Ownership of the given pointer will be transferred to us. Any existing + // property matching the given ID will be deleted. + void SetProperty(PropID id, Prop* prop); + + // Used by the PropertyAccessor to retrieve the property with the given ID. + // The returned pointer will be NULL if there is no match. Ownership of the + // pointer will stay with the property bag. + Prop* GetProperty(PropID id); + + // Deletes the property with the given ID from the bag if it exists. + void DeleteProperty(PropID id); + + PropertyMap props_; + + // Copy and assign is explicitly allowed for this class. +}; + +// PropertyAccessorBase ------------------------------------------------------- + +// Manages getting the unique IDs to identify a property. Callers should use +// PropertyAccessor below instead. +class PropertyAccessorBase { + public: + PropertyAccessorBase(); + virtual ~PropertyAccessorBase() {} + + // Removes our property, if any, from the given property bag. + void DeleteProperty(PropertyBag* bag) { + bag->DeleteProperty(prop_id_); + } + + protected: + void SetPropertyInternal(PropertyBag* bag, PropertyBag::Prop* prop) { + bag->SetProperty(prop_id_, prop); + } + PropertyBag::Prop* GetPropertyInternal(PropertyBag* bag) { + return bag->GetProperty(prop_id_); + } + + private: + // Identifier for this property. + PropertyBag::PropID prop_id_; + + DISALLOW_COPY_AND_ASSIGN(PropertyAccessorBase); +}; + +// PropertyAccessor ----------------------------------------------------------- + +// Provides typesafe accessor functions for a property bag, and manages the +// unique identifiers for properties via the PropertyAccessorBase. +// +// Note that class T must be derived from PropertyBag::Prop. +template<class T> +class PropertyAccessor : public PropertyAccessorBase { + public: + PropertyAccessor() : PropertyAccessorBase() {} + virtual ~PropertyAccessor() {} + + // Takes ownership of the |prop| pointer. + void SetProperty(PropertyBag* bag, const T& prop) { + SetPropertyInternal(bag, new Container<T>(prop)); + } + + // Returns our property in the given bag or NULL if there is no match. The + // returned pointer's ownership will stay with the property bag. + T* GetProperty(PropertyBag* bag) { + PropertyBag::Prop* prop = GetPropertyInternal(bag); + if (!prop) + return NULL; + return static_cast< Container<T>* >(prop)->get(); + } + + // See also DeleteProperty on thn PropertyAccessorBase. + + private: + template<class T> + class Container : public PropertyBag::Prop { + public: + Container(const T& data) : data_(data) {} + + T* get() { return &data_; } + + private: + virtual Prop* copy() { + return new Container<T>(data_); + } + + T data_; + }; + + DISALLOW_COPY_AND_ASSIGN(PropertyAccessor); +}; + +#endif // CHROME_COMMON_PROPERTY_BAG_H_ diff --git a/chrome/common/property_bag_unittest.cc b/chrome/common/property_bag_unittest.cc new file mode 100644 index 0000000..51db07c --- /dev/null +++ b/chrome/common/property_bag_unittest.cc @@ -0,0 +1,62 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/property_bag.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(PropertyBagTest, AddQueryRemove) { + PropertyBag bag; + PropertyAccessor<int> adaptor; + + // Should be no match initially. + EXPECT_EQ(NULL, adaptor.GetProperty(&bag)); + + // Add the value and make sure we get it back. + const int kFirstValue = 1; + adaptor.SetProperty(&bag, kFirstValue); + ASSERT_TRUE(adaptor.GetProperty(&bag)); + EXPECT_EQ(kFirstValue, *adaptor.GetProperty(&bag)); + + // Set it to a new value. + const int kSecondValue = 2; + adaptor.SetProperty(&bag, kSecondValue); + ASSERT_TRUE(adaptor.GetProperty(&bag)); + EXPECT_EQ(kSecondValue, *adaptor.GetProperty(&bag)); + + // Remove the value and make sure it's gone. + adaptor.DeleteProperty(&bag); + EXPECT_EQ(NULL, adaptor.GetProperty(&bag)); +} + +TEST(PropertyBagTest, Copy) { + PropertyAccessor<int> adaptor1; + PropertyAccessor<double> adaptor2; + + // Create a bag with property type 1 in it. + PropertyBag copy; + adaptor1.SetProperty(©, 22); + + const int kType1Value = 10; + const double kType2Value = 2.7; + { + // Create a bag with property types 1 and 2 in it. + PropertyBag initial; + adaptor1.SetProperty(&initial, kType1Value); + adaptor2.SetProperty(&initial, kType2Value); + + // Assign to the original. + copy = initial; + } + + // Verify the copy got the two properties. + ASSERT_TRUE(adaptor1.GetProperty(©)); + ASSERT_TRUE(adaptor2.GetProperty(©)); + EXPECT_EQ(kType1Value, *adaptor1.GetProperty(©)); + EXPECT_EQ(kType2Value, *adaptor2.GetProperty(©)); + + // Clear it out, neither property should be left. + copy = PropertyBag(); + EXPECT_EQ(NULL, adaptor1.GetProperty(©)); + EXPECT_EQ(NULL, adaptor2.GetProperty(©)); +} |