import {isPresent, isBlank, looseIdentical} from 'angular2/src/facade/lang'; import {List, ListWrapper, Map} from 'angular2/src/facade/collection'; import {RecordType, ProtoRecord} from './proto_record'; /** * Removes "duplicate" records. It assuming that record evaluation does not * have side-effects. * * Records that are not last in bindings are removed and all the indices * of the records that depend on them are updated. * * Records that are last in bindings CANNOT be removed, and instead are * replaced with very cheap SELF records. */ export function coalesce(records: ProtoRecord[]): ProtoRecord[] { var res: List = []; var indexMap: Map = new Map(); for (var i = 0; i < records.length; ++i) { var r = records[i]; var record = _replaceIndices(r, res.length + 1, indexMap); var matchingRecord = _findMatching(record, res); if (isPresent(matchingRecord) && record.lastInBinding) { res.push(_selfRecord(record, matchingRecord.selfIndex, res.length + 1)); indexMap.set(r.selfIndex, matchingRecord.selfIndex); matchingRecord.referencedBySelf = true; } else if (isPresent(matchingRecord) && !record.lastInBinding) { if (record.argumentToPureFunction) { matchingRecord.argumentToPureFunction = true; } indexMap.set(r.selfIndex, matchingRecord.selfIndex); } else { res.push(record); indexMap.set(r.selfIndex, record.selfIndex); } } return res; } function _selfRecord(r: ProtoRecord, contextIndex: number, selfIndex: number): ProtoRecord { return new ProtoRecord(RecordType.SELF, "self", null, [], r.fixedArgs, contextIndex, r.directiveIndex, selfIndex, r.bindingRecord, r.expressionAsString, r.lastInBinding, r.lastInDirective, false, false); } function _findMatching(r: ProtoRecord, rs: List) { return ListWrapper.find( rs, (rr) => rr.mode !== RecordType.DIRECTIVE_LIFECYCLE && _sameDirIndex(rr, r) && rr.mode === r.mode && looseIdentical(rr.funcOrValue, r.funcOrValue) && rr.contextIndex === r.contextIndex && looseIdentical(rr.name, r.name) && ListWrapper.equals(rr.args, r.args)); } function _sameDirIndex(a: ProtoRecord, b: ProtoRecord): boolean { var di1 = isBlank(a.directiveIndex) ? null : a.directiveIndex.directiveIndex; var ei1 = isBlank(a.directiveIndex) ? null : a.directiveIndex.elementIndex; var di2 = isBlank(b.directiveIndex) ? null : b.directiveIndex.directiveIndex; var ei2 = isBlank(b.directiveIndex) ? null : b.directiveIndex.elementIndex; return di1 === di2 && ei1 === ei2; } function _replaceIndices(r: ProtoRecord, selfIndex: number, indexMap: Map) { var args = ListWrapper.map(r.args, (a) => _map(indexMap, a)); var contextIndex = _map(indexMap, r.contextIndex); return new ProtoRecord(r.mode, r.name, r.funcOrValue, args, r.fixedArgs, contextIndex, r.directiveIndex, selfIndex, r.bindingRecord, r.expressionAsString, r.lastInBinding, r.lastInDirective, r.argumentToPureFunction, r.referencedBySelf); } function _map(indexMap: Map, value: number) { var r = indexMap.get(value); return isPresent(r) ? r : value; }