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

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

Issue 14793016: pkg/serialization types cleanup (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: const is better, right? Created 7 years, 7 months 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
« no previous file with comments | « pkg/serialization/lib/serialization.dart ('k') | pkg/serialization/lib/src/format.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 part of serialization; 5 part of serialization;
6 6
7 // TODO(alanknight): Figure out how to reasonably separate out the things 7 // TODO(alanknight): Figure out how to reasonably separate out the things
8 // that require reflection without making the API more awkward. Or if that is 8 // that require reflection without making the API more awkward. Or if that is
9 // in fact necessary. Maybe the tree-shaking will just remove it if unused. 9 // in fact necessary. Maybe the tree-shaking will just remove it if unused.
10 10
(...skipping 10 matching lines...) Expand all
21 /** 21 /**
22 * The [type] is used both to find fields and to verify if the object is one 22 * The [type] is used both to find fields and to verify if the object is one
23 * that we handle. 23 * that we handle.
24 */ 24 */
25 final ClassMirror type; 25 final ClassMirror type;
26 26
27 /** Used to create new objects when reading. */ 27 /** Used to create new objects when reading. */
28 Constructor constructor; 28 Constructor constructor;
29 29
30 /** This holds onto our list of fields, and can also calculate them. */ 30 /** This holds onto our list of fields, and can also calculate them. */
31 _FieldList fields; 31 _FieldList _fields;
32 32
33 /** 33 /**
34 * Instances can either use maps or lists to hold the object's state. The list 34 * Instances can either use maps or lists to hold the object's state. The list
35 * representation is much more compact and used by default. The map 35 * representation is much more compact and used by default. The map
36 * representation is more human-readable. The default is to use lists. 36 * representation is more human-readable. The default is to use lists.
37 */ 37 */
38 bool useMaps = false; 38 bool useMaps = false;
39 39
40 // TODO(alanknight) Change the type parameter once we have class literals. 40 // TODO(alanknight) Change the type parameter once we have class literals.
41 // Issue 6282. 41 // Issue 6282.
(...skipping 14 matching lines...) Expand all
56 * fields, getter/setter pairs are fine. If this is null, it's assumed 56 * fields, getter/setter pairs are fine. If this is null, it's assumed
57 * that we should figure them out. 57 * that we should figure them out.
58 * [excludeFields] lets you tell it to find the fields automatically, but 58 * [excludeFields] lets you tell it to find the fields automatically, but
59 * omit some that would otherwise be included. 59 * omit some that would otherwise be included.
60 */ 60 */
61 BasicRule(ClassMirror this.type, String constructorName, 61 BasicRule(ClassMirror this.type, String constructorName,
62 List constructorFields, List regularFields, 62 List constructorFields, List regularFields,
63 List excludeFields) { 63 List excludeFields) {
64 _findFields(constructorFields, regularFields, excludeFields); 64 _findFields(constructorFields, regularFields, excludeFields);
65 constructor = new Constructor( 65 constructor = new Constructor(
66 type, constructorName, fields.constructorFieldIndices()); 66 type, constructorName, _fields.constructorFieldIndices());
67 configureForLists(); 67 configureForLists();
68 } 68 }
69 69
70 /** 70 /**
71 * Sometimes it's necessary to treat fields of an object differently, based 71 * Sometimes it's necessary to treat fields of an object differently, based
72 * on the containing object. For example, by default a list treats its 72 * on the containing object. For example, by default a list treats its
73 * contents as non-essential state, so it will be populated only after all 73 * contents as non-essential state, so it will be populated only after all
74 * objects have been created. An object may have a list which is used in its 74 * objects have been created. An object may have a list which is used in its
75 * constructor and must be fully created before the owning object can be 75 * constructor and must be fully created before the owning object can be
76 * created. Alternatively, it may not be possible to set a field directly, 76 * created. Alternatively, it may not be possible to set a field directly,
77 * and some other method must be called to set it, perhaps calling a method 77 * and some other method must be called to set it, perhaps calling a method
78 * on the owning object to add each individual element. 78 * on the owning object to add each individual element.
79 * 79 *
80 * This method lets you designate a function to use to set the value of a 80 * This method lets you designate a function to use to set the value of a
81 * field. It also makes the contents of that field be treated as essential, 81 * field. It also makes the contents of that field be treated as essential,
82 * which currently only has meaning if the field is a list. This is done 82 * which currently only has meaning if the field is a list. This is done
83 * because you might set a list field's special treatment function to add 83 * because you might set a list field's special treatment function to add
84 * each item individually and that will only work if those objects already 84 * each item individually and that will only work if those objects already
85 * exist. 85 * exist.
86 * 86 *
87 * For example, to serialize a Serialization, we need its rules to be 87 * For example, to serialize a Serialization, we need its rules to be
88 * individually added rather than just setting the rules field. 88 * individually added rather than just setting the rules field.
89 * ..addRuleFor(new Serialization()).setFieldWith('rules', 89 * ..addRuleFor(new Serialization()).setFieldWith('rules',
90 * (InstanceMirror s, List rules) { 90 * (InstanceMirror s, List rules) {
91 * rules.forEach((x) => s.reflectee.addRule(x)); 91 * rules.forEach((x) => s.reflectee.addRule(x));
92 * Note that the function is passed the owning object as well as the field 92 * Note that the function is passed the owning object as well as the field
93 * value, but that it is passed as a mirror. 93 * value, but that it is passed as a mirror.
94 */ 94 */
95 setFieldWith(String fieldName, SetWithFunction setWith) { 95 void setFieldWith(String fieldName, SetWithFunction setWith) {
96 fields.addAllByName([fieldName]); 96 _fields.addAllByName([fieldName]);
97 _NamedField field = fields.named(_asSymbol(fieldName)); 97 _NamedField field = _fields.named(_asSymbol(fieldName));
98 Function setter = (setWith == null) ? field.defaultSetter : setWith; 98 Function setter = (setWith == null) ? field.defaultSetter : setWith;
99 field.customSetter = setter; 99 field.customSetter = setter;
100 } 100 }
101 101
102 /** Return the name of the constructor used to create new instances on read.*/ 102 /** Return the name of the constructor used to create new instances on read.*/
103 String get constructorName => constructor.name; 103 String get constructorName => constructor.name;
104 104
105 /** Return the list of field names to be passed to the constructor.*/ 105 /** Return the list of field names to be passed to the constructor.*/
106 List<String> get constructorFields => fields.constructorFieldNames(); 106 List<String> get constructorFields => _fields.constructorFieldNames();
107 107
108 /** Return the list of field names not used in the constructor. */ 108 /** Return the list of field names not used in the constructor. */
109 List<String> get regularFields => fields.regularFieldNames(); 109 List<String> get regularFields => _fields.regularFieldNames();
110 110
111 String toString() => "Basic Rule for ${type.simpleName}"; 111 String toString() => "Basic Rule for ${type.simpleName}";
112 112
113 /** 113 /**
114 * Configure this instance to use maps by field name as its output. 114 * Configure this instance to use maps by field name as its output.
115 * Instances can either produce maps or lists. The list representation 115 * Instances can either produce maps or lists. The list representation
116 * is much more compact and used by default. The map representation is 116 * is much more compact and used by default. The map representation is
117 * much easier to debug. The default is to use lists. 117 * much easier to debug. The default is to use lists.
118 */ 118 */
119 configureForMaps() { 119 void configureForMaps() {
120 useMaps = true; 120 useMaps = true;
121 } 121 }
122 122
123 /** 123 /**
124 * Configure this instance to use lists accessing fields by index as its 124 * Configure this instance to use lists accessing fields by index as its
125 * output. Instances can either produce maps or lists. The list representation 125 * output. Instances can either produce maps or lists. The list representation
126 * is much more compact and used by default. The map representation is 126 * is much more compact and used by default. The map representation is
127 * much easier to debug. The default is to use lists. 127 * much easier to debug. The default is to use lists.
128 */ 128 */
129 configureForLists() { 129 void configureForLists() {
130 useMaps = false; 130 useMaps = false;
131 } 131 }
132 132
133 /** 133 /**
134 * Create either a list or a map to hold the object's state, depending 134 * Create either a list or a map to hold the object's state, depending
135 * on the [useMaps] variable. If using a Map, we wrap it in order to keep 135 * on the [useMaps] variable. If using a Map, we wrap it in order to keep
136 * the protocol compatible. See [configureForLists]/[configureForMaps]. 136 * the protocol compatible. See [configureForLists]/[configureForMaps].
137 * 137 *
138 * If a list is returned, it is growable. 138 * If a list is returned, it is growable.
139 */ 139 */
140 createStateHolder() { 140 createStateHolder() {
141 if (useMaps) return new _MapWrapper(fields.contents); 141 if (useMaps) return new _MapWrapper(_fields.contents);
142 List list = []; 142 List list = [];
143 list.length = fields.length; 143 list.length = _fields.length;
144 return list; 144 return list;
145 } 145 }
146 146
147 /** 147 /**
148 * Wrap the state if it's passed in as a map, and if the keys are references, 148 * Wrap the state if it's passed in as a map, and if the keys are references,
149 * resolve them to the strings we expect. We leave the previous keys in there 149 * resolve them to the strings we expect. We leave the previous keys in there
150 * as well, as they shouldn't be harmful, and it costs more to remove them. 150 * as well, as they shouldn't be harmful, and it costs more to remove them.
151 */ 151 */
152 makeIndexableByNumber(state) { 152 makeIndexableByNumber(state) {
153 if (!(state is Map)) return state; 153 if (!(state is Map)) return state;
154 // TODO(alanknight): This is quite inefficient, and we do it twice per 154 // TODO(alanknight): This is quite inefficient, and we do it twice per
155 // instance. If the keys are references, we need to turn them into strings 155 // instance. If the keys are references, we need to turn them into strings
156 // before we can look at indexing them by field position. It's also eager, 156 // before we can look at indexing them by field position. It's also eager,
157 // but we know our keys are always primitives, so we don't have to worry 157 // but we know our keys are always primitives, so we don't have to worry
158 // about their instances not having been created yet. 158 // about their instances not having been created yet.
159 var newState = new Map(); 159 var newState = new Map();
160 for (var each in state.keys) { 160 for (var each in state.keys) {
161 var newKey = (each is Reference) ? each.inflated() : each; 161 var newKey = (each is Reference) ? each.inflated() : each;
162 newState[newKey] = state[each]; 162 newState[newKey] = state[each];
163 } 163 }
164 return new _MapWrapper.fromMap(newState, fields.contents); 164 return new _MapWrapper.fromMap(newState, _fields.contents);
165 } 165 }
166 166
167 /** 167 /**
168 * Extract the state from [object] using an instanceMirror and the field 168 * Extract the state from [object] using an instanceMirror and the field
169 * names in [fields]. Call the function [callback] on each value. 169 * names in [_fields]. Call the function [callback] on each value.
170 */ 170 */
171 extractState(object, Function callback, Writer w) { 171 extractState(object, Function callback, Writer w) {
172 var result = createStateHolder(); 172 var result = createStateHolder();
173 var mirror = reflect(object); 173 var mirror = reflect(object);
174 174
175 keysAndValues(fields).forEach( 175 keysAndValues(_fields).forEach(
176 (index, field) { 176 (index, field) {
177 var value = _value(mirror, field); 177 var value = _value(mirror, field);
178 callback(field.name); 178 callback(field.name);
179 callback(checkForEssentialLists(index, value)); 179 callback(checkForEssentialLists(index, value));
180 result[index] = value; 180 result[index] = value;
181 }); 181 });
182 return _unwrap(result); 182 return _unwrap(result);
183 } 183 }
184 184
185 flatten(state, Writer writer) { 185 flatten(state, Writer writer) {
(...skipping 10 matching lines...) Expand all
196 } 196 }
197 } 197 }
198 198
199 /** 199 /**
200 * If the value is a List, and the field is a constructor field or 200 * If the value is a List, and the field is a constructor field or
201 * otherwise specially designated, we wrap it in something that indicates 201 * otherwise specially designated, we wrap it in something that indicates
202 * a restriction on the rules that can be used. Which in this case amounts 202 * a restriction on the rules that can be used. Which in this case amounts
203 * to designating the rule, since we so far only have one rule per object. 203 * to designating the rule, since we so far only have one rule per object.
204 */ 204 */
205 checkForEssentialLists(index, value) { 205 checkForEssentialLists(index, value) {
206 if (value is List && fields.contents[index].isEssential) { 206 if (value is List && _fields.contents[index].isEssential) {
207 return new DesignatedRuleForObject(value, 207 return new DesignatedRuleForObject(value,
208 (SerializationRule rule) => rule is ListRuleEssential); 208 (SerializationRule rule) => rule is ListRuleEssential);
209 } else { 209 } else {
210 return value; 210 return value;
211 } 211 }
212 } 212 }
213 213
214 /** Remove any MapWrapper from the extracted state. */ 214 /** Remove any MapWrapper from the extracted state. */
215 _unwrap(result) => (result is _MapWrapper) ? result.asMap() : result; 215 _unwrap(result) => (result is _MapWrapper) ? result.asMap() : result;
216 216
217 /** 217 /**
218 * Call the designated constructor with the appropriate fields from [state], 218 * Call the designated constructor with the appropriate fields from [state],
219 * first resolving references in the context of [reader]. 219 * first resolving references in the context of [reader].
220 */ 220 */
221 inflateEssential(state, Reader reader) { 221 inflateEssential(state, Reader reader) {
222 InstanceMirror mirror = constructor.constructFrom( 222 InstanceMirror mirror = constructor.constructFrom(
223 makeIndexableByNumber(state), reader); 223 makeIndexableByNumber(state), reader);
224 return mirror.reflectee; 224 return mirror.reflectee;
225 } 225 }
226 226
227 /** For all [rawState] not required in the constructor, set it in the 227 /** For all [rawState] not required in the constructor, set it in the
228 * [object], resolving references in the context of [reader]. 228 * [object], resolving references in the context of [reader].
229 */ 229 */
230 inflateNonEssential(rawState, object, Reader reader) { 230 inflateNonEssential(rawState, object, Reader reader) {
231 InstanceMirror mirror = reflect(object); 231 InstanceMirror mirror = reflect(object);
232 var state = makeIndexableByNumber(rawState); 232 var state = makeIndexableByNumber(rawState);
233 fields.forEachRegularField( (_Field field) { 233 _fields.forEachRegularField( (_Field field) {
234 var value = reader.inflateReference(state[field.index]); 234 var value = reader.inflateReference(state[field.index]);
235 field.setValue(mirror, value); 235 field.setValue(mirror, value);
236 }); 236 });
237 } 237 }
238 238
239 /** 239 /**
240 * Determine if this rule applies to the object in question. In our case 240 * Determine if this rule applies to the object in question. In our case
241 * this is true if the type mirrors are the same. 241 * this is true if the type mirrors are the same.
242 */ 242 */
243 // TODO(alanknight): This seems likely to be slow. Verify. Other options? 243 // TODO(alanknight): This seems likely to be slow. Verify. Other options?
244 bool appliesTo(object, Writer w) => reflect(object).type == type; 244 bool appliesTo(object, Writer w) => reflect(object).type == type;
245 245
246 /** 246 /**
247 * Given the various field lists provided by the user, construct the list 247 * Given the various field lists provided by the user, construct the list
248 * of field names that we want. 248 * of field names that we want.
249 */ 249 */
250 void _findFields(List constructorFields, List regularFields, 250 void _findFields(List constructorFields, List regularFields,
251 List excludeFields) { 251 List excludeFields) {
252 fields = new _FieldList(type); 252 _fields = new _FieldList(type);
253 fields.constructorFields = constructorFields; 253 _fields.constructorFields = constructorFields;
254 fields.regular = regularFields; 254 _fields.regular = regularFields;
255 // TODO(alanknight): The order of this matters. It shouldn't. 255 // TODO(alanknight): The order of this matters. It shouldn't.
256 fields.exclude = excludeFields; 256 _fields.exclude = excludeFields;
257 fields.figureOutFields(); 257 _fields.figureOutFields();
258 } 258 }
259 259
260 bool get hasVariableLengthEntries => false; 260 bool get hasVariableLengthEntries => false;
261 261
262 int get dataLength => fields.length; 262 int get dataLength => _fields.length;
263 263
264 /** 264 /**
265 * Extract the value of [field] from the object reflected 265 * Extract the value of [field] from the object reflected
266 * by [mirror]. 266 * by [mirror].
267 */ 267 */
268 // TODO(alanknight): The framework should be resilient if there are fields 268 // TODO(alanknight): The framework should be resilient if there are fields
269 // it expects that are missing, either for the case of de-serializing to a 269 // it expects that are missing, either for the case of de-serializing to a
270 // different definition, or for the case that tree-shaking has removed state. 270 // different definition, or for the case that tree-shaking has removed state.
271 // TODO(alanknight): This, and other places, rely on synchronous access to 271 // TODO(alanknight): This, and other places, rely on synchronous access to
272 // mirrors. Should be changed to use a synchronous API once one is available, 272 // mirrors. Should be changed to use a synchronous API once one is available,
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
335 * using [BasicRule.setFieldWith]. 335 * using [BasicRule.setFieldWith].
336 */ 336 */
337 bool get isEssential => usedInConstructor; 337 bool get isEssential => usedInConstructor;
338 338
339 /** Set the [value] of our field in the given mirrored [object]. */ 339 /** Set the [value] of our field in the given mirrored [object]. */
340 void setValue(InstanceMirror object, value); 340 void setValue(InstanceMirror object, value);
341 341
342 // Because [x] may not be a named field, we compare the toString. We don't 342 // Because [x] may not be a named field, we compare the toString. We don't
343 // care that much where constants come in the sort order as long as it's 343 // care that much where constants come in the sort order as long as it's
344 // consistent. 344 // consistent.
345 compareTo(_Field x) => toString().compareTo(x.toString()); 345 int compareTo(_Field x) => toString().compareTo(x.toString());
346 } 346 }
347 347
348 /** 348 /**
349 * This represents a field in the object, either stored as a field or 349 * This represents a field in the object, either stored as a field or
350 * accessed via getter/setter/constructor parameter. It has a name and 350 * accessed via getter/setter/constructor parameter. It has a name and
351 * will attempt to access the state for that name using an [InstanceMirror]. 351 * will attempt to access the state for that name using an [InstanceMirror].
352 */ 352 */
353 class _NamedField extends _Field { 353 class _NamedField extends _Field {
354 /** The name of the field (or getter) */ 354 /** The name of the field (or getter) */
355 String _name; 355 String _name;
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
423 * use the value for that. 423 * use the value for that.
424 */ 424 */
425 get name => value; 425 get name => value;
426 } 426 }
427 427
428 /** 428 /**
429 * The organization of fields in an object can be reasonably complex, so they 429 * The organization of fields in an object can be reasonably complex, so they
430 * are kept in a separate object, which also has the ability to compute the 430 * are kept in a separate object, which also has the ability to compute the
431 * default fields to use reflectively. 431 * default fields to use reflectively.
432 */ 432 */
433 class _FieldList extends IterableBase { 433 class _FieldList extends IterableBase<_Field> {
434 /** 434 /**
435 * All of our fields, indexed by name. Note that the names are 435 * All of our fields, indexed by name. Note that the names are
436 * typically Symbols, but can also be arbitrary constants. 436 * typically Symbols, but can also be arbitrary constants.
437 */ 437 */
438 Map<dynamic, _Field> allFields = new Map<dynamic, _Field>(); 438 Map<dynamic, _Field> allFields = new Map<dynamic, _Field>();
439 439
440 /** 440 /**
441 * The fields which are used in the constructor. The fields themselves also 441 * The fields which are used in the constructor. The fields themselves also
442 * know if they are constructor fields or not, but we need to keep this 442 * know if they are constructor fields or not, but we need to keep this
443 * information here because the order matters. 443 * information here because the order matters.
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
516 516
517 /** 517 /**
518 * Fields have been added. In case we had already forced calculation of the 518 * Fields have been added. In case we had already forced calculation of the
519 * list of contents, re-set it. 519 * list of contents, re-set it.
520 */ 520 */
521 void invalidate() { 521 void invalidate() {
522 _contents = null; 522 _contents = null;
523 contents; 523 contents;
524 } 524 }
525 525
526 Iterator get iterator => contents.iterator; 526 Iterator<_Field> get iterator => contents.iterator;
527 527
528 /** Return a cached, sorted list of all the fields. */ 528 /** Return a cached, sorted list of all the fields. */
529 List<_Field> get contents { 529 List<_Field> get contents {
530 if (_contents == null) { 530 if (_contents == null) {
531 _contents = sorted(allFields.values); 531 _contents = sorted(allFields.values);
532 for (var i = 0; i < _contents.length; i++) 532 for (var i = 0; i < _contents.length; i++)
533 _contents[i].index = i; 533 _contents[i].index = i;
534 } 534 }
535 return _contents; 535 return _contents;
536 } 536 }
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
643 } 643 }
644 return result; 644 return result;
645 } 645 }
646 } 646 }
647 647
648 /** 648 /**
649 * This wraps a map to make it indexable by integer field numbers. It translates 649 * This wraps a map to make it indexable by integer field numbers. It translates
650 * from the index into a field name and then looks it up in the map. 650 * from the index into a field name and then looks it up in the map.
651 */ 651 */
652 class _MapWrapper { 652 class _MapWrapper {
653 final _map; 653 final Map _map;
654 List fieldList; 654 final List fieldList;
655 _MapWrapper(this.fieldList) : _map = new Map(); 655 _MapWrapper(this.fieldList) : _map = new Map();
656 _MapWrapper.fromMap(this._map, this.fieldList); 656 _MapWrapper.fromMap(this._map, this.fieldList);
657 657
658 operator [](key) => _map[fieldList[key].name]; 658 operator [](key) => _map[fieldList[key].name];
659 659
660 operator []=(key, value) { _map[fieldList[key].name] = value; } 660 operator []=(key, value) { _map[fieldList[key].name] = value; }
661 get length => _map.length; 661 get length => _map.length;
662 662
663 asMap() => _map; 663 Map asMap() => _map;
664 } 664 }
665 665
666 /** 666 /**
667 * Return a symbol corresponding to [value], which may be a String or a 667 * Return a symbol corresponding to [value], which may be a String or a
668 * Symbol. If it is any other type, or if the string is an 668 * Symbol. If it is any other type, or if the string is an
669 * invalid symbol, return null; 669 * invalid symbol, return null;
670 */ 670 */
671 _asSymbol(value) { 671 Symbol _asSymbol(value) {
672 if (value is Symbol) return value; 672 if (value is Symbol) return value;
673 if (value is String) { 673 if (value is String) {
674 try { 674 try {
675 return new Symbol(value); 675 return new Symbol(value);
676 } on ArgumentError { 676 } on ArgumentError {
677 return null; 677 return null;
678 }; 678 };
679 } else { 679 } else {
680 return null; 680 return null;
681 } 681 }
682 } 682 }
OLDNEW
« no previous file with comments | « pkg/serialization/lib/serialization.dart ('k') | pkg/serialization/lib/src/format.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698