2014-09-26 11:20:08 -07:00
|
|
|
import {ProtoRecord, Record} from './record';
|
2014-10-28 12:22:38 -04:00
|
|
|
import {FIELD, IMPLEMENTS, isBlank, isPresent} from 'facade/lang';
|
2014-11-11 17:33:47 -08:00
|
|
|
import {AST, AccessMember, ImplicitReceiver, AstVisitor, Binary, LiteralPrimitive} from './parser/ast';
|
2014-09-26 11:20:08 -07:00
|
|
|
|
|
|
|
export class ProtoWatchGroup {
|
2014-10-28 12:22:38 -04:00
|
|
|
@FIELD('headRecord:ProtoRecord')
|
|
|
|
@FIELD('tailRecord:ProtoRecord')
|
2014-09-26 11:20:08 -07:00
|
|
|
constructor() {
|
2014-10-01 16:29:45 +02:00
|
|
|
this.headRecord = null;
|
|
|
|
this.tailRecord = null;
|
2014-09-26 11:20:08 -07:00
|
|
|
}
|
|
|
|
|
2014-09-30 15:50:20 -07:00
|
|
|
/**
|
2014-10-28 12:22:38 -04:00
|
|
|
* Parses [ast] into [ProtoRecord]s and adds them to [ProtoWatchGroup].
|
2014-09-30 15:50:20 -07:00
|
|
|
*
|
2014-10-28 12:22:38 -04:00
|
|
|
* @param ast The expression to watch
|
2014-10-02 15:14:32 +02:00
|
|
|
* @param memento an opaque object which will be passed to WatchGroupDispatcher on
|
2014-09-30 15:50:20 -07:00
|
|
|
* detecting a change.
|
|
|
|
* @param shallow Should collections be shallow watched
|
|
|
|
*/
|
2014-10-28 12:22:38 -04:00
|
|
|
watch(ast:AST,
|
2014-10-02 15:14:32 +02:00
|
|
|
memento,
|
2014-10-27 17:57:36 +01:00
|
|
|
shallow = false)
|
2014-09-26 11:20:08 -07:00
|
|
|
{
|
2014-10-28 12:22:38 -04:00
|
|
|
var creator = new ProtoRecordCreator(this);
|
|
|
|
creator.createRecordsFromAST(ast, memento);
|
|
|
|
this._addRecords(creator.headRecord, creator.tailRecord);
|
|
|
|
}
|
2014-10-02 15:14:32 +02:00
|
|
|
|
2014-10-28 12:22:38 -04:00
|
|
|
// try to encapsulate this behavior in some class (e.g., LinkedList)
|
|
|
|
// so we can say: group.appendList(creator.list);
|
|
|
|
_addRecords(head:ProtoRecord, tail:ProtoRecord) {
|
|
|
|
if (isBlank(this.headRecord)) {
|
|
|
|
this.headRecord = head;
|
|
|
|
} else {
|
|
|
|
this.tailRecord.next = head;
|
|
|
|
head.prev = this.tailRecord;
|
2014-10-02 15:14:32 +02:00
|
|
|
}
|
2014-10-28 12:22:38 -04:00
|
|
|
this.tailRecord = tail;
|
2014-09-26 11:20:08 -07:00
|
|
|
}
|
|
|
|
|
2014-10-29 15:41:50 -07:00
|
|
|
// TODO(rado): the type annotation should be dispatcher:WatchGroupDispatcher.
|
|
|
|
// but @Implements is not ready yet.
|
|
|
|
instantiate(dispatcher):WatchGroup {
|
2014-09-26 11:20:08 -07:00
|
|
|
var watchGroup:WatchGroup = new WatchGroup(this, dispatcher);
|
|
|
|
var tail:Record = null;
|
2014-10-29 15:41:50 -07:00
|
|
|
var proto:ProtoRecord = null;
|
2014-10-02 15:14:32 +02:00
|
|
|
var prevRecord:Record = null;
|
2014-09-26 11:20:08 -07:00
|
|
|
|
2014-10-02 15:14:32 +02:00
|
|
|
if (this.headRecord !== null) {
|
|
|
|
watchGroup.headRecord = tail = new Record(watchGroup, this.headRecord);
|
2014-09-26 11:20:08 -07:00
|
|
|
|
2014-10-02 15:14:32 +02:00
|
|
|
for (proto = this.headRecord.next; proto != null; proto = proto.next) {
|
|
|
|
prevRecord = tail;
|
|
|
|
tail = new Record(watchGroup, proto);
|
|
|
|
tail.prev = prevRecord;
|
|
|
|
prevRecord.next = tail;
|
|
|
|
tail.checkPrev = prevRecord;
|
|
|
|
prevRecord.checkNext = tail;
|
|
|
|
}
|
|
|
|
|
|
|
|
watchGroup.tailRecord = tail;
|
2014-09-26 11:20:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return watchGroup;
|
|
|
|
}
|
2014-09-30 15:50:20 -07:00
|
|
|
|
2014-09-26 11:20:08 -07:00
|
|
|
}
|
|
|
|
|
2014-09-19 16:38:37 -07:00
|
|
|
export class WatchGroup {
|
2014-09-26 11:20:08 -07:00
|
|
|
@FIELD('final protoWatchGroup:ProtoWatchGroup')
|
2014-09-19 16:38:37 -07:00
|
|
|
@FIELD('final dispatcher:WatchGroupDispatcher')
|
2014-10-01 16:29:45 +02:00
|
|
|
@FIELD('final headRecord:Record')
|
|
|
|
@FIELD('final tailRecord:Record')
|
2014-10-29 15:41:50 -07:00
|
|
|
// TODO(rado): the type annotation should be dispatcher:WatchGroupDispatcher.
|
|
|
|
// but @Implements is not ready yet.
|
|
|
|
constructor(protoWatchGroup:ProtoWatchGroup, dispatcher) {
|
2014-09-26 11:20:08 -07:00
|
|
|
this.protoWatchGroup = protoWatchGroup;
|
|
|
|
this.dispatcher = dispatcher;
|
2014-10-01 16:29:45 +02:00
|
|
|
this.headRecord = null;
|
|
|
|
this.tailRecord = null;
|
2014-10-02 15:14:32 +02:00
|
|
|
this.context = null;
|
2014-09-26 11:20:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
insertChildGroup(newChild:WatchGroup, insertAfter:WatchGroup) {
|
2014-10-02 15:14:32 +02:00
|
|
|
throw 'not implemented';
|
2014-09-26 11:20:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
remove() {
|
2014-10-02 15:14:32 +02:00
|
|
|
throw 'not implemented';
|
2014-09-26 11:20:08 -07:00
|
|
|
}
|
|
|
|
|
2014-09-30 16:39:37 -07:00
|
|
|
/**
|
|
|
|
* Sets the context (the object) on which the change detection expressions will
|
|
|
|
* dereference themselves on. Since the WatchGroup can be reused the context
|
|
|
|
* can be re-set many times during the lifetime of the WatchGroup.
|
|
|
|
*
|
2014-10-02 15:14:32 +02:00
|
|
|
* @param context the new context for change detection for the current WatchGroup
|
2014-09-30 16:39:37 -07:00
|
|
|
*/
|
|
|
|
setContext(context) {
|
2014-10-02 15:14:32 +02:00
|
|
|
for (var record:Record = this.headRecord;
|
|
|
|
record != null;
|
|
|
|
record = record.next) {
|
|
|
|
record.setContext(context);
|
|
|
|
}
|
2014-09-30 16:39:37 -07:00
|
|
|
}
|
2014-09-26 11:20:08 -07:00
|
|
|
}
|
2014-09-28 16:29:11 -07:00
|
|
|
|
|
|
|
export class WatchGroupDispatcher {
|
2014-10-02 15:14:32 +02:00
|
|
|
// The record holds the previous value at the time of the call
|
2014-09-28 16:29:11 -07:00
|
|
|
onRecordChange(record:Record, context) {}
|
|
|
|
}
|
2014-10-28 12:22:38 -04:00
|
|
|
|
|
|
|
@IMPLEMENTS(AstVisitor)
|
|
|
|
class ProtoRecordCreator {
|
|
|
|
@FIELD('final protoWatchGroup:ProtoWatchGroup')
|
|
|
|
@FIELD('headRecord:ProtoRecord')
|
|
|
|
@FIELD('tailRecord:ProtoRecord')
|
|
|
|
constructor(protoWatchGroup) {
|
|
|
|
this.protoWatchGroup = protoWatchGroup;
|
|
|
|
this.headRecord = null;
|
|
|
|
this.tailRecord = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
visitImplicitReceiver(ast:ImplicitReceiver) {
|
|
|
|
//do nothing
|
|
|
|
}
|
|
|
|
|
2014-11-11 17:33:47 -08:00
|
|
|
// TODO: add tests for this method!
|
|
|
|
visitLiteralPrimitive(ast:LiteralPrimitive) {
|
|
|
|
// do nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: add tests for this method!
|
|
|
|
visitBinary(ast:Binary) {
|
|
|
|
ast.left.visit(this);
|
|
|
|
ast.right.visit(this);
|
|
|
|
}
|
|
|
|
|
2014-11-05 17:10:37 -08:00
|
|
|
visitAccessMember(ast:AccessMember) {
|
2014-10-28 12:22:38 -04:00
|
|
|
ast.receiver.visit(this);
|
|
|
|
this.add(new ProtoRecord(this.protoWatchGroup, ast.name, null));
|
|
|
|
}
|
|
|
|
|
|
|
|
createRecordsFromAST(ast:AST, memento){
|
|
|
|
ast.visit(this);
|
|
|
|
if (isPresent(this.tailRecord)) {
|
|
|
|
this.tailRecord.dispatchMemento = memento;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
add(protoRecord:ProtoRecord) {
|
|
|
|
if (this.headRecord === null) {
|
|
|
|
this.headRecord = this.tailRecord = protoRecord;
|
|
|
|
} else {
|
|
|
|
this.tailRecord.next = protoRecord;
|
|
|
|
protoRecord.prev = this.tailRecord;
|
|
|
|
this.tailRecord = protoRecord;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|