| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of gcloud.db; |
| |
| /// Represents a unique identifier for a [Model] stored in a datastore. |
| /// |
| /// The [Key] can be incomplete if it's id is `null`. In this case the id will |
| /// be automatically allocated and set at commit time. |
| class Key { |
| // Either KeyImpl or PartitionImpl |
| final Object _parent; |
| |
| final Type type; |
| final Object id; |
| |
| Key(Key parent, this.type, this.id) : _parent = parent { |
| if (type == null) { |
| throw ArgumentError('The type argument must not be null.'); |
| } |
| if (id != null && id is! String && id is! int) { |
| throw ArgumentError('The id argument must be an integer or a String.'); |
| } |
| } |
| |
| Key.emptyKey(Partition partition) |
| : _parent = partition, |
| type = null, |
| id = null; |
| |
| /// Parent of this [Key]. |
| Key get parent { |
| if (_parent is Key) { |
| return _parent as Key; |
| } |
| return null; |
| } |
| |
| /// The partition of this [Key]. |
| Partition get partition { |
| var obj = _parent; |
| while (obj is! Partition) { |
| obj = (obj as Key)._parent; |
| } |
| return obj as Partition; |
| } |
| |
| Key append(Type modelType, {Object id}) { |
| return Key(this, modelType, id); |
| } |
| |
| bool get isEmpty => _parent is Partition; |
| |
| operator ==(Object other) { |
| return other is Key && |
| _parent == other._parent && |
| type == other.type && |
| id == other.id; |
| } |
| |
| int get hashCode => _parent.hashCode ^ type.hashCode ^ id.hashCode; |
| } |
| |
| /// Represents a datastore partition. |
| /// |
| /// A datastore is partitioned into namespaces. The default namespace is |
| /// `null`. |
| class Partition { |
| final String namespace; |
| |
| Partition(this.namespace) { |
| if (namespace == '') { |
| throw ArgumentError('The namespace must not be an empty string'); |
| } |
| } |
| |
| /// Returns an empty [Key]. |
| /// |
| /// Entities where the parent [Key] is empty will create their own entity |
| /// group. |
| Key get emptyKey => Key.emptyKey(this); |
| |
| operator ==(Object other) { |
| return other is Partition && namespace == other.namespace; |
| } |
| |
| int get hashCode => namespace.hashCode; |
| } |
| |
| /// Superclass for all model classes. |
| /// |
| /// Every model class has a [id] -- which must be an integer or a string, and |
| /// a [parentKey]. The [key] getter is returning the key for the model object. |
| abstract class Model { |
| Object id; |
| Key parentKey; |
| |
| Key get key => parentKey.append(this.runtimeType, id: id); |
| } |
| |
| /// Superclass for all expanded model classes. |
| /// |
| /// The [ExpandoModel] class adds support for having dynamic properties. You can |
| /// set arbitrary fields on these models. The expanded values must be values |
| /// accepted by the [RawDatastore] implementation. |
| abstract class ExpandoModel extends Model { |
| final Map<String, Object> additionalProperties = {}; |
| |
| Object noSuchMethod(Invocation invocation) { |
| var name = mirrors.MirrorSystem.getName(invocation.memberName); |
| if (name.endsWith('=')) name = name.substring(0, name.length - 1); |
| if (invocation.isGetter) { |
| return additionalProperties[name]; |
| } else if (invocation.isSetter) { |
| var value = invocation.positionalArguments[0]; |
| additionalProperties[name] = value; |
| return value; |
| } else { |
| throw ArgumentError('Unsupported noSuchMethod call on ExpandoModel'); |
| } |
| } |
| } |