summaryrefslogtreecommitdiffstats
path: root/base/property_bag.h
blob: 42fa1b99bc66fe74b97ef358f335960334b51822 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// Copyright (c) 2011 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 BASE_PROPERTY_BAG_H_
#define BASE_PROPERTY_BAG_H_
#pragma once

#include <map>

#include "base/basictypes.h"
#include "base/base_export.h"

template <typename T> class linked_ptr;

namespace base {

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 be 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. 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 BASE_EXPORT 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);
  const Prop* GetProperty(PropID id) const;

  // 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 BASE_EXPORT 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_);
  }
  const PropertyBag::Prop* GetPropertyInternal(const PropertyBag* bag) const {
    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.
template<class T>
class PropertyAccessor : public PropertyAccessorBase {
 public:
  PropertyAccessor() : PropertyAccessorBase() {}
  virtual ~PropertyAccessor() {}

  // Makes a copy of the |prop| object for storage.
  void SetProperty(PropertyBag* bag, const T& prop) {
    SetPropertyInternal(bag, new Container(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*>(prop)->get();
  }
  const T* GetProperty(const PropertyBag* bag) const {
    const PropertyBag::Prop* prop = GetPropertyInternal(bag);
    if (!prop)
      return NULL;
    return static_cast<const Container*>(prop)->get();
  }

  // See also DeleteProperty on thn PropertyAccessorBase.

 private:
  class Container : public PropertyBag::Prop {
   public:
    explicit Container(const T& data) : data_(data) {}

    T* get() { return &data_; }
    const T* get() const { return &data_; }

   private:
    virtual Prop* copy() {
      return new Container(data_);
    }

    T data_;
  };

  DISALLOW_COPY_AND_ASSIGN(PropertyAccessor);
};

}  // namespace base

#endif  // BASE_PROPERTY_BAG_H_