| Index: samples/todomvc/dart/todomvc_presenter_model.dart
|
| diff --git a/samples/todomvc/dart/todomvc_presenter_model.dart b/samples/todomvc/dart/todomvc_presenter_model.dart
|
| deleted file mode 100644
|
| index 7572b6bb0c4fbe3765d6789277001fade687d44b..0000000000000000000000000000000000000000
|
| --- a/samples/todomvc/dart/todomvc_presenter_model.dart
|
| +++ /dev/null
|
| @@ -1,378 +0,0 @@
|
| -// Copyright (c) 2015, the Dartino 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.md file.
|
| -
|
| -// Should become auto-generated.
|
| -
|
| -library todomvc_presenter_model;
|
| -
|
| -import 'todomvc_service.dart';
|
| -
|
| -/*
|
| - The presentation model should be generated by a terse description.
|
| - For example, the presenter model for this sample might have been declared as:
|
| -
|
| - struct Immutable {
|
| - union {
|
| - Atom;
|
| - Cons;
|
| - }
|
| - }
|
| -
|
| - struct Atom {
|
| - union {
|
| - void nil;
|
| - int32 num;
|
| - bool truth;
|
| - String str;
|
| - }
|
| - }
|
| -
|
| - struct Cons {
|
| - Immutable fst;
|
| - Immutable snd;
|
| - }
|
| -
|
| - service TodoListPresenter {
|
| - void Create(String);
|
| - void Delete(Int32);
|
| - void Complete(Int32);
|
| - void Clear();
|
| - }
|
| -
|
| - From the description we would generate dart classes and their path descriptors
|
| - for each struct together with the diff algorithm. For services we would create
|
| - command descriptors. All of these would come with serialization support for
|
| - wire transfer.
|
| -
|
| - Until then, all of the above is implemented below.
|
| -
|
| - */
|
| -
|
| -// Tracing to ease debugging on dartino...
|
| -bool TRACE = false;
|
| -void trace(obj) { if (TRACE) print(" $obj"); }
|
| -
|
| -// Some constants.
|
| -
|
| -const TAG_CONS_FST = 0;
|
| -const TAG_CONS_SND = 1;
|
| -const TAG_CONS_DELETE_EVENT = 2;
|
| -const TAG_CONS_COMPLETE_EVENT = 3;
|
| -const TAG_CONS_UNCOMPLETE_EVENT = 4;
|
| -
|
| -// The event manager maintains a mapping from the active handler ids transferred
|
| -// as part of the presentation graph to the handler callbacks.
|
| -class EventManager {
|
| - Map<int, EventHandler> _handlers = new Map();
|
| -
|
| - int _lfsr = 0xABBA;
|
| -
|
| - int get _next {
|
| - // 16 bits with taps 16, 14, 13, 11.
|
| - int lfsr = _lfsr;
|
| - int bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 5)) & 1;
|
| - lfsr = (lfsr >> 1) | (bit << 15);
|
| - _lfsr = lfsr;
|
| - return lfsr;
|
| - }
|
| -
|
| - int register(EventHandler handler) {
|
| - if (handler == null) return 0;
|
| - if (handler.allocated) {
|
| - // After event manager reset we must re-register event handlers.
|
| - int id = handler.id;
|
| - EventHandler registered = _handlers[id];
|
| - if (registered == null) {
|
| - _handlers[id] = handler;
|
| - } else if (registered != handler) {
|
| - print("ERROR: attempt to re-register event handler: $id");
|
| - }
|
| - return id;
|
| - }
|
| - int id = _next;
|
| - // TODO(zerny): Do something clever once completing the lfsr sequence.
|
| - while (_handlers.containsKey(id)) id = _next;
|
| - trace("registered event-handler id: $id");
|
| - _handlers[id] = handler;
|
| - handler.allocate(id);
|
| - return id;
|
| - }
|
| -
|
| - void unregister(EventHandler handler) {
|
| - if (handler == null) return;
|
| - // TODO(zerny): replace by assert.
|
| - int id = handler.id;
|
| - if (!handler.allocated) {
|
| - print("ERROR: attempt to unregister unallocated event handler: $id");
|
| - return;
|
| - }
|
| - trace("unregistered event-handler id: $id");
|
| - assert(_handlers[id] == handler);
|
| - handler.delete();
|
| - _handlers.remove(id);
|
| - }
|
| -
|
| - void call(int id) {
|
| - assert(id > 0);
|
| - EventHandler handler = _handlers[id];
|
| - if (handler == null) {
|
| - print("ERROR: attempt to call non-exisiting event handler: $id");
|
| - return;
|
| - }
|
| - handler.call();
|
| - }
|
| -
|
| - void clear() {
|
| - _handlers.clear();
|
| - }
|
| -}
|
| -
|
| -// Immutable model for the presentation (reused as a mutable mirror on the
|
| -// "host" side). (just sexp to simulate a "rich structure" for now)
|
| -
|
| -abstract class Immutable {
|
| - void diff(Immutable other, Path path, MyPatchSet patches);
|
| - void serialize(NodeBuilder builder, EventManager events);
|
| - void unregisterEvents(EventManager events);
|
| -}
|
| -
|
| -class EventHandler extends Immutable {
|
| - final Function _handler;
|
| - int _id = 0;
|
| -
|
| - EventHandler(this._handler);
|
| -
|
| - int get id => _id;
|
| - bool get allocated => _id > 0;
|
| - bool get deleted => _id < 0;
|
| -
|
| - void call() {
|
| - assert(allocated);
|
| - _handler();
|
| - }
|
| -
|
| - void allocate(int id) {
|
| - assert(!deleted);
|
| - _id = id;
|
| - }
|
| -
|
| - void delete() {
|
| - _id = -1;
|
| - }
|
| -
|
| - static void staticDiff(EventHandler self,
|
| - EventHandler other,
|
| - Path path,
|
| - MyPatchSet patches) {
|
| - if (identical(self, other)) return;
|
| - // TODO(zerny): Do we want to consider another definition of equality here?
|
| - // Currently only the same physically allocated event-handler object is
|
| - // considered "the same", but we could refine that to be if the _handler
|
| - // object is "the same" (which it typically won't be because of closure
|
| - // allocation). Also, if we do so, we need to make sure that the allocated
|
| - // event ids are updated to be the same too, otherwise we could end up with
|
| - // an unallocated event handler being referenced from the binding layer.
|
| - patches.add(path, self, other);
|
| - }
|
| -
|
| - void diff(Immutable other, Path path, MyPatchSet patches) {
|
| - EventHandler.staticDiff(this, other, path, patches);
|
| - }
|
| -
|
| - // TODO(zerny): Event handler fields should not be encoded in Node.
|
| - void serialize(NodeBuilder builder, EventManager events) {
|
| - builder.num = events.register(this);
|
| - }
|
| -
|
| - void unregisterEvents(EventManager events) {
|
| - events.unregister(this);
|
| - }
|
| -}
|
| -
|
| -abstract class Atom extends Immutable {
|
| - final value;
|
| - Atom(this.value) {
|
| - trace("Allocating Atom");
|
| - }
|
| - String toString() => "value($value)";
|
| -
|
| - void diff(Immutable other, Path path, MyPatchSet patches) {
|
| - trace("Atom::diff $this ~=~ $other");
|
| - if (other is Atom && value == other.value)
|
| - return;
|
| - patches.add(path, this, other);
|
| - }
|
| -
|
| - void unregisterEvents(EventManager events) { }
|
| -}
|
| -
|
| -class Cons extends Immutable {
|
| - final Immutable fst, snd;
|
| - final EventHandler deleteEvent, completeEvent, uncompleteEvent;
|
| -
|
| - Cons(this.fst, this.snd,
|
| - [ this.deleteEvent,
|
| - this.completeEvent,
|
| - this.uncompleteEvent ]) {
|
| - trace("Allocating Cons");
|
| - }
|
| -
|
| - String toString() => "($fst . $snd)";
|
| -
|
| - void diff(Immutable other, Path path, MyPatchSet patches) {
|
| - if (identical(this, other)) return;
|
| - if (other is! Cons) {
|
| - patches.add(path, this, other);
|
| - return;
|
| - }
|
| - Cons otherCons = other;
|
| - fst.diff(otherCons.fst, new ConsFst(path), patches);
|
| - snd.diff(otherCons.snd, new ConsSnd(path), patches);
|
| - EventHandler.staticDiff(
|
| - deleteEvent,
|
| - otherCons.deleteEvent,
|
| - new ConsDeleteEvent(path), patches);
|
| - EventHandler.staticDiff(
|
| - completeEvent,
|
| - otherCons.completeEvent,
|
| - new ConsCompleteEvent(path), patches);
|
| - EventHandler.staticDiff(
|
| - uncompleteEvent,
|
| - otherCons.uncompleteEvent,
|
| - new ConsUncompleteEvent(path), patches);
|
| - }
|
| -
|
| - void serialize(NodeBuilder builder, EventManager events) {
|
| - trace("Cons::serialize: $this");
|
| - ConsBuilder cons = builder.initCons();
|
| - cons.deleteEvent = events.register(deleteEvent);
|
| - cons.completeEvent = events.register(completeEvent);
|
| - cons.uncompleteEvent = events.register(uncompleteEvent);
|
| - fst.serialize(cons.initFst(), events);
|
| - snd.serialize(cons.initSnd(), events);
|
| - }
|
| -
|
| - void unregisterEvents(EventManager events) {
|
| - events.unregister(deleteEvent);
|
| - events.unregister(completeEvent);
|
| - events.unregister(uncompleteEvent);
|
| - fst.unregisterEvents(events);
|
| - snd.unregisterEvents(events);
|
| - }
|
| -}
|
| -
|
| -class Nil extends Atom {
|
| - Nil() : super(null);
|
| - void serialize(NodeBuilder builder, EventManager events) {
|
| - trace("Nil::serialize");
|
| - builder.setNil();
|
| - }
|
| -}
|
| -
|
| -class Bool extends Atom {
|
| - Bool(bool value) : super(value);
|
| - void serialize(NodeBuilder builder, EventManager events) {
|
| - trace("Bool::serialize: $this");
|
| - builder.truth = value;
|
| - }
|
| -}
|
| -
|
| -class Num extends Atom {
|
| - Num(num value) : super(value);
|
| - void serialize(NodeBuilder builder, EventManager events) {
|
| - trace("Num::serialize: $this");
|
| - builder.num = value;
|
| - }
|
| -}
|
| -
|
| -class Str extends Atom {
|
| - Str(String value) : super(value);
|
| - void serialize(NodeBuilder builder, EventManager events) {
|
| - trace("Str::serialize: $this");
|
| - builder.str = value;
|
| - }
|
| -}
|
| -
|
| -
|
| -// Path in the immutable model. (We probably won't need an actual representation
|
| -// of these. We could just construct the serialized form on the fly).
|
| -
|
| -// Note that (serialize) reverses the path description. Ie, we construct the
|
| -// path 'inside-out' and read it 'outside-in' on the host side.
|
| -
|
| -abstract class Path {
|
| - final Path parent;
|
| - Path(this.parent);
|
| - int get tag;
|
| -
|
| - static void serialize(Path inner, PatchBuilder builder) {
|
| - trace("Path::serialize: path($inner)");
|
| - int length = 0;
|
| - for (Path current = inner; current != null; current = current.parent) {
|
| - ++length;
|
| - }
|
| - var out = builder.initPath(length);
|
| - for (Path current = inner; current != null; current = current.parent) {
|
| - out[--length] = current.tag;
|
| - }
|
| - }
|
| -
|
| - String toString() => "$parent;$tag";
|
| -}
|
| -
|
| -class ConsFst extends Path {
|
| - ConsFst(Path parent) : super(parent);
|
| - int get tag => TAG_CONS_FST;
|
| -}
|
| -
|
| -class ConsSnd extends Path {
|
| - ConsSnd(Path parent) : super(parent);
|
| - int get tag => TAG_CONS_SND;
|
| -}
|
| -
|
| -class ConsDeleteEvent extends Path {
|
| - ConsDeleteEvent(Path parent) : super(parent);
|
| - int get tag => TAG_CONS_DELETE_EVENT;
|
| -}
|
| -
|
| -class ConsCompleteEvent extends Path {
|
| - ConsCompleteEvent(Path parent) : super(parent);
|
| - int get tag => TAG_CONS_COMPLETE_EVENT;
|
| -}
|
| -
|
| -class ConsUncompleteEvent extends Path {
|
| - ConsUncompleteEvent(Path parent) : super(parent);
|
| - int get tag => TAG_CONS_UNCOMPLETE_EVENT;
|
| -}
|
| -
|
| -// Patch description
|
| -
|
| -class MyPatchSet {
|
| - List<Patch> patches = new List<Patch>();
|
| - add(Path path, Immutable content, Immutable oldContent) =>
|
| - patches.add(new Patch(path, content, oldContent));
|
| -
|
| - void serialize(PatchSetBuilder builder, EventManager events) {
|
| - var length = patches.length;
|
| - trace("MyPatchSet::serialize: length($length)");
|
| - var out = builder.initPatches(patches.length);
|
| - for (int i = 0; i < length; ++i) {
|
| - patches[i].serialize(out[i], events);
|
| - }
|
| - }
|
| -}
|
| -
|
| -class Patch {
|
| - Path path;
|
| - Immutable content;
|
| - Immutable oldContent;
|
| - Patch(this.path, this.content, this.oldContent);
|
| -
|
| - void serialize(PatchBuilder builder, EventManager events) {
|
| - trace("Patch::serialize");
|
| - oldContent.unregisterEvents(events);
|
| - Path.serialize(path, builder);
|
| - content.serialize(builder.initContent(), events);
|
| - }
|
| -}
|
|
|