Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(152)

Side by Side Diff: pkg/serialization/lib/serialization.dart

Issue 11553012: Better ability to have hand-written custom rules and various cleanups. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 /** 5 /**
6 * This provides a general-purpose serialization facility for Dart objects. A 6 * This provides a general-purpose serialization facility for Dart objects. A
7 * [Serialization] is defined in terms of [SerializationRule]s and supports 7 * [Serialization] is defined in terms of [SerializationRule]s and supports
8 * reading and writing to different formats. 8 * reading and writing to different formats.
9 * 9 *
10 * Setup 10 * Setup
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
170 170
171 /** 171 /**
172 * When reading, we may need to resolve references to existing objects in 172 * When reading, we may need to resolve references to existing objects in
173 * the system. The right action may not be to create a new instance of 173 * the system. The right action may not be to create a new instance of
174 * something, but rather to find an existing instance and connect to it. 174 * something, but rather to find an existing instance and connect to it.
175 * For example, if we have are serializing an Email message and it has a 175 * For example, if we have are serializing an Email message and it has a
176 * link to the owning account, it may not be appropriate to try and serialize 176 * link to the owning account, it may not be appropriate to try and serialize
177 * the account. Instead we should just connect the de-serialized message 177 * the account. Instead we should just connect the de-serialized message
178 * object to the account object that already exists there. 178 * object to the account object that already exists there.
179 */ 179 */
180 Map<String, dynamic> externalObjects = {}; 180 Map<String, dynamic> namedObjects = {};
181 181
182 /** 182 /**
183 * When we write out data using this serialization, should we also write 183 * When we write out data using this serialization, should we also write
184 * out a description of the rules. 184 * out a description of the rules. This is on by default unless using
185 * CustomRule subclasses, in which case it requires additional setup and
186 * is off by default.
185 */ 187 */
186 bool selfDescribing = true; 188 bool _selfDescribing;
189
190 /**
191 * When we write out data using this serialization, should we also write
192 * out a description of the rules. This is on by default unless using
193 * CustomRule subclasses, in which case it requires additional setup and
194 * is off by default.
195 */
196 bool get selfDescribing {
197 if (_selfDescribing != null) return _selfDescribing;
198 _selfDescribing = !rules.some((x) => x is CustomRule);
Jennifer Messerly 2012/12/12 20:38:28 Do we know that the set of "rules" is fixed at thi
Alan Knight 2012/12/12 21:19:33 Yes, this was a cheesy attempt to make it default
199 return _selfDescribing;
200 }
201
202 /**
203 * When we write out data using this serialization, should we also write
204 * out a description of the rules. This is on by default unless using
205 * CustomRule subclasses, in which case it requires additional setup and
206 * is off by default.
207 */
208 set selfDescribing(x) => _selfDescribing = x;
187 209
188 /** 210 /**
189 * Creates a new serialization with a default set of rules for primitives 211 * Creates a new serialization with a default set of rules for primitives
190 * and lists. 212 * and lists.
191 */ 213 */
192 Serialization() { 214 Serialization() {
193 addDefaultRules(); 215 addDefaultRules();
194 } 216 }
195 217
196 /** 218 /**
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
248 // it will always find the first one. 270 // it will always find the first one.
249 addRule(new ListRuleEssential()); 271 addRule(new ListRuleEssential());
250 } 272 }
251 273
252 /** 274 /**
253 * Add a new SerializationRule [rule]. The addRuleFor method will probably 275 * Add a new SerializationRule [rule]. The addRuleFor method will probably
254 * handle most simple cases, but for adding an arbitrary rule, including 276 * handle most simple cases, but for adding an arbitrary rule, including
255 * a SerializationRule subclass which you have created, you can use this 277 * a SerializationRule subclass which you have created, you can use this
256 * method. 278 * method.
257 */ 279 */
258 void addRule(SerializationRule rule) { 280 void addRule(SerializationRule rule) {
Jennifer Messerly 2012/12/12 20:38:28 An issue I just noticed (maybe we already have a T
Alan Knight 2012/12/12 21:19:33 Made rules private. If it starts seem like people
259 rule.number = rules.length; 281 rule.number = rules.length;
260 rules.add(rule); 282 rules.add(rule);
261 } 283 }
262 284
263 /** 285 /**
264 * This is the basic method to write out an object graph rooted at 286 * This is the basic method to write out an object graph rooted at
265 * [object] and return the result. Right now this is hard-coded to return 287 * [object] and return the result. Right now this is hard-coded to return
266 * a String from a custom [JSON] format, but that is likely to change to be 288 * a String from a custom [JSON] format, but that is likely to change to be
267 * more pluggable in the near future. 289 * more pluggable in the near future.
268 */ 290 */
(...skipping 10 matching lines...) Expand all
279 301
280 /** 302 /**
281 * Write out the tree in a custom flat format, returning a list containing 303 * Write out the tree in a custom flat format, returning a list containing
282 * only "simple" types: num, String, and bool. 304 * only "simple" types: num, String, and bool.
283 */ 305 */
284 List writeFlat(Object object) { 306 List writeFlat(Object object) {
285 return newWriter().writeFlat(object); 307 return newWriter().writeFlat(object);
286 } 308 }
287 309
288 /** 310 /**
289 * Read the serialized data from [input] and return a List of the root 311 * Read the serialized data from [input] and return the root object
290 * objects from the result. If there are objects that need to be resolved 312 * from the result. If there are objects that need to be resolved
291 * in the current context, they should be provided in [externals] as a 313 * in the current context, they should be provided in [externals] as a
292 * Map from names to values. In particular, in the current implementation 314 * Map from names to values. In particular, in the current implementation
293 * any class mirrors needed should be provided in [externals] using the 315 * any class mirrors needed should be provided in [externals] using the
294 * class name as a key. In addition to the [externals] map provided here, 316 * class name as a key. In addition to the [externals] map provided here,
295 * values will be looked up in the [externalObjects] map. 317 * values will be looked up in the [externalObjects] map.
296 */ 318 */
297 List read(String input, [Map externals = const {}]) { 319 read(String input, [Map externals = const {}]) {
298 return newReader().read(input, externals); 320 return newReader().read(input, externals);
299 } 321 }
300 322
301 /** 323 /**
302 * In the most common case there is only a single root object to be read,
303 * and this method can be used to return just one object rather than
304 * a List. The [input] and [externals] parameters are the same as for the
305 * general [read] method.
306 */
307 Object readOne(String input, [Map externals = const {}]) {
308 return newReader().readOne(input, externals);
309 }
310
311 /**
312 * Return a new [Reader] object for this serialization. This is useful if 324 * Return a new [Reader] object for this serialization. This is useful if
313 * you want to do something more complex with the reader than just returning 325 * you want to do something more complex with the reader than just returning
314 * the final result. 326 * the final result.
315 */ 327 */
316 Reader newReader() => new Reader(this); 328 Reader newReader() => new Reader(this);
317 329
318 /** 330 /**
319 * Return the list of SerializationRule that apply to [object]. For 331 * Return the list of SerializationRule that apply to [object]. For
320 * internal use, but public because it's used in testing. 332 * internal use, but public because it's used in testing.
321 */ 333 */
322 List<SerializationRule> rulesFor(object) { 334 List<SerializationRule> rulesFor(object, Writer w) {
323 // This has a couple of edge cases. 335 // This has a couple of edge cases.
324 // 1) The owning object may have indicated we should use a different 336 // 1) The owning object may have indicated we should use a different
325 // rule than the default. 337 // rule than the default.
326 // 2) We may not have a rule, in which case we lazily create a BasicRule. 338 // 2) We may not have a rule, in which case we lazily create a BasicRule.
327 // 3) Rules are allowed to say mustBePrimary, meaning that they can be used 339 // 3) Rules are allowed to say mustBePrimary, meaning that they can be used
328 // iff no other rule was chosen first. 340 // iff no other rule was chosen first.
329 // TODO(alanknight): Can the mustBePrimary mechanism be removed or changed. 341 // TODO(alanknight): Can the mustBePrimary mechanism be removed or changed.
330 // It adds an order dependency to the rules, and is messy. Reconsider in the 342 // It adds an order dependency to the rules, and is messy. Reconsider in the
331 // light of a more general mechanism for multiple rules per object. 343 // light of a more general mechanism for multiple rules per object.
332 // TODO(alanknight): Finding which rules apply seems likely to be a 344 // TODO(alanknight): Finding which rules apply seems likely to be a
333 // bottleneck, particularly with the current reflective implementation. 345 // bottleneck, particularly with the current reflective implementation.
334 // Consider how to improve it. e.g. cache the list of rules by class. But 346 // Consider how to improve it. e.g. cache the list of rules by class. But
335 // be careful of issues like rules which have arbitrary predicates. Or 347 // be careful of issues like rules which have arbitrary predicates. Or
336 // consider having the arbitrary predicates be secondary to an initial 348 // consider having the arbitrary predicates be secondary to an initial
337 // class-based lookup mechanism. 349 // class-based lookup mechanism.
338 var target, candidateRules; 350 var target, candidateRules;
339 if (object is DesignatedRuleForObject) { 351 if (object is DesignatedRuleForObject) {
340 target = object.target; 352 target = object.target;
341 candidateRules = object.possibleRules(rules); 353 candidateRules = object.possibleRules(rules);
342 } else { 354 } else {
343 target = object; 355 target = object;
344 candidateRules = rules; 356 candidateRules = rules;
345 } 357 }
346 List applicable = candidateRules.filter((each) => each.appliesTo(target)); 358 List applicable = candidateRules.filter(
359 (each) => each.appliesTo(target, w));
347 360
348 if (applicable.isEmpty) { 361 if (applicable.isEmpty) {
349 return [addRuleFor(target)]; 362 return [addRuleFor(target)];
350 } 363 }
351 364
352 if (applicable.length == 1) return applicable; 365 if (applicable.length == 1) return applicable;
353 var first = applicable[0]; 366 var first = applicable[0];
354 var finalRules = applicable.filter( 367 var finalRules = applicable.filter(
355 (x) => !x.mustBePrimary || (x == first)); 368 (x) => !x.mustBePrimary || (x == first));
356 369
(...skipping 10 matching lines...) Expand all
367 */ 380 */
368 Serialization _ruleSerialization() { 381 Serialization _ruleSerialization() {
369 // TODO(alanknight): There's an extensibility issue here with new rules. 382 // TODO(alanknight): There's an extensibility issue here with new rules.
370 // TODO(alanknight): How to handle rules with closures? They have to 383 // TODO(alanknight): How to handle rules with closures? They have to
371 // exist on the other side, but we might be able to hook them up by name, 384 // exist on the other side, but we might be able to hook them up by name,
372 // or we might just be able to validate that they're correctly set up 385 // or we might just be able to validate that they're correctly set up
373 // on the other side. 386 // on the other side.
374 387
375 // Make some bogus rule instances so we have something to feed rule creation 388 // Make some bogus rule instances so we have something to feed rule creation
376 // and get their types. If only we had class literals implemented... 389 // and get their types. If only we had class literals implemented...
377 var closureRule = new ClosureToMapRule.stub([].runtimeType); 390 var basicRule = new BasicRule(reflect(null).type, '', [], [], []);
378 var basicRule = new BasicRule(reflect(null).type, '', [], [], []);
379 391
380 var meta = new Serialization() 392 var meta = new Serialization()
381 ..selfDescribing = false 393 ..selfDescribing = false
382 ..addRuleFor(new ListRule()) 394 ..addRuleFor(new ListRule())
383 ..addRuleFor(new PrimitiveRule()) 395 ..addRuleFor(new PrimitiveRule())
384 ..addRuleFor(new ListRuleEssential()) 396 ..addRuleFor(new ListRuleEssential())
385 ..addRuleFor(basicRule, 397 ..addRuleFor(basicRule,
386 constructorFields: ['typeWrapped', 398 constructorFields: ['typeWrapped',
387 'constructorName', 399 'constructorName',
388 'constructorFields', 'regularFields', []], 400 'constructorFields', 'regularFields', []],
389 fields: []) 401 fields: [])
390 ..addRule(new ClassMirrorRule()); 402 ..addRule(new NamedObjectRule())
391 meta.externalObjects = externalObjects; 403 ..addRule(new MirrorRule());
404 meta.namedObjects = namedObjects;
392 return meta; 405 return meta;
393 } 406 }
407
408 /** Return true if our [namedObjects] collection has an entry for [object].*/
409 bool _hasNameFor(object) {
410 var sentinel = const _Sentinel();
411 return _nameFor(object, () => sentinel) != sentinel;
412 }
413
414 /**
415 * Return the name we have for [object] in our [namedObjects] collection or
416 * the result of evaluating [ifAbsent] if there is no entry.
417 */
418 _nameFor(object, [ifAbsent]) {
419 for (var key in namedObjects.keys) {
420 if (identical(namedObjects[key], object)) return key;
421 }
422 return ifAbsent == null ? null : ifAbsent();
423 }
394 } 424 }
395 425
396 /** 426 /**
397 * An exception class for errors during serialization. 427 * An exception class for errors during serialization.
398 */ 428 */
399 class SerializationException implements Exception { 429 class SerializationException implements Exception {
400 final String message; 430 final String message;
401 const SerializationException([this.message]); 431 const SerializationException([this.message]);
402 } 432 }
OLDNEW
« no previous file with comments | « no previous file | pkg/serialization/lib/src/basic_rule.dart » ('j') | pkg/serialization/lib/src/reader_writer.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698