search

src/lab/util/AbstractSubject.js

1// Copyright 2016 Erik Neumann. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the 'License');
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an 'AS IS' BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15goog.provide('myphysicslab.lab.util.AbstractSubject');
16
17goog.require('goog.array');
18goog.require('myphysicslab.lab.util.ParameterBoolean');
19goog.require('myphysicslab.lab.util.ParameterNumber');
20goog.require('myphysicslab.lab.util.ParameterString');
21goog.require('myphysicslab.lab.util.UtilityCore');
22goog.require('myphysicslab.lab.util.Subject');
23
24goog.scope(function() {
25
26var UtilityCore = myphysicslab.lab.util.UtilityCore;
27var Subject = myphysicslab.lab.util.Subject;
28var ParameterBoolean = myphysicslab.lab.util.ParameterBoolean;
29var ParameterNumber = myphysicslab.lab.util.ParameterNumber;
30var ParameterString = myphysicslab.lab.util.ParameterString;
31
32/** Implementation of {@link myphysicslab.lab.util.Subject} interface.
33
34@param {string=} name
35@constructor
36@struct
37@implements {myphysicslab.lab.util.Subject}
38@abstract
39*/
40myphysicslab.lab.util.AbstractSubject = function(name) {
41 /* This implementation makes some direct calls on itself, so it is not
42 * appropriate for a [decorator class](http://en.wikipedia.org/wiki/Decorator_pattern)
43 * that needs to override methods of this class. If a class calls a method on itself,
44 * then that method cannot be overridden by a decorator.
45 */
46 if (!name) {
47 throw new Error('no name');
48 }
49 /**
50 * @type {string}
51 * @private
52 */
53 this.name_ = UtilityCore.validName(UtilityCore.toName(name));
54 /** The list of Observers of this Subject.
55 * @type {!Array<!myphysicslab.lab.util.Observer>}
56 * @private
57 */
58 this.observers_ = [];
59 /**
60 * @type {!Array<!myphysicslab.lab.util.Parameter>}
61 * @private
62 */
63 this.paramList_ = [];
64 /**
65 * @type {boolean}
66 * @private
67 */
68 this.doBroadcast_ = true;
69};
70var AbstractSubject = myphysicslab.lab.util.AbstractSubject;
71
72if (!UtilityCore.ADVANCED) {
73 /** @inheritDoc */
74 AbstractSubject.prototype.toString = function() {
75 // assumes that className and name are displayed by sub-class
76 return ', parameters: ['
77 + goog.array.map(this.paramList_, function(p) { return p.toStringShort(); })
78 +'], observers: ['
79 + goog.array.map(this.observers_, function(p) { return p.toStringShort(); })
80 +']}';
81 };
82
83 /** @inheritDoc */
84 AbstractSubject.prototype.toStringShort = function() {
85 return this.getClassName() + '{name_: "' + this.getName() + '"}';
86 };
87};
88
89/** @inheritDoc */
90AbstractSubject.prototype.addObserver = function(observer) {
91 if (!goog.array.contains(this.observers_, observer)) {
92 this.observers_.push(observer);
93 }
94};
95
96/** Adds the Parameter to the list of this Subject's available Parameters.
97@throws {Error} if a Parameter with the same name already exists.
98@param {!myphysicslab.lab.util.Parameter} parameter the Parameter to add
99*/
100AbstractSubject.prototype.addParameter = function(parameter) {
101 var name = parameter.getName();
102 var p = this.getParam(name);
103 if (p != null) {
104 throw new Error('parameter '+name+' already exists: '+p);
105 }
106 this.paramList_.push(parameter);
107};
108
109/** @inheritDoc */
110AbstractSubject.prototype.broadcast = function(evt) {
111 if (this.doBroadcast_) {
112 // For debugging: can see events being broadcast here.
113 //if (!this.getName().match(/.*GRAPH.*/i)) { console.log('broadcast '+evt); }
114 for (var i=0, len=this.observers_.length; i<len; i++) {
115 this.observers_[i].observe(evt);
116 }
117 }
118};
119
120/** @inheritDoc */
121AbstractSubject.prototype.broadcastParameter = function(name) {
122 var p = this.getParam(name);
123 if (p == null) {
124 throw new Error('unknown Parameter '+name);
125 }
126 this.broadcast(p);
127};
128
129/** Returns whether this Subject is broadcasting events.
130@return {boolean} whether this Subject is broadcasting events
131@protected
132*/
133AbstractSubject.prototype.getBroadcast = function() {
134 return this.doBroadcast_;
135};
136
137/** Returns name of class of this object.
138* @return {string} name of class of this object.
139* @abstract
140*/
141AbstractSubject.prototype.getClassName = function() {};
142
143/** @inheritDoc */
144AbstractSubject.prototype.getName = function() {
145 return this.name_;
146};
147
148/** @inheritDoc */
149AbstractSubject.prototype.getObservers = function() {
150 return goog.array.clone(this.observers_);
151};
152
153/** Returns the Parameter with the given name, or null if not found
154* @param {string} name name of parameter to search for
155* @return {?myphysicslab.lab.util.Parameter} the Parameter with the given name, or
156 null if not found
157* @private
158*/
159AbstractSubject.prototype.getParam = function(name) {
160 name = UtilityCore.toName(name);
161 return goog.array.find(this.paramList_, function(p) {
162 return p.getName() == name;
163 });
164};
165
166/** @inheritDoc */
167AbstractSubject.prototype.getParameter = function(name) {
168 var p = this.getParam(name);
169 if (p != null) {
170 return p;
171 }
172 throw new Error('Parameter not found '+name);
173};
174
175/** @inheritDoc */
176AbstractSubject.prototype.getParameterBoolean = function(name) {
177 var p = this.getParam(name);
178 if (p instanceof ParameterBoolean) {
179 return p;
180 }
181 throw new Error('ParameterBoolean not found '+name);
182};
183
184/** @inheritDoc */
185AbstractSubject.prototype.getParameterNumber = function(name) {
186 var p = this.getParam(name);
187 if (p instanceof ParameterNumber) {
188 return p;
189 }
190 throw new Error('ParameterNumber not found '+name);
191};
192
193/** @inheritDoc */
194AbstractSubject.prototype.getParameterString = function(name) {
195 var p = this.getParam(name);
196 if (p instanceof ParameterString) {
197 return p;
198 }
199 throw new Error('ParameterString not found '+name);
200};
201
202/** @inheritDoc */
203AbstractSubject.prototype.getParameters = function() {
204 return goog.array.clone(this.paramList_);
205};
206
207/** @inheritDoc */
208AbstractSubject.prototype.removeObserver = function(observer) {
209 goog.array.remove(this.observers_, observer);
210};
211
212/** Removes the Parameter from the list of this Subject's available Parameters.
213@param {!myphysicslab.lab.util.Parameter} parameter the Parameter to remove
214*/
215AbstractSubject.prototype.removeParameter = function(parameter) {
216 goog.array.remove(this.paramList_, parameter);
217};
218
219/** Sets whether this Subject will broadcast events, typically used to temporarily
220disable broadcasting. Intended to be used in situations where a subclass overrides a
221method that broadcasts an event. This allows the subclass to prevent the superclass
222broadcasting that event, so that the subclass can broadcast the event when the method is
223completed.
224@param {boolean} value whether this Subject should broadcast events
225@return {boolean} the previous value
226@protected
227*/
228AbstractSubject.prototype.setBroadcast = function(value) {
229 var saveBroadcast = this.doBroadcast_;
230 this.doBroadcast_ = value;
231 return saveBroadcast;
232};
233
234}); // goog.scope