OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of gcloud.db; | 5 part of gcloud.db; |
6 | 6 |
7 /// Annotation used to mark dart classes which can be stored into datastore. | 7 /// Annotation used to mark dart classes which can be stored into datastore. |
8 /// | 8 /// |
9 /// The `Kind` annotation on a class as well as other `Property` annotations on | 9 /// The `Kind` annotation on a class as well as other `Property` annotations on |
10 /// fields or getters of the class itself (and any of it's superclasses) up to | 10 /// fields or getters of the class itself (and any of it's superclasses) up to |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
81 /// `true`. | 81 /// `true`. |
82 final bool indexed; | 82 final bool indexed; |
83 | 83 |
84 const Property({this.propertyName, this.required: false, this.indexed: true}); | 84 const Property({this.propertyName, this.required: false, this.indexed: true}); |
85 | 85 |
86 bool validate(ModelDB db, Object value) { | 86 bool validate(ModelDB db, Object value) { |
87 if (required && value == null) return false; | 87 if (required && value == null) return false; |
88 return true; | 88 return true; |
89 } | 89 } |
90 | 90 |
91 Object encodeValue(ModelDB db, Object value); | 91 Object encodeValue(ModelDB db, Object value, {bool forComparison: false}); |
92 | 92 |
93 Object decodePrimitiveValue(ModelDB db, Object value); | 93 Object decodePrimitiveValue(ModelDB db, Object value); |
94 } | 94 } |
95 | 95 |
96 /// An abstract base class for primitive properties which can e.g. be used | 96 /// An abstract base class for primitive properties which can e.g. be used |
97 /// within a composed `ListProperty`. | 97 /// within a composed `ListProperty`. |
98 abstract class PrimitiveProperty extends Property { | 98 abstract class PrimitiveProperty extends Property { |
99 const PrimitiveProperty( | 99 const PrimitiveProperty( |
100 {String propertyName, bool required: false, bool indexed: true}) | 100 {String propertyName, bool required: false, bool indexed: true}) |
101 : super(propertyName: propertyName, required: required, indexed: indexed); | 101 : super(propertyName: propertyName, required: required, indexed: indexed); |
102 | 102 |
103 Object encodeValue(ModelDB db, Object value) => value; | 103 Object encodeValue(ModelDB db, Object value, {bool forComparison: false}) => v
alue; |
104 | 104 |
105 Object decodePrimitiveValue(ModelDB db, Object value) => value; | 105 Object decodePrimitiveValue(ModelDB db, Object value) => value; |
106 } | 106 } |
107 | 107 |
108 /// A boolean [Property]. | 108 /// A boolean [Property]. |
109 /// | 109 /// |
110 /// It will validate that values are booleans before writing them to the | 110 /// It will validate that values are booleans before writing them to the |
111 /// datastore and when reading them back. | 111 /// datastore and when reading them back. |
112 class BoolProperty extends PrimitiveProperty { | 112 class BoolProperty extends PrimitiveProperty { |
113 const BoolProperty( | 113 const BoolProperty( |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
162 /// It will validate that values are keys before writing them to the | 162 /// It will validate that values are keys before writing them to the |
163 /// datastore and when reading them back. | 163 /// datastore and when reading them back. |
164 class ModelKeyProperty extends PrimitiveProperty { | 164 class ModelKeyProperty extends PrimitiveProperty { |
165 const ModelKeyProperty( | 165 const ModelKeyProperty( |
166 {String propertyName, bool required: false, bool indexed: true}) | 166 {String propertyName, bool required: false, bool indexed: true}) |
167 : super(propertyName: propertyName, required: required, indexed: indexed); | 167 : super(propertyName: propertyName, required: required, indexed: indexed); |
168 | 168 |
169 bool validate(ModelDB db, Object value) | 169 bool validate(ModelDB db, Object value) |
170 => super.validate(db, value) && (value == null || value is Key); | 170 => super.validate(db, value) && (value == null || value is Key); |
171 | 171 |
172 Object encodeValue(ModelDB db, Object value) { | 172 Object encodeValue(ModelDB db, Object value, {bool forComparison: false}) { |
173 if (value == null) return null; | 173 if (value == null) return null; |
174 return db.toDatastoreKey(value); | 174 return db.toDatastoreKey(value); |
175 } | 175 } |
176 | 176 |
177 Object decodePrimitiveValue(ModelDB db, Object value) { | 177 Object decodePrimitiveValue(ModelDB db, Object value) { |
178 if (value == null) return null; | 178 if (value == null) return null; |
179 return db.fromDatastoreKey(value as datastore.Key); | 179 return db.fromDatastoreKey(value as datastore.Key); |
180 } | 180 } |
181 } | 181 } |
182 | 182 |
183 /// A binary blob [Property]. | 183 /// A binary blob [Property]. |
184 /// | 184 /// |
185 /// It will validate that values are blobs before writing them to the | 185 /// It will validate that values are blobs before writing them to the |
186 /// datastore and when reading them back. Blob values will be represented by | 186 /// datastore and when reading them back. Blob values will be represented by |
187 /// List<int>. | 187 /// List<int>. |
188 class BlobProperty extends PrimitiveProperty { | 188 class BlobProperty extends PrimitiveProperty { |
189 const BlobProperty({String propertyName, bool required: false}) | 189 const BlobProperty({String propertyName, bool required: false}) |
190 : super(propertyName: propertyName, required: required, indexed: false); | 190 : super(propertyName: propertyName, required: required, indexed: false); |
191 | 191 |
192 // NOTE: We don't validate that the entries of the list are really integers | 192 // NOTE: We don't validate that the entries of the list are really integers |
193 // of the range 0..255! | 193 // of the range 0..255! |
194 // If an untyped list was created the type check will always succeed. i.e. | 194 // If an untyped list was created the type check will always succeed. i.e. |
195 // "[1, true, 'bar'] is List<int>" evaluates to `true` | 195 // "[1, true, 'bar'] is List<int>" evaluates to `true` |
196 bool validate(ModelDB db, Object value) | 196 bool validate(ModelDB db, Object value) |
197 => super.validate(db, value) && (value == null || value is List<int>); | 197 => super.validate(db, value) && (value == null || value is List<int>); |
198 | 198 |
199 Object encodeValue(ModelDB db, Object value) { | 199 Object encodeValue(ModelDB db, Object value, {bool forComparison: false}) { |
200 if (value == null) return null; | 200 if (value == null) return null; |
201 return new datastore.BlobValue(value); | 201 return new datastore.BlobValue(value); |
202 } | 202 } |
203 | 203 |
204 Object decodePrimitiveValue(ModelDB db, Object value) { | 204 Object decodePrimitiveValue(ModelDB db, Object value) { |
205 if (value == null) return null; | 205 if (value == null) return null; |
206 | 206 |
207 datastore.BlobValue blobValue = value; | 207 datastore.BlobValue blobValue = value; |
208 return blobValue.bytes; | 208 return blobValue.bytes; |
209 } | 209 } |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
247 | 247 |
248 bool validate(ModelDB db, Object value) { | 248 bool validate(ModelDB db, Object value) { |
249 if (!super.validate(db, value) || value is! List) return false; | 249 if (!super.validate(db, value) || value is! List) return false; |
250 | 250 |
251 for (var entry in value) { | 251 for (var entry in value) { |
252 if (!subProperty.validate(db, entry)) return false; | 252 if (!subProperty.validate(db, entry)) return false; |
253 } | 253 } |
254 return true; | 254 return true; |
255 } | 255 } |
256 | 256 |
257 Object encodeValue(ModelDB db, Object value) { | 257 Object encodeValue(ModelDB db, Object value, {bool forComparison: false}) { |
| 258 if (forComparison) { |
| 259 // If we have comparison of list properties (i.e. repeated property names) |
| 260 // the comparison object must not be a list, but the value itself. |
| 261 // i.e. |
| 262 // |
| 263 // class Article { |
| 264 // ... |
| 265 // @ListProperty(StringProperty()) |
| 266 // List<String> tags; |
| 267 // ... |
| 268 // } |
| 269 // |
| 270 // should be queried via |
| 271 // |
| 272 // await db.query(Article, 'tags=', "Dart").toList(); |
| 273 // |
| 274 // So the [value] for the comparison is of type `String` and not |
| 275 // `List<String>`! |
| 276 return subProperty.encodeValue(db, value, forComparison: true); |
| 277 } |
| 278 |
258 if (value == null) return null; | 279 if (value == null) return null; |
259 List list = value; | 280 List list = value; |
260 if (list.length == 0) return null; | 281 if (list.length == 0) return null; |
261 if (list.length == 1) return subProperty.encodeValue(db, list[0]); | 282 if (list.length == 1) return subProperty.encodeValue(db, list[0]); |
262 return list.map( | 283 return list.map( |
263 (value) => subProperty.encodeValue(db, value)).toList(); | 284 (value) => subProperty.encodeValue(db, value)).toList(); |
264 } | 285 } |
265 | 286 |
266 Object decodePrimitiveValue(ModelDB db, Object value) { | 287 Object decodePrimitiveValue(ModelDB db, Object value) { |
267 if (value == null) return []; | 288 if (value == null) return []; |
268 if (value is! List) return [subProperty.decodePrimitiveValue(db, value)]; | 289 if (value is! List) return [subProperty.decodePrimitiveValue(db, value)]; |
269 return (value as List) | 290 return (value as List) |
270 .map((entry) => subProperty.decodePrimitiveValue(db, entry)) | 291 .map((entry) => subProperty.decodePrimitiveValue(db, entry)) |
271 .toList(); | 292 .toList(); |
272 } | 293 } |
273 } | 294 } |
274 | 295 |
275 /// A convenience [Property] for list of strings. | 296 /// A convenience [Property] for list of strings. |
276 class StringListProperty extends ListProperty { | 297 class StringListProperty extends ListProperty { |
277 const StringListProperty({String propertyName, bool indexed: true}) | 298 const StringListProperty({String propertyName, bool indexed: true}) |
278 : super(const StringProperty(), | 299 : super(const StringProperty(), |
279 propertyName: propertyName, indexed: indexed); | 300 propertyName: propertyName, indexed: indexed); |
280 } | 301 } |
OLD | NEW |