1<!DOCTYPE html>
2<!--
3Copyright (c) 2013 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
8<link rel="import" href="/tracing/model/event_container.html">
9<link rel="import" href="/tracing/model/object_instance.html">
10<link rel="import" href="/tracing/model/time_to_object_instance_map.html">
11<link rel="import" href="/tracing/base/utils.html">
12<link rel="import" href="/tracing/base/range.html">
13<link rel="import" href="/tracing/base/sorted_array_utils.html">
14
15<script>
16'use strict';
17
18/**
19 * @fileoverview Provides the ObjectCollection class.
20 */
21tr.exportTo('tr.model', function() {
22  var ObjectInstance = tr.model.ObjectInstance;
23  var ObjectSnapshot = tr.model.ObjectSnapshot;
24
25  /**
26   * A collection of object instances and their snapshots, accessible by id and
27   * time, or by object name.
28   *
29   * @constructor
30   */
31  function ObjectCollection(parent) {
32    tr.model.EventContainer.call(this);
33    this.parent = parent;
34    // scope -> {id -> TimeToObjectInstanceMap}
35    this.instanceMapsByScopedId_ = {};
36    this.instancesByTypeName_ = {};
37    this.createObjectInstance_ = this.createObjectInstance_.bind(this);
38  }
39
40  ObjectCollection.prototype = {
41    __proto__: tr.model.EventContainer.prototype,
42
43    iterateAllChildEventContainers: function(callback, opt_this) {
44    },
45
46    iterateAllEventsInThisContainer: function(eventTypePredicate,
47                                              callback, opt_this) {
48      var bI = !!eventTypePredicate.call(opt_this, ObjectInstance);
49      var bS = !!eventTypePredicate.call(opt_this, ObjectSnapshot);
50      if (bI === false && bS === false)
51        return;
52      this.iterObjectInstances(function(instance) {
53        if (bI)
54          callback.call(opt_this, instance);
55        if (bS)
56          instance.snapshots.forEach(callback, opt_this);
57      }, opt_this);
58    },
59
60    createObjectInstance_: function(
61        parent, scopedId, category, name, creationTs, opt_baseTypeName) {
62      var constructor = tr.model.ObjectInstance.getConstructor(
63          category, name);
64      var instance = new constructor(
65          parent, scopedId, category, name, creationTs, opt_baseTypeName);
66      var typeName = instance.typeName;
67      var instancesOfTypeName = this.instancesByTypeName_[typeName];
68      if (!instancesOfTypeName) {
69        instancesOfTypeName = [];
70        this.instancesByTypeName_[typeName] = instancesOfTypeName;
71      }
72      instancesOfTypeName.push(instance);
73      return instance;
74    },
75
76    getOrCreateInstanceMap_: function(scopedId) {
77      var dict;
78      if (scopedId.scope in this.instanceMapsByScopedId_) {
79        dict = this.instanceMapsByScopedId_[scopedId.scope];
80      } else {
81        dict = {};
82        this.instanceMapsByScopedId_[scopedId.scope] = dict;
83      }
84      var instanceMap = dict[scopedId.id];
85      if (instanceMap)
86        return instanceMap;
87      instanceMap = new tr.model.TimeToObjectInstanceMap(
88          this.createObjectInstance_, this.parent, scopedId);
89      dict[scopedId.id] = instanceMap;
90      return instanceMap;
91    },
92
93    idWasCreated: function(scopedId, category, name, ts) {
94      var instanceMap = this.getOrCreateInstanceMap_(scopedId);
95      return instanceMap.idWasCreated(category, name, ts);
96    },
97
98    addSnapshot: function(
99        scopedId, category, name, ts, args, opt_baseTypeName) {
100      var instanceMap = this.getOrCreateInstanceMap_(scopedId);
101      var snapshot = instanceMap.addSnapshot(
102          category, name, ts, args, opt_baseTypeName);
103      if (snapshot.objectInstance.category != category) {
104        var msg = 'Added snapshot name=' + name + ' with cat=' + category +
105            ' impossible. It instance was created/snapshotted with cat=' +
106            snapshot.objectInstance.category + ' name=' +
107            snapshot.objectInstance.name;
108        throw new Error(msg);
109      }
110      if (opt_baseTypeName &&
111          snapshot.objectInstance.baseTypeName != opt_baseTypeName) {
112        throw new Error('Could not add snapshot with baseTypeName=' +
113                        opt_baseTypeName + '. It ' +
114                        'was previously created with name=' +
115                        snapshot.objectInstance.baseTypeName);
116      }
117      if (snapshot.objectInstance.name != name) {
118        throw new Error('Could not add snapshot with name=' + name + '. It ' +
119                        'was previously created with name=' +
120                        snapshot.objectInstance.name);
121      }
122      return snapshot;
123    },
124
125    idWasDeleted: function(scopedId, category, name, ts) {
126      var instanceMap = this.getOrCreateInstanceMap_(scopedId);
127      var deletedInstance = instanceMap.idWasDeleted(category, name, ts);
128      if (!deletedInstance)
129        return;
130      if (deletedInstance.category != category) {
131        var msg = 'Deleting object ' + deletedInstance.name +
132            ' with a different category ' +
133            'than when it was created. It previous had cat=' +
134            deletedInstance.category + ' but the delete command ' +
135            'had cat=' + category;
136        throw new Error(msg);
137      }
138      if (deletedInstance.baseTypeName != name) {
139        throw new Error('Deletion requested for name=' +
140                        name + ' could not proceed: ' +
141                        'An existing object with baseTypeName=' +
142                        deletedInstance.baseTypeName + ' existed.');
143      }
144    },
145
146    autoDeleteObjects: function(maxTimestamp) {
147      tr.b.iterItems(this.instanceMapsByScopedId_, function(scope, imapById) {
148        tr.b.iterItems(imapById, function(id, i2imap) {
149          var lastInstance = i2imap.lastInstance;
150          if (lastInstance.deletionTs != Number.MAX_VALUE)
151            return;
152          i2imap.idWasDeleted(
153              lastInstance.category, lastInstance.name, maxTimestamp);
154          // idWasDeleted will cause lastInstance.deletionTsWasExplicit to be
155          // set to true. Unset it here.
156          lastInstance.deletionTsWasExplicit = false;
157        });
158      });
159    },
160
161    getObjectInstanceAt: function(scopedId, ts) {
162      var instanceMap;
163      if (scopedId.scope in this.instanceMapsByScopedId_)
164        instanceMap = this.instanceMapsByScopedId_[scopedId.scope][scopedId.id];
165      if (!instanceMap)
166        return undefined;
167      return instanceMap.getInstanceAt(ts);
168    },
169
170    getSnapshotAt: function(scopedId, ts) {
171      var instance = this.getObjectInstanceAt(scopedId, ts);
172      if (!instance)
173        return undefined;
174      return instance.getSnapshotAt(ts);
175    },
176
177    iterObjectInstances: function(iter, opt_this) {
178      opt_this = opt_this || this;
179      tr.b.iterItems(this.instanceMapsByScopedId_, function(scope, imapById) {
180        tr.b.iterItems(imapById, function(id, i2imap) {
181          i2imap.instances.forEach(iter, opt_this);
182        });
183      });
184    },
185
186    getAllObjectInstances: function() {
187      var instances = [];
188      this.iterObjectInstances(function(i) { instances.push(i); });
189      return instances;
190    },
191
192    getAllInstancesNamed: function(name) {
193      return this.instancesByTypeName_[name];
194    },
195
196    getAllInstancesByTypeName: function() {
197      return this.instancesByTypeName_;
198    },
199
200    preInitializeAllObjects: function() {
201      this.iterObjectInstances(function(instance) {
202        instance.preInitialize();
203      });
204    },
205
206    initializeAllObjects: function() {
207      this.iterObjectInstances(function(instance) {
208        instance.initialize();
209      });
210    },
211
212    initializeInstances: function() {
213      this.iterObjectInstances(function(instance) {
214        instance.initialize();
215      });
216    },
217
218    updateBounds: function() {
219      this.bounds.reset();
220      this.iterObjectInstances(function(instance) {
221        instance.updateBounds();
222        this.bounds.addRange(instance.bounds);
223      }, this);
224    },
225
226    shiftTimestampsForward: function(amount) {
227      this.iterObjectInstances(function(instance) {
228        instance.shiftTimestampsForward(amount);
229      });
230    },
231
232    addCategoriesToDict: function(categoriesDict) {
233      this.iterObjectInstances(function(instance) {
234        categoriesDict[instance.category] = true;
235      });
236    }
237  };
238
239  return {
240    ObjectCollection: ObjectCollection
241  };
242});
243</script>
244