OLD | NEW |
(Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 part of serialization; |
| 6 |
| 7 // TODO(alanknight): We should have an example and tests for subclassing |
| 8 // serialization rule rather than using the hard-coded ClosureToMap rule. And |
| 9 // possibly an abstract superclass that's designed to be subclassed that way. |
| 10 /** |
| 11 * The abstract superclass for serialization rules. |
| 12 */ |
| 13 abstract class SerializationRule { |
| 14 /** |
| 15 * Rules belong uniquely to a particular Serialization instance, and can |
| 16 * be identified within it by number. |
| 17 */ |
| 18 int _number; |
| 19 |
| 20 /** |
| 21 * Rules belong uniquely to a particular Serialization instance, and can |
| 22 * be identified within it by number. |
| 23 */ |
| 24 int get number => _number; |
| 25 |
| 26 /** |
| 27 * Rules belong uniquely to a particular Serialization instance, and can |
| 28 * be identified within it by number. |
| 29 */ |
| 30 void set number(int value) { |
| 31 if (_number != null && _number != value) throw |
| 32 new SerializationException("Rule numbers cannot be changed, once set"); |
| 33 _number = value; |
| 34 } |
| 35 |
| 36 /** |
| 37 * Return true if this rule applies to this object, in the context |
| 38 * where we're writing it, false otherwise. |
| 39 */ |
| 40 bool appliesTo(object, Writer writer); |
| 41 |
| 42 /** |
| 43 * This extracts the state from the object, calling [f] for each value |
| 44 * as it is extracted, and returning an object representing the whole |
| 45 * state at the end. The state that results will still have direct |
| 46 * pointers to objects, rather than references. |
| 47 */ |
| 48 extractState(object, void f(value), Writer w); |
| 49 |
| 50 /** |
| 51 * Allows rules to tell us how they expect to store their state. If this |
| 52 * isn't specified we can also just look at the data to tell. |
| 53 */ |
| 54 bool get storesStateAsLists => false; |
| 55 bool get storesStateAsMaps => false; |
| 56 bool get storesStateAsPrimitives => false; |
| 57 |
| 58 /** |
| 59 * Given the variables representing the state of an object, flatten it |
| 60 * by turning object pointers into Reference objects where needed. This |
| 61 * destructively modifies the state object. |
| 62 * |
| 63 * This has a default implementation which assumes that object is indexable, |
| 64 * so either conforms to Map or List. Subclasses may override to do something |
| 65 * different, including returning a new state object to be used in place |
| 66 * of the original. |
| 67 */ |
| 68 // This has to be a separate operation from extracting, because we extract |
| 69 // as we are traversing the objects, so we don't yet have the objects to |
| 70 // generate references for them. It might be possible to avoid that by |
| 71 // doing a depth-first rather than breadth-first traversal, but I'm not |
| 72 // sure it's worth it. |
| 73 flatten(state, Writer writer) { |
| 74 keysAndValues(state).forEach((key, value) { |
| 75 var reference = writer._referenceFor(value); |
| 76 state[key] = reference; |
| 77 }); |
| 78 } |
| 79 |
| 80 /** Return true if this rule should only be applied when we are the first |
| 81 * rule found that applies to this object. This may or may not be a hack |
| 82 * that will disappear once we have better support for multiple rules. |
| 83 * We want to have multiple different rules that apply to the same object. We |
| 84 * also want to have multiple different rules that might exclusively apply |
| 85 * to the same object. So, we want either ListRule or ListRuleEssential, and |
| 86 * only one of them can be there. But on the other hand, we may want both |
| 87 * ListRule and BasicRule. So we identify the kinds of rules that can share. |
| 88 * If mustBePrimary returns true, then this rule will only be chosen if no |
| 89 * other rule has been found yet. This means that the ordering of rules in |
| 90 * the serialization is significant, which is unpleasant, but we'll have |
| 91 * to see how bad it is. |
| 92 */ |
| 93 // TODO(alanknight): Reconsider whether this should be handled differently. |
| 94 bool get mustBePrimary => false; |
| 95 |
| 96 /** |
| 97 * Create the new object corresponding to [state] using the rules |
| 98 * from [reader]. This may involve recursively inflating "essential" |
| 99 * references in the state, which are those that are required for the |
| 100 * object's constructor. It is up to the rule what state is considered |
| 101 * essential. |
| 102 */ |
| 103 inflateEssential(state, Reader reader); |
| 104 |
| 105 /** |
| 106 * The [object] has already been created. Set any of its non-essential |
| 107 * variables from the representation in [state]. Where there are references |
| 108 * to other objects they are resolved in the context of [reader]. |
| 109 */ |
| 110 void inflateNonEssential(state, object, Reader reader); |
| 111 |
| 112 /** |
| 113 * If we have [object] as part of our state, should we represent that |
| 114 * directly, or should we make a reference for it. By default, true. |
| 115 * This may also delegate to [writer]. |
| 116 */ |
| 117 bool shouldUseReferenceFor(object, Writer writer) => true; |
| 118 |
| 119 /** |
| 120 * Return true if the data this rule returns is variable length, so a |
| 121 * length needs to be written for it if the format requires that. Return |
| 122 * false if the results are always the same length. |
| 123 */ |
| 124 bool get hasVariableLengthEntries => true; |
| 125 |
| 126 /** |
| 127 * If the data is fixed length, return it here. The format may or may not |
| 128 * make use of this, depending on whether it already has enough information |
| 129 * to determine the length on its own. If [hasVariableLengthEntries] is true |
| 130 * this is ignored. |
| 131 */ |
| 132 int get dataLength => 0; |
| 133 } |
| 134 |
| 135 /** |
| 136 * This rule handles things that implement List. It will recreate them as |
| 137 * whatever the default implemenation of List is on the target platform. |
| 138 */ |
| 139 class ListRule extends SerializationRule { |
| 140 |
| 141 bool appliesTo(object, Writer w) => object is List; |
| 142 |
| 143 bool get storesStateAsLists => true; |
| 144 |
| 145 List extractState(List list, f, Writer w) { |
| 146 var result = new List(); |
| 147 for (var each in list) { |
| 148 result.add(each); |
| 149 f(each); |
| 150 } |
| 151 return result; |
| 152 } |
| 153 |
| 154 inflateEssential(List state, Reader r) => new List(); |
| 155 |
| 156 // For a list, we consider all of its state non-essential and add it |
| 157 // after creation. |
| 158 void inflateNonEssential(List state, List newList, Reader r) { |
| 159 populateContents(state, newList, r); |
| 160 } |
| 161 |
| 162 void populateContents(List state, List newList, Reader r) { |
| 163 for(var each in state) { |
| 164 newList.add(r.inflateReference(each)); |
| 165 } |
| 166 } |
| 167 |
| 168 bool get hasVariableLengthEntries => true; |
| 169 } |
| 170 |
| 171 /** |
| 172 * This is a subclass of ListRule where all of the list's contents are |
| 173 * considered essential state. This is needed if an object X contains a List L, |
| 174 * but it expects L's contents to be fixed when X's constructor is called. |
| 175 */ |
| 176 class ListRuleEssential extends ListRule { |
| 177 |
| 178 /** Create the new List and also inflate all of its contents. */ |
| 179 inflateEssential(List state, Reader r) { |
| 180 var object = super.inflateEssential(state, r); |
| 181 populateContents(state, object, r); |
| 182 return object; |
| 183 } |
| 184 |
| 185 /** Does nothing, because all the work has been done in inflateEssential. */ |
| 186 void inflateNonEssential(state, newList, reader) {} |
| 187 |
| 188 bool get mustBePrimary => true; |
| 189 } |
| 190 |
| 191 /** |
| 192 * This rule handles things that implement Map. It will recreate them as |
| 193 * whatever the default implemenation of Map is on the target platform. If a |
| 194 * map has string keys it will attempt to retain it as a map for JSON formats, |
| 195 * otherwise it will store it as a list of references to keys and values. |
| 196 */ |
| 197 class MapRule extends SerializationRule { |
| 198 |
| 199 bool appliesTo(object, Writer w) => object is Map; |
| 200 |
| 201 bool get storesStateAsMaps => true; |
| 202 |
| 203 extractState(Map map, f, Writer w) { |
| 204 // Note that we make a copy here because flattening may be destructive. |
| 205 var newMap = new Map.from(map); |
| 206 newMap.forEach((key, value) { |
| 207 f(key); |
| 208 f(value); |
| 209 }); |
| 210 return newMap; |
| 211 } |
| 212 |
| 213 /** |
| 214 * Change the keys and values of [state] into references in [writer]. |
| 215 * If [state] is a map whose keys are all strings then we leave the keys |
| 216 * as is so that JSON formats will be more readable. If the keys are |
| 217 * arbitrary then we need to turn them into references and replace the |
| 218 * state with a new Map whose keys are the references. |
| 219 */ |
| 220 flatten(Map state, Writer writer) { |
| 221 bool keysAreAllStrings = state.keys.every((x) => x is String); |
| 222 if (keysAreAllStrings && !writer.shouldUseReferencesForPrimitives) { |
| 223 keysAndValues(state).forEach( |
| 224 (key, value) => state[key] = writer._referenceFor(value)); |
| 225 return state; |
| 226 } else { |
| 227 var newState = []; |
| 228 keysAndValues(state).forEach((key, value) { |
| 229 newState.add(writer._referenceFor(key)); |
| 230 newState.add(writer._referenceFor(value)); |
| 231 }); |
| 232 return newState; |
| 233 } |
| 234 } |
| 235 |
| 236 inflateEssential(state, Reader r) => new Map(); |
| 237 |
| 238 // For a map, we consider all of its state non-essential and add it |
| 239 // after creation. |
| 240 void inflateNonEssential(state, Map newMap, Reader r) { |
| 241 if (state is List) { |
| 242 inflateNonEssentialFromList(state, newMap, r); |
| 243 } else { |
| 244 inflateNonEssentialFromMap(state, newMap, r); |
| 245 } |
| 246 } |
| 247 |
| 248 void inflateNonEssentialFromList(List state, Map newMap, Reader r) { |
| 249 var key; |
| 250 for (var each in state) { |
| 251 if (key == null) { |
| 252 key = each; |
| 253 } else { |
| 254 newMap[r.inflateReference(key)] = r.inflateReference(each); |
| 255 key = null; |
| 256 } |
| 257 } |
| 258 } |
| 259 |
| 260 void inflateNonEssentialFromMap(Map state, Map newMap, Reader r) { |
| 261 state.forEach((key, value) { |
| 262 newMap[r.inflateReference(key)] = r.inflateReference(value); |
| 263 }); |
| 264 } |
| 265 |
| 266 bool get hasVariableLengthEntries => true; |
| 267 } |
| 268 |
| 269 /** |
| 270 * This rule handles primitive types, defined as those that we can normally |
| 271 * represent directly in the output format. We hard-code that to mean |
| 272 * num, String, and bool. |
| 273 */ |
| 274 class PrimitiveRule extends SerializationRule { |
| 275 bool appliesTo(object, Writer w) => isPrimitive(object); |
| 276 extractState(object, Function f, Writer w) => object; |
| 277 flatten(object, Writer writer) {} |
| 278 inflateEssential(state, Reader r) => state; |
| 279 void inflateNonEssential(object, _, Reader r) {} |
| 280 |
| 281 bool get storesStateAsPrimitives => true; |
| 282 |
| 283 /** |
| 284 * Indicate whether we should save pointers to this object as references |
| 285 * or store the object directly. For primitives this depends on the format, |
| 286 * so we delegate to the writer. |
| 287 */ |
| 288 bool shouldUseReferenceFor(object, Writer w) => |
| 289 w.shouldUseReferencesForPrimitives; |
| 290 |
| 291 bool get hasVariableLengthEntries => false; |
| 292 } |
| 293 |
| 294 /** Typedef for the object construction closure used in ClosureRule. */ |
| 295 typedef ConstructType(Map m); |
| 296 |
| 297 /** Typedef for the state-getting closure used in ClosureToMapRule. */ |
| 298 typedef Map<String, dynamic> GetStateType(object); |
| 299 |
| 300 /** Typedef for the state-setting closure used in ClosureToMapRule. */ |
| 301 typedef void NonEssentialStateType(object, Map m); |
| 302 |
| 303 /** |
| 304 * This is a rule where the extraction and creation are hard-coded as |
| 305 * closures. The result is expected to be a map indexed by field name. |
| 306 */ |
| 307 class ClosureRule extends CustomRule { |
| 308 |
| 309 /** The runtimeType of objects that this rule applies to. Used in appliesTo.*/ |
| 310 final Type type; |
| 311 |
| 312 /** The function for constructing new objects when reading. */ |
| 313 final ConstructType construct; |
| 314 |
| 315 /** The function for returning an object's state as a Map. */ |
| 316 final GetStateType getStateFunction; |
| 317 |
| 318 /** The function for setting an object's state from a Map. */ |
| 319 final NonEssentialStateType setNonEssentialState; |
| 320 |
| 321 /** |
| 322 * Create a ClosureToMapRule for the given [type] which gets an object's |
| 323 * state by calling [getState], creates a new object by calling [construct] |
| 324 * and sets the new object's state by calling [setNonEssentialState]. |
| 325 */ |
| 326 ClosureRule(this.type, this.getStateFunction, this.construct, |
| 327 this.setNonEssentialState); |
| 328 |
| 329 bool appliesTo(object, Writer w) => object.runtimeType == type; |
| 330 |
| 331 getState(object) => getStateFunction(object); |
| 332 |
| 333 create(state) => construct(state); |
| 334 |
| 335 void setState(object, state) { |
| 336 if (setNonEssentialState == null) return; |
| 337 setNonEssentialState(object, state); |
| 338 } |
| 339 } |
| 340 |
| 341 /** |
| 342 * This rule handles things we can't pass directly, but only by reference. |
| 343 * If objects are listed in the namedObjects in the writer or serialization, |
| 344 * it will save the name rather than saving the state. |
| 345 */ |
| 346 class NamedObjectRule extends SerializationRule { |
| 347 /** |
| 348 * Return true if this rule applies to the object. Checked by looking up |
| 349 * in the namedObjects collection. |
| 350 */ |
| 351 bool appliesTo(object, Writer writer) { |
| 352 return writer.hasNameFor(object); |
| 353 } |
| 354 |
| 355 /** Extract the state of the named objects as just the object itself. */ |
| 356 extractState(object, Function f, Writer writer) { |
| 357 var result = [nameFor(object, writer)]; |
| 358 f(result.first); |
| 359 return result; |
| 360 } |
| 361 |
| 362 /** When we flatten the state we save it as the name. */ |
| 363 // TODO(alanknight): This seems questionable. In a truly flat format we may |
| 364 // want to have extracted the name as a string first and flatten it into a |
| 365 // reference to that. But that requires adding the Writer as a parameter to |
| 366 // extractState, and I'm reluctant to add yet another parameter until |
| 367 // proven necessary. |
| 368 flatten(state, Writer writer) { |
| 369 state[0] = writer._referenceFor(state[0]); |
| 370 } |
| 371 |
| 372 /** Look up the named object and return it. */ |
| 373 inflateEssential(state, Reader r) => |
| 374 r.objectNamed(r.resolveReference(state.first)); |
| 375 |
| 376 /** Set any non-essential state on the object. For this rule, a no-op. */ |
| 377 void inflateNonEssential(state, object, Reader r) {} |
| 378 |
| 379 /** Return the name for this object in the Writer. */ |
| 380 String nameFor(object, Writer writer) => writer.nameFor(object); |
| 381 } |
| 382 |
| 383 /** |
| 384 * This rule handles the special case of Mirrors. It stores the mirror by its |
| 385 * qualifiedName and attempts to look it up in both the namedObjects |
| 386 * collection, or if it's not found there, by looking it up in the mirror |
| 387 * system. When reading, the user is responsible for supplying the appropriate |
| 388 * values in [Serialization.namedObjects] or in the [externals] paramter to |
| 389 * [Serialization.read]. |
| 390 */ |
| 391 class MirrorRule extends NamedObjectRule { |
| 392 bool appliesTo(object, Writer writer) => object is DeclarationMirror; |
| 393 |
| 394 String nameFor(DeclarationMirror object, Writer writer) => |
| 395 MirrorSystem.getName(object.qualifiedName); |
| 396 |
| 397 inflateEssential(state, Reader r) { |
| 398 var qualifiedName = r.resolveReference(state.first); |
| 399 var lookupFull = r.objectNamed(qualifiedName, (x) => null); |
| 400 if (lookupFull != null) return lookupFull; |
| 401 var separatorIndex = qualifiedName.lastIndexOf("."); |
| 402 var type = qualifiedName.substring(separatorIndex + 1); |
| 403 var lookup = r.objectNamed(type, (x) => null); |
| 404 if (lookup != null) return lookup; |
| 405 var name = qualifiedName.substring(0, separatorIndex); |
| 406 // This is very ugly. The library name for an unnamed library is its URI. |
| 407 // That can't be constructed as a Symbol, so we can't use findLibrary. |
| 408 // So follow one or the other path depending if it has a colon, which we |
| 409 // assume is in any URI and can't be in a Symbol. |
| 410 if (name.contains(":")) { |
| 411 var uri = Uri.parse(name); |
| 412 var libMirror = currentMirrorSystem().libraries[uri]; |
| 413 var candidate = libMirror.declarations[new Symbol(type)]; |
| 414 return candidate is ClassMirror ? candidate : null; |
| 415 } else { |
| 416 var symbol = new Symbol(name); |
| 417 var typeSymbol = new Symbol(type); |
| 418 for (var libMirror in currentMirrorSystem().libraries.values) { |
| 419 if (libMirror.simpleName != symbol) continue; |
| 420 var candidate = libMirror.declarations[typeSymbol]; |
| 421 if (candidate != null && candidate is ClassMirror) return candidate; |
| 422 } |
| 423 return null; |
| 424 } |
| 425 } |
| 426 } |
| 427 |
| 428 /** |
| 429 * This provides an abstract superclass for writing your own rules specific to |
| 430 * a class. It makes some assumptions about behaviour, and so can have a |
| 431 * simpler set of methods that need to be implemented in order to subclass it. |
| 432 * |
| 433 */ |
| 434 abstract class CustomRule extends SerializationRule { |
| 435 // TODO(alanknight): It would be nice if we could provide an implementation |
| 436 // of appliesTo() here. If we add a type parameter to these classes |
| 437 // we can "is" test against it, but we need to be able to rule out subclasses. |
| 438 // => instance.runtimeType == T |
| 439 // should work. |
| 440 /** |
| 441 * Return true if this rule applies to this object, in the context |
| 442 * where we're writing it, false otherwise. |
| 443 */ |
| 444 bool appliesTo(instance, Writer w); |
| 445 |
| 446 /** |
| 447 * Subclasses should implement this to return a list of the important fields |
| 448 * in the object. The order of the fields doesn't matter, except that the |
| 449 * create and setState methods need to know how to use it. |
| 450 */ |
| 451 List getState(instance); |
| 452 |
| 453 /** |
| 454 * Given a [List] of the object's [state], re-create the object. This should |
| 455 * do the minimum needed to create the object, just calling the constructor. |
| 456 * Setting the remaining state of the object should be done in the [setState] |
| 457 * method, which will be called only once all the objects are created, so |
| 458 * it won't cause problems with cycles. |
| 459 */ |
| 460 create(List state); |
| 461 |
| 462 /** |
| 463 * Set any state in [object] which wasn't set in the constructor. Between |
| 464 * this method and [create] all of the information in [state] should be set |
| 465 * in the new object. |
| 466 */ |
| 467 void setState(object, List state); |
| 468 |
| 469 extractState(instance, Function f, Writer w) { |
| 470 var state = getState(instance); |
| 471 for (var each in values(state)) { |
| 472 f(each); |
| 473 } |
| 474 return state; |
| 475 } |
| 476 |
| 477 inflateEssential(state, Reader r) => create(_lazy(state, r)); |
| 478 |
| 479 void inflateNonEssential(state, object, Reader r) { |
| 480 setState(object, _lazy(state, r)); |
| 481 } |
| 482 |
| 483 // We don't want to have to make the end user tell us how long the list is |
| 484 // separately, so write it out for each object, even though they're all |
| 485 // expected to be the same length. |
| 486 bool get hasVariableLengthEntries => true; |
| 487 } |
| 488 |
| 489 /** A hard-coded rule for serializing Symbols. */ |
| 490 class SymbolRule extends CustomRule { |
| 491 bool appliesTo(instance, _) => instance is Symbol; |
| 492 getState(instance) => [MirrorSystem.getName(instance)]; |
| 493 create(state) => new Symbol(state[0]); |
| 494 void setState(symbol, state) {} |
| 495 int get dataLength => 1; |
| 496 bool get hasVariableLengthEntries => false; |
| 497 } |
| 498 |
| 499 /** A hard-coded rule for DateTime. */ |
| 500 class DateTimeRule extends CustomRule { |
| 501 bool appliesTo(instance, _) => instance is DateTime; |
| 502 List getState(DateTime date) => [date.millisecondsSinceEpoch, date.isUtc]; |
| 503 DateTime create(List state) => |
| 504 new DateTime.fromMillisecondsSinceEpoch(state[0], isUtc: state[1]); |
| 505 void setState(date, state) {} |
| 506 // Let the system know we don't have to store a length for these. |
| 507 int get dataLength => 2; |
| 508 bool get hasVariableLengthEntries => false; |
| 509 } |
| 510 |
| 511 /** Create a lazy list/map that will inflate its items on demand in [r]. */ |
| 512 _lazy(l, Reader r) { |
| 513 if (l is List) return new _LazyList(l, r); |
| 514 if (l is Map) return new _LazyMap(l, r); |
| 515 throw new SerializationException("Invalid type: must be Map or List - $l"); |
| 516 } |
| 517 |
| 518 /** |
| 519 * This provides an implementation of Map that wraps a list which may |
| 520 * contain references to (potentially) non-inflated objects. If these |
| 521 * are accessed it will inflate them. This allows us to pass something that |
| 522 * looks like it's just a list of objects to a [CustomRule] without needing |
| 523 * to inflate all the references in advance. |
| 524 */ |
| 525 class _LazyMap implements Map { |
| 526 _LazyMap(this._raw, this._reader); |
| 527 |
| 528 final Map _raw; |
| 529 final Reader _reader; |
| 530 |
| 531 // This is the only operation that really matters. |
| 532 operator [](x) => _reader.inflateReference(_raw[x]); |
| 533 |
| 534 int get length => _raw.length; |
| 535 bool get isEmpty => _raw.isEmpty; |
| 536 bool get isNotEmpty => _raw.isNotEmpty; |
| 537 Iterable get keys => _raw.keys; |
| 538 bool containsKey(x) => _raw.containsKey(x); |
| 539 |
| 540 // These operations will work, but may be expensive, and are probably |
| 541 // best avoided. |
| 542 get _inflated => mapValues(_raw, _reader.inflateReference); |
| 543 bool containsValue(x) => _inflated.containsValue(x); |
| 544 Iterable get values => _inflated.values; |
| 545 void forEach(f) => _inflated.forEach(f); |
| 546 |
| 547 // These operations are all invalid |
| 548 _throw() { |
| 549 throw new UnsupportedError("Not modifiable"); |
| 550 } |
| 551 operator []=(x, y) => _throw(); |
| 552 putIfAbsent(x, y) => _throw(); |
| 553 bool remove(x) => _throw(); |
| 554 void clear() => _throw(); |
| 555 void addAll(Map other) => _throw(); |
| 556 } |
| 557 |
| 558 /** |
| 559 * This provides an implementation of List that wraps a list which may |
| 560 * contain references to (potentially) non-inflated objects. If these |
| 561 * are accessed it will inflate them. This allows us to pass something that |
| 562 * looks like it's just a list of objects to a [CustomRule] without needing |
| 563 * to inflate all the references in advance. |
| 564 */ |
| 565 class _LazyList extends ListBase { |
| 566 _LazyList(this._raw, this._reader); |
| 567 |
| 568 final List _raw; |
| 569 final Reader _reader; |
| 570 |
| 571 operator [](int x) => _reader.inflateReference(_raw[x]); |
| 572 int get length => _raw.length; |
| 573 |
| 574 void set length(int value) => _throw(); |
| 575 |
| 576 void operator []=(int index, value) => _throw(); |
| 577 |
| 578 void _throw() { |
| 579 throw new UnsupportedError("Not modifiable"); |
| 580 } |
| 581 } |
OLD | NEW |