kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 1 | # Protocol Buffers - Google's data interchange format |
| 2 | # Copyright 2008 Google Inc. All rights reserved. |
| 3 | # http://code.google.com/p/protobuf/ |
| 4 | # |
| 5 | # Redistribution and use in source and binary forms, with or without |
| 6 | # modification, are permitted provided that the following conditions are |
| 7 | # met: |
| 8 | # |
| 9 | # * Redistributions of source code must retain the above copyright |
| 10 | # notice, this list of conditions and the following disclaimer. |
| 11 | # * Redistributions in binary form must reproduce the above |
| 12 | # copyright notice, this list of conditions and the following disclaimer |
| 13 | # in the documentation and/or other materials provided with the |
| 14 | # distribution. |
| 15 | # * Neither the name of Google Inc. nor the names of its |
| 16 | # contributors may be used to endorse or promote products derived from |
| 17 | # this software without specific prior written permission. |
| 18 | # |
| 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 20 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 21 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 22 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 23 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | |
| 31 | """Contains container classes to represent different protocol buffer types. |
| 32 | |
| 33 | This file defines container classes which represent categories of protocol |
| 34 | buffer field types which need extra maintenance. Currently these categories |
| 35 | are: |
| 36 | - Repeated scalar fields - These are all repeated fields which aren't |
| 37 | composite (e.g. they are of simple types like int32, string, etc). |
| 38 | - Repeated composite fields - Repeated fields which are composite. This |
| 39 | includes groups and nested messages. |
| 40 | """ |
| 41 | |
| 42 | __author__ = 'petar@google.com (Petar Petrov)' |
| 43 | |
| 44 | |
| 45 | class BaseContainer(object): |
| 46 | |
| 47 | """Base container class.""" |
| 48 | |
| 49 | # Minimizes memory usage and disallows assignment to other attributes. |
| 50 | __slots__ = ['_message_listener', '_values'] |
| 51 | |
| 52 | def __init__(self, message_listener): |
| 53 | """ |
| 54 | Args: |
| 55 | message_listener: A MessageListener implementation. |
| 56 | The RepeatedScalarFieldContainer will call this object's |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 57 | Modified() method when it is modified. |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 58 | """ |
| 59 | self._message_listener = message_listener |
| 60 | self._values = [] |
| 61 | |
| 62 | def __getitem__(self, key): |
| 63 | """Retrieves item by the specified key.""" |
| 64 | return self._values[key] |
| 65 | |
| 66 | def __len__(self): |
| 67 | """Returns the number of elements in the container.""" |
| 68 | return len(self._values) |
| 69 | |
| 70 | def __ne__(self, other): |
| 71 | """Checks if another instance isn't equal to this one.""" |
| 72 | # The concrete classes should define __eq__. |
| 73 | return not self == other |
| 74 | |
liujisi@google.com | 33165fe | 2010-11-02 13:14:58 +0000 | [diff] [blame^] | 75 | def __hash__(self): |
| 76 | raise TypeError('unhashable object') |
| 77 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 78 | def __repr__(self): |
| 79 | return repr(self._values) |
| 80 | |
liujisi@google.com | 33165fe | 2010-11-02 13:14:58 +0000 | [diff] [blame^] | 81 | def sort(self, sort_function=cmp): |
| 82 | self._values.sort(sort_function) |
| 83 | |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 84 | |
| 85 | class RepeatedScalarFieldContainer(BaseContainer): |
| 86 | |
| 87 | """Simple, type-checked, list-like container for holding repeated scalars.""" |
| 88 | |
| 89 | # Disallows assignment to other attributes. |
| 90 | __slots__ = ['_type_checker'] |
| 91 | |
| 92 | def __init__(self, message_listener, type_checker): |
| 93 | """ |
| 94 | Args: |
| 95 | message_listener: A MessageListener implementation. |
| 96 | The RepeatedScalarFieldContainer will call this object's |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 97 | Modified() method when it is modified. |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 98 | type_checker: A type_checkers.ValueChecker instance to run on elements |
| 99 | inserted into this container. |
| 100 | """ |
| 101 | super(RepeatedScalarFieldContainer, self).__init__(message_listener) |
| 102 | self._type_checker = type_checker |
| 103 | |
pesho.petrov | 87e64e1 | 2008-12-24 01:07:22 +0000 | [diff] [blame] | 104 | def append(self, value): |
| 105 | """Appends an item to the list. Similar to list.append().""" |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 106 | self._type_checker.CheckValue(value) |
| 107 | self._values.append(value) |
| 108 | if not self._message_listener.dirty: |
| 109 | self._message_listener.Modified() |
pesho.petrov | 87e64e1 | 2008-12-24 01:07:22 +0000 | [diff] [blame] | 110 | |
| 111 | def insert(self, key, value): |
| 112 | """Inserts the item at the specified position. Similar to list.insert().""" |
| 113 | self._type_checker.CheckValue(value) |
| 114 | self._values.insert(key, value) |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 115 | if not self._message_listener.dirty: |
| 116 | self._message_listener.Modified() |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 117 | |
kenton@google.com | 2d6daa7 | 2009-01-22 01:27:00 +0000 | [diff] [blame] | 118 | def extend(self, elem_seq): |
| 119 | """Extends by appending the given sequence. Similar to list.extend().""" |
| 120 | if not elem_seq: |
| 121 | return |
| 122 | |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 123 | new_values = [] |
kenton@google.com | 2d6daa7 | 2009-01-22 01:27:00 +0000 | [diff] [blame] | 124 | for elem in elem_seq: |
| 125 | self._type_checker.CheckValue(elem) |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 126 | new_values.append(elem) |
| 127 | self._values.extend(new_values) |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 128 | self._message_listener.Modified() |
| 129 | |
| 130 | def MergeFrom(self, other): |
| 131 | """Appends the contents of another repeated field of the same type to this |
| 132 | one. We do not check the types of the individual fields. |
| 133 | """ |
| 134 | self._values.extend(other._values) |
| 135 | self._message_listener.Modified() |
kenton@google.com | 2d6daa7 | 2009-01-22 01:27:00 +0000 | [diff] [blame] | 136 | |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 137 | def remove(self, elem): |
pesho.petrov | 87e64e1 | 2008-12-24 01:07:22 +0000 | [diff] [blame] | 138 | """Removes an item from the list. Similar to list.remove().""" |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 139 | self._values.remove(elem) |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 140 | self._message_listener.Modified() |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 141 | |
| 142 | def __setitem__(self, key, value): |
| 143 | """Sets the item on the specified position.""" |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 144 | self._type_checker.CheckValue(value) |
| 145 | self._values[key] = value |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 146 | self._message_listener.Modified() |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 147 | |
pesho.petrov | 87e64e1 | 2008-12-24 01:07:22 +0000 | [diff] [blame] | 148 | def __getslice__(self, start, stop): |
| 149 | """Retrieves the subset of items from between the specified indices.""" |
| 150 | return self._values[start:stop] |
| 151 | |
| 152 | def __setslice__(self, start, stop, values): |
| 153 | """Sets the subset of items from between the specified indices.""" |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 154 | new_values = [] |
pesho.petrov | 87e64e1 | 2008-12-24 01:07:22 +0000 | [diff] [blame] | 155 | for value in values: |
| 156 | self._type_checker.CheckValue(value) |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 157 | new_values.append(value) |
| 158 | self._values[start:stop] = new_values |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 159 | self._message_listener.Modified() |
pesho.petrov | 87e64e1 | 2008-12-24 01:07:22 +0000 | [diff] [blame] | 160 | |
| 161 | def __delitem__(self, key): |
| 162 | """Deletes the item at the specified position.""" |
| 163 | del self._values[key] |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 164 | self._message_listener.Modified() |
pesho.petrov | 87e64e1 | 2008-12-24 01:07:22 +0000 | [diff] [blame] | 165 | |
| 166 | def __delslice__(self, start, stop): |
| 167 | """Deletes the subset of items from between the specified indices.""" |
| 168 | del self._values[start:stop] |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 169 | self._message_listener.Modified() |
pesho.petrov | 87e64e1 | 2008-12-24 01:07:22 +0000 | [diff] [blame] | 170 | |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 171 | def __eq__(self, other): |
| 172 | """Compares the current instance with another one.""" |
| 173 | if self is other: |
| 174 | return True |
| 175 | # Special case for the same type which should be common and fast. |
| 176 | if isinstance(other, self.__class__): |
| 177 | return other._values == self._values |
| 178 | # We are presumably comparing against some other sequence type. |
| 179 | return other == self._values |
| 180 | |
| 181 | |
| 182 | class RepeatedCompositeFieldContainer(BaseContainer): |
| 183 | |
| 184 | """Simple, list-like container for holding repeated composite fields.""" |
| 185 | |
| 186 | # Disallows assignment to other attributes. |
| 187 | __slots__ = ['_message_descriptor'] |
| 188 | |
| 189 | def __init__(self, message_listener, message_descriptor): |
| 190 | """ |
| 191 | Note that we pass in a descriptor instead of the generated directly, |
| 192 | since at the time we construct a _RepeatedCompositeFieldContainer we |
| 193 | haven't yet necessarily initialized the type that will be contained in the |
| 194 | container. |
| 195 | |
| 196 | Args: |
| 197 | message_listener: A MessageListener implementation. |
| 198 | The RepeatedCompositeFieldContainer will call this object's |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 199 | Modified() method when it is modified. |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 200 | message_descriptor: A Descriptor instance describing the protocol type |
| 201 | that should be present in this container. We'll use the |
| 202 | _concrete_class field of this descriptor when the client calls add(). |
| 203 | """ |
| 204 | super(RepeatedCompositeFieldContainer, self).__init__(message_listener) |
| 205 | self._message_descriptor = message_descriptor |
| 206 | |
liujisi@google.com | 33165fe | 2010-11-02 13:14:58 +0000 | [diff] [blame^] | 207 | def add(self, **kwargs): |
| 208 | """Adds a new element at the end of the list and returns it. Keyword |
| 209 | arguments may be used to initialize the element. |
| 210 | """ |
| 211 | new_element = self._message_descriptor._concrete_class(**kwargs) |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 212 | new_element._SetListener(self._message_listener) |
| 213 | self._values.append(new_element) |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 214 | if not self._message_listener.dirty: |
| 215 | self._message_listener.Modified() |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 216 | return new_element |
| 217 | |
liujisi@google.com | 33165fe | 2010-11-02 13:14:58 +0000 | [diff] [blame^] | 218 | def extend(self, elem_seq): |
| 219 | """Extends by appending the given sequence of elements of the same type |
| 220 | as this one, copying each individual message. |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 221 | """ |
| 222 | message_class = self._message_descriptor._concrete_class |
| 223 | listener = self._message_listener |
| 224 | values = self._values |
liujisi@google.com | 33165fe | 2010-11-02 13:14:58 +0000 | [diff] [blame^] | 225 | for message in elem_seq: |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 226 | new_element = message_class() |
| 227 | new_element._SetListener(listener) |
| 228 | new_element.MergeFrom(message) |
| 229 | values.append(new_element) |
| 230 | listener.Modified() |
| 231 | |
liujisi@google.com | 33165fe | 2010-11-02 13:14:58 +0000 | [diff] [blame^] | 232 | def MergeFrom(self, other): |
| 233 | """Appends the contents of another repeated field of the same type to this |
| 234 | one, copying each individual message. |
| 235 | """ |
| 236 | self.extend(other._values) |
| 237 | |
pesho.petrov | 87e64e1 | 2008-12-24 01:07:22 +0000 | [diff] [blame] | 238 | def __getslice__(self, start, stop): |
| 239 | """Retrieves the subset of items from between the specified indices.""" |
| 240 | return self._values[start:stop] |
| 241 | |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 242 | def __delitem__(self, key): |
pesho.petrov | 87e64e1 | 2008-12-24 01:07:22 +0000 | [diff] [blame] | 243 | """Deletes the item at the specified position.""" |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 244 | del self._values[key] |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 245 | self._message_listener.Modified() |
pesho.petrov | 87e64e1 | 2008-12-24 01:07:22 +0000 | [diff] [blame] | 246 | |
| 247 | def __delslice__(self, start, stop): |
| 248 | """Deletes the subset of items from between the specified indices.""" |
| 249 | del self._values[start:stop] |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 250 | self._message_listener.Modified() |
kenton@google.com | 26bd9ee | 2008-11-21 00:06:27 +0000 | [diff] [blame] | 251 | |
| 252 | def __eq__(self, other): |
| 253 | """Compares the current instance with another one.""" |
| 254 | if self is other: |
| 255 | return True |
| 256 | if not isinstance(other, self.__class__): |
| 257 | raise TypeError('Can only compare repeated composite fields against ' |
| 258 | 'other repeated composite fields.') |
| 259 | return self._values == other._values |