001/* A parameter that is shared globally in a model.
002
003 Copyright (c) 2004-2015 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027
028 */
029package ptolemy.actor.parameters;
030
031import java.io.Writer;
032import java.lang.ref.WeakReference;
033import java.util.Collection;
034import java.util.HashMap;
035import java.util.HashSet;
036import java.util.Iterator;
037import java.util.LinkedList;
038import java.util.List;
039
040import ptolemy.actor.ApplicationConfigurer;
041import ptolemy.actor.Executable;
042import ptolemy.actor.Initializable;
043import ptolemy.data.Token;
044import ptolemy.data.expr.Parameter;
045import ptolemy.kernel.util.IllegalActionException;
046import ptolemy.kernel.util.NameDuplicationException;
047import ptolemy.kernel.util.NamedObj;
048import ptolemy.kernel.util.Workspace;
049
050///////////////////////////////////////////////////////////////////
051//// SharedParameter
052
053/**
054 This parameter is shared throughout a model. Changing the expression of
055 any one instance of the parameter will result in all instances that
056 are shared being changed to the same expression.  An instance elsewhere
057 in the model (within the same top level) is shared if it has the
058 same name and its container is of the class specified in the
059 constructor (or of the container class, if no class is specified
060 in the constructor). Note that two parameters with the same
061 expression do not necessarily have the same value, since the
062 expression may reference other parameters that are in scope.
063 <p>
064 One exception is that if this parameter is (deeply) within an
065 instance of EntityLibrary, then the parameter is not shared.
066 Were this not the case, then opening a library containing this
067 parameter would force expansion of all the sublibraries of
068 EntityLibrary, which would defeat the lazy instantiation
069 of EntityLibrary.
070 <p>
071 When this parameter is constructed, the specified container
072 will be used to infer the parameter value from the container.
073 That is, if the container is within a model that has any
074 parameters shared with this one, then the value will be
075 set to the last of those encountered.
076 If the container is subsequently changed, it is up to the
077 code implementing the change to use the inferValueFromContext()
078 method to reset the value to match the new context.
079 Note that this really needs to be done if the container
080 of the container, or its container, or any container
081 above this parameter is changed.  It is recommended to use
082 the four-argument constructor, so you can specify a default
083 value to use if there are no shared parameters.
084 <p>
085 Note that it might be tempting to use a static parameter field
086 to achieve this effect, but this would be problematic for two
087 reasons. First, the parameter would only be able to have one
088 container. Second, the parameter would be shared across all
089 models in the same Java virtual machine, not just within a
090 single model.
091
092 @author Edward A. Lee, contributor: Christopher Brooks, Bert Rodiers
093 @version $Id$
094 @since Ptolemy II 8.0
095 @Pt.ProposedRating Green (eal)
096 @Pt.AcceptedRating Green (acataldo)
097 */
098public class SharedParameter extends Parameter implements Initializable {
099    /** Construct a parameter with the given container and name.
100     *  The container class will be used to determine which other
101     *  instances of SharedParameter are shared with this one.
102     *  @param container The container.
103     *  @param name The name of the parameter.
104     *  @exception IllegalActionException If the parameter is not of an
105     *   acceptable class for the container.
106     *  @exception NameDuplicationException If the name coincides with
107     *   a parameter already in the container.
108     */
109    public SharedParameter(NamedObj container, String name)
110            throws IllegalActionException, NameDuplicationException {
111        this(container, name, null, "");
112    }
113
114    /** Construct a parameter with the given container, name, and
115     *  container class. The specified class will be used to determine
116     *  which other instances of SharedParameter are shared with this one.
117     *  @param container The container.
118     *  @param name The name of the parameter.
119     *  @param containerClass The class used to determine shared instances.
120     *  @exception IllegalActionException If the parameter is not of an
121     *   acceptable class for the container.
122     *  @exception NameDuplicationException If the name coincides with
123     *   a parameter already in the container.
124     */
125    public SharedParameter(NamedObj container, String name,
126            Class<?> containerClass)
127            throws IllegalActionException, NameDuplicationException {
128        this(container, name, containerClass, "");
129    }
130
131    /** Construct a parameter with the given container, name,
132     *  container class, and default value.  This is the preferred
133     *  constructor to use.
134     *  The specified class will be used to determine
135     *  which other instances of SharedParameter are shared with this one.
136     *  @param container The container.
137     *  @param name The name of the parameter.
138     *  @param containerClass The class used to determine shared instances.
139     *   An argument of null means simply to use the class of the container,
140     *   whatever that happens to be.
141     *  @param defaultValue The default value to use if the container's
142     *   model has no shared parameters.
143     *  @exception IllegalActionException If the parameter is not of an
144     *   acceptable class for the container, or an empty string to specify no
145     *   default value.
146     *  @exception NameDuplicationException If the name coincides with
147     *   a parameter already in the container.
148     */
149    public SharedParameter(NamedObj container, String name,
150            Class<?> containerClass, String defaultValue)
151            throws IllegalActionException, NameDuplicationException {
152        super(container, name);
153
154        if (containerClass == null) {
155            containerClass = container.getClass();
156        }
157        if (_delayValidation) {
158            _suppressingPropagation = true;
159            setLazy(true);
160        }
161        _containerClass = containerClass;
162        inferValueFromContext(defaultValue);
163        _constructionFinished = true;
164    }
165
166    ///////////////////////////////////////////////////////////////////
167    ////                         public methods                    ////
168
169    /** Add the specified object to the list of objects whose
170     *  preinitialize(), initialize(), and wrapup()
171     *  methods should be invoked upon invocation of the corresponding
172     *  methods of this object.
173     *  @param initializable The object whose methods should be invoked.
174     *  @see #removeInitializable(Initializable)
175     *  @see ptolemy.actor.CompositeActor#addPiggyback(Executable)
176     */
177    @Override
178    public void addInitializable(Initializable initializable) {
179        if (_initializables == null) {
180            _initializables = new LinkedList<Initializable>();
181        }
182        _initializables.add(initializable);
183    }
184
185    /** Override the base class to register the object, since setName()
186     *  will not be called.
187     *  @param workspace The workspace for the new object.
188     *  @return A new NamedObj.
189     *  @exception CloneNotSupportedException If any of the attributes
190     *   cannot be cloned.
191     *  @see #exportMoML(Writer, int, String)
192     *  @see #setDeferringChangeRequests(boolean)
193     */
194    @Override
195    public Object clone(Workspace workspace) throws CloneNotSupportedException {
196        SharedParameter newObject = (SharedParameter) super.clone(workspace);
197        SharedParameterRegistry registry = _getSharedParameterRegistry(
198                workspace);
199        registry.register(newObject);
200        newObject._sharedParameterSet = null;
201        return newObject;
202    }
203
204    /** Get the token contained by this variable.  The type of the returned
205     *  token is always that returned by getType().  Calling this method
206     *  will trigger evaluation of the expression, if the value has been
207     *  given by setExpression(). Notice the evaluation of the expression
208     *  can trigger an exception if the expression is not valid, or if the
209     *  result of the expression violates type constraints specified by
210     *  setTypeEquals() or setTypeAtMost(), or if the result of the expression
211     *  is null and there are other variables that depend on this one.
212     *  The returned value will be null if neither an expression nor a
213     *  token has been set, or either has been set to null.
214     *  @return The token contained by this variable converted to the
215     *   type of this variable, or null if there is none.
216     *  @exception IllegalActionException If the expression cannot
217     *   be parsed or cannot be evaluated, or if the result of evaluation
218     *   violates type constraints, or if the result of evaluation is null
219     *   and there are variables that depend on this one.
220     *  @see #setToken(Token)
221     */
222    @Override
223    public Token getToken() throws IllegalActionException {
224        if (_delayValidation) {
225            boolean previousSuppressing = _suppressingPropagation;
226            try {
227                _suppressingPropagation = false;
228                validate();
229                return super.getToken();
230            } finally {
231                _suppressingPropagation = previousSuppressing;
232            }
233        } else {
234            return super.getToken();
235        }
236    }
237
238    /** Return the top level of the containment hierarchy, unless
239     *  one of the containers is an instance of EntityLibrary,
240     *  in which case, return null.
241     *  @return The top level, or null if this is within a library.
242     */
243    public NamedObj getRoot() {
244        NamedObj result = this;
245
246        while (result.getContainer() != null) {
247            result = result.getContainer();
248            if (result instanceof ApplicationConfigurer) {
249                // If the results is a Configuration, then go no higher.
250                // If we do go higher, then we end up expanding the actor
251                // library tree which take a long time and fails if
252                // not all the actors are present.  For example, not
253                // everyone will have Matlab or quicktime.
254                return null;
255            }
256        }
257
258        return result;
259    }
260
261    /** Infer the value of this parameter from the container
262     *  context. That is, search for parameters that are
263     *  shared with this one, and set the value of this parameter
264     *  to match the first one encountered.
265     *  If there are no shared parameters, then assign the
266     *  default value given as an argument.
267     *  @param defaultValue The default parameter value to use.
268     */
269    public void inferValueFromContext(String defaultValue) {
270        SharedParameter candidate = null;
271        NamedObj toplevel = getRoot();
272
273        if (toplevel != null && toplevel != this) {
274            candidate = _getOneSharedParameter(toplevel);
275        }
276        if (candidate != null) {
277            defaultValue = candidate.getExpression();
278        }
279        boolean previousSuppressing = _suppressingPropagation;
280        try {
281            _suppressingPropagation = true;
282
283            setExpression(defaultValue);
284
285            // Try getting the evaluated token.
286            if (candidate != null && candidate.isKnown()) {
287                setToken(candidate.getToken());
288            }
289
290        } catch (IllegalActionException e) {
291            // The token is not set when this happens.
292
293        } finally {
294            _suppressingPropagation = previousSuppressing;
295        }
296    }
297
298    /** Do nothing except invoke the initialize methods
299     *  of objects that have been added using addInitializable().
300     *  @exception IllegalActionException If one of the added objects
301     *   throws it.
302     */
303    @Override
304    public void initialize() throws IllegalActionException {
305        // Invoke initializable methods.
306        if (_initializables != null) {
307            for (Initializable initializable : _initializables) {
308                initializable.initialize();
309            }
310        }
311    }
312
313    /** Return true.
314     *  @return True.
315     */
316    public boolean isFireFunctional() {
317        return true;
318    }
319
320    /** Return false.
321     *  @return False.
322     */
323    public boolean isStrict() {
324        return false;
325    }
326
327    /** Return true if this instance is suppressing propagation.
328     *  Unless setSuppressingPropagation() has been called, this
329     *  returns false.
330     *  @return Returns whether this instance is suppressing propagation.
331     *  @see #setSuppressingPropagation(boolean)
332     */
333    public boolean isSuppressingPropagation() {
334        return _suppressingPropagation;
335    }
336
337    /** Traverse the model and update values.
338     *  @exception IllegalActionException Not thrown in this base class.
339     */
340    @Override
341    public void preinitialize() throws IllegalActionException {
342        // Invoke initializable methods.
343        if (_initializables != null) {
344            for (Initializable initializable : _initializables) {
345                initializable.preinitialize();
346            }
347        }
348        if (_delayValidation) {
349            _suppressingPropagation = false;
350            validate();
351        }
352    }
353
354    /** Remove the specified object from the list of objects whose
355     *  preinitialize(), initialize(), and wrapup()
356     *  methods should be invoked upon invocation of the corresponding
357     *  methods of this object. If the specified object is not
358     *  on the list, do nothing.
359     *  @param initializable The object whose methods should no longer be invoked.
360     *  @see #addInitializable(Initializable)
361     *  @see ptolemy.actor.CompositeActor#removePiggyback(Executable)
362     */
363    @Override
364    public void removeInitializable(Initializable initializable) {
365        if (_initializables != null) {
366            _initializables.remove(initializable);
367            if (_initializables.size() == 0) {
368                _initializables = null;
369            }
370        }
371    }
372
373    /** Override the base class to register as an initializable slave with the
374     *  new container. This results in the preinitialize(), initialize(), and
375     *  wrapup() methods of this instance being invoked when the corresponding
376     *  method of the container are invoked.
377     *  @param container The proposed container.
378     *  @exception IllegalActionException If the action would result in a
379     *   recursive containment structure, or if
380     *   this entity and container are not in the same workspace.
381     *  @exception NameDuplicationException If the container already has
382     *   an entity with the name of this entity.
383     */
384    @Override
385    public void setContainer(NamedObj container)
386            throws IllegalActionException, NameDuplicationException {
387        NamedObj previousContainer = getContainer();
388        if (previousContainer != container) {
389            if (previousContainer instanceof Initializable) {
390                ((Initializable) previousContainer).removeInitializable(this);
391            }
392            if (container instanceof Initializable) {
393                ((Initializable) container).addInitializable(this);
394            }
395        }
396        super.setContainer(container);
397    }
398
399    /** Override the base class to register as a shared parameter in the workspace.
400     *  @param name The proposed name.
401     *  @exception IllegalActionException If the name contains a period
402     *   or if this variable is referenced in some other expression.
403     *  @exception NameDuplicationException If there is already an
404     *       attribute with the same name in the container.
405     */
406    @Override
407    public void setName(String name)
408            throws IllegalActionException, NameDuplicationException {
409        if (name != null && !name.equals(getName())) {
410            SharedParameterRegistry registry = _getSharedParameterRegistry(
411                    workspace());
412            // Unregister under previous name.
413            if (getName() != null && !getName().equals("")) {
414                registry.unregister(this);
415            }
416            super.setName(name);
417            registry.register(this);
418        }
419    }
420
421    /** Override the base class to also set the expression of shared
422     *  parameters.
423     *  @param expression The expression.
424     */
425    @Override
426    public void setExpression(String expression) {
427        // The expression may have already been inferred from context,
428        // in which case we don't want to set it again. This prevents
429        // spurious replication of the parameter in the MoML file.
430        if (expression != null && expression.equals(getExpression())) {
431            return;
432        }
433        super.setExpression(expression);
434
435        if (!_suppressingPropagation) {
436            Iterator<SharedParameter> sharedParameters = sharedParameterSet()
437                    .iterator();
438
439            while (sharedParameters.hasNext()) {
440                SharedParameter sharedParameter = sharedParameters.next();
441
442                if (sharedParameter != this) {
443                    try {
444                        sharedParameter._suppressingPropagation = true;
445
446                        if (!sharedParameter.getExpression()
447                                .equals(expression)) {
448                            sharedParameter.setExpression(expression);
449                        }
450                    } finally {
451                        sharedParameter._suppressingPropagation = false;
452                    }
453                }
454            }
455        }
456    }
457
458    /** Specify whether this instance should be suppressing
459     *  propagation. If this is called with value true, then
460     *  changes to this parameter will not propagate to other
461     *  shared instances in the model.
462     *  @param propagation True to suppress propagation.
463     *  @see #isSuppressingPropagation()
464     */
465    public void setSuppressingPropagation(boolean propagation) {
466        _suppressingPropagation = propagation;
467    }
468
469    /** Override the base class to also set the token of shared
470     *  parameters.
471     *  @param token The token.
472     *  @exception IllegalActionException Thrown if super class throws it.
473     *  @see #getToken()
474     */
475    @Override
476    public void setToken(Token token) throws IllegalActionException {
477
478        super.setToken(token);
479
480        if (!_suppressingPropagation) {
481            Iterator<SharedParameter> sharedParameters = sharedParameterSet()
482                    .iterator();
483
484            while (sharedParameters.hasNext()) {
485                SharedParameter sharedParameter = sharedParameters.next();
486
487                if (sharedParameter != this) {
488                    try {
489                        sharedParameter._suppressingPropagation = true;
490                        sharedParameter.setToken(token);
491                    } finally {
492                        sharedParameter._suppressingPropagation = false;
493                    }
494                }
495            }
496        }
497    }
498
499    /** Return a collection of all the shared parameters within the
500     *  same model as this parameter.  If there are no such parameters
501     *  or if this parameter is deeply contained within an EntityLibrary, then
502     *  return an empty collection. Otherwise, the list will include this
503     *  instance if this instance has a container. If this instance has
504     *  no container, then return an empty collection.
505     *  A shared parameter is one that is an instance of SharedParameter,
506     *  has the same name as this one, and is contained by the container
507     *  class specified in the constructor.
508     *  @return A collection of parameters.
509     */
510    public synchronized Collection sharedParameterSet() {
511        // If we call clone(), then _sharedParameterSet could be null.
512        if (workspace().getVersion() != _sharedParameterSetVersion
513                || _sharedParameterSet == null) {
514            try {
515                workspace().getReadAccess();
516                _sharedParameterSet = new HashSet<SharedParameter>();
517                _sharedParameterSetVersion = workspace().getVersion();
518                NamedObj root = getRoot();
519                if (root != null) {
520                    SharedParameterRegistry registry = _getSharedParameterRegistry(
521                            workspace());
522                    for (WeakReference<SharedParameter> reference : registry
523                            .getSharedParametersWithName(getName())) {
524                        if (reference != null) {
525                            SharedParameter parameter = reference.get();
526                            if (parameter != null) {
527                                // Have a candidate. See if the roots match and if
528                                // the container classes match.
529                                if (parameter.getRoot() == root
530                                        && parameter._containerClass == _containerClass) {
531                                    _sharedParameterSet.add(parameter);
532                                }
533                            }
534                        }
535                    }
536                }
537            } finally {
538                workspace().doneReading();
539            }
540        }
541        return _sharedParameterSet;
542    }
543
544    /** Supress propagation.
545     *  @exception IllegalActionException Not thrown in this base class.
546     */
547    @Override
548    public void wrapup() throws IllegalActionException {
549        // Invoke initializable methods.
550        if (_initializables != null) {
551            for (Initializable initializable : _initializables) {
552                initializable.wrapup();
553            }
554        }
555        if (_delayValidation) {
556            _suppressingPropagation = true;
557        }
558    }
559
560    /** Override the base class to also validate the shared instances.
561     *  @return A Collection of all the shared parameters within the same
562     *   model as this parameter, see {@link #sharedParameterSet}.
563     *  @exception IllegalActionException If this variable or a
564     *   variable dependent on this variable cannot be evaluated (and is
565     *   not lazy) and the model error handler throws an exception.
566     *   Also thrown if the change is not acceptable to the container.
567     */
568    @Override
569    public Collection validate() throws IllegalActionException {
570        Collection result = super.validate();
571        if (result == null) {
572            result = new HashSet();
573        }
574
575        // NOTE: This is called by setContainer(), which is called from
576        // within a base class constructor. That call occurs before this
577        // object has been fully constructed. It doesn't make sense at
578        // that time to propagate validation to shared instances, since
579        // in fact the value of this shared parameter will be inferred
580        // from those instances if there are any. So in that case, we
581        // just return.
582        if (!_constructionFinished) {
583            return result;
584        }
585
586        if (!_suppressingPropagation) {
587            Iterator<SharedParameter> sharedParameters = sharedParameterSet()
588                    .iterator();
589            while (sharedParameters.hasNext()) {
590                SharedParameter sharedParameter = sharedParameters.next();
591                if (sharedParameter != this) {
592                    try {
593                        sharedParameter._suppressingPropagation = true;
594                        result.addAll(sharedParameter.validate());
595                        result.add(sharedParameter);
596                    } finally {
597                        sharedParameter._suppressingPropagation = false;
598                    }
599                }
600            }
601        }
602        return result;
603    }
604
605    ///////////////////////////////////////////////////////////////////
606    ////                         protected methods                 ////
607
608    /** Override the base class to do the propagation only if
609     *  the specified destination is not shared, because if
610     *  it is shared, then the value will be propagated
611     *  in through the sharing mechanism.
612     *  @param destination Object to which to propagate the
613     *   value.
614     *  @exception IllegalActionException If the value cannot
615     *   be propagated.
616     */
617    @Override
618    protected void _propagateValue(NamedObj destination)
619            throws IllegalActionException {
620        if (!sharedParameterSet().contains(destination)) {
621            super._propagateValue(destination);
622        }
623    }
624
625    ///////////////////////////////////////////////////////////////////
626    ////                         private methods                   ////
627
628    /** Find and return one shared parameter deeply contained by
629     *  the specified container.  If there is no such parameter, then
630     *  return null.
631     *  A shared parameter is one that is an instance of SharedParameter,
632     *  has the same name as this one, and is contained by the container
633     *  class specified in the constructor.
634     *  @param container The container.
635     *  @return A shared parameter different from this one, or null if
636     *   there is none.
637     */
638    private SharedParameter _getOneSharedParameter(NamedObj container) {
639        if (workspace().getVersion() != _sharedParameterVersion) {
640            try {
641                workspace().getReadAccess();
642                _sharedParameter = null;
643                _sharedParameterVersion = workspace().getVersion();
644                NamedObj root = getRoot();
645                if (root != null) {
646                    SharedParameterRegistry registry = _getSharedParameterRegistry(
647                            workspace());
648                    for (WeakReference<SharedParameter> reference : registry
649                            .getSharedParametersWithName(getName())) {
650                        if (reference != null) {
651                            SharedParameter parameter = reference.get();
652                            if (parameter != null) {
653                                // Have a candidate. See if the roots match and if
654                                // the container classes match.
655                                if (parameter != this
656                                        && parameter.getRoot() == root
657                                        && parameter._containerClass == _containerClass) {
658                                    _sharedParameter = parameter;
659                                    // Successful match. No need to search further.
660                                    return _sharedParameter;
661                                }
662                            }
663                        }
664                    }
665                }
666            } finally {
667                workspace().doneReading();
668            }
669        }
670        return _sharedParameter;
671    }
672
673    /** Return the shared parameter registry associated with this workspace.
674     */
675    private static synchronized SharedParameterRegistry _getSharedParameterRegistry(
676            Workspace workspace) {
677
678        Iterator<SharedParameterRegistry> iterator = _REGISTRY.iterator();
679        while (iterator.hasNext()) {
680            SharedParameterRegistry registry = iterator.next();
681            Workspace registerWorkspace = registry.workspace();
682            if (registerWorkspace == workspace) {
683                return registry;
684            } else if (registerWorkspace == null) {
685                // Clean up register.
686                // The workspace is a weak reference. If it is null, we know that the
687                // workspace is garbage collected and hence we can remove the entry in the
688                // list.
689                iterator.remove();
690            }
691        }
692        SharedParameterRegistry result = new SharedParameterRegistry(workspace);
693        _REGISTRY.add(result);
694        return result;
695    }
696
697    ///////////////////////////////////////////////////////////////////
698    ////                         private variables                 ////
699
700    /** Indicator that the constructor has reached the end. */
701    private boolean _constructionFinished = false;
702
703    /** The container class. */
704    private Class<?> _containerClass;
705
706    /** True if we are delaying validation.
707     *  FIXME: This variable is only present for testing and development.
708     *  To try out the delay of validation, set it to true and recompile.
709     */
710    private static final boolean _delayValidation = false;
711
712    /** Empty list. */
713    private static Collection<WeakReference<SharedParameter>> _EMPTY_LIST = new LinkedList<WeakReference<SharedParameter>>();
714
715    /** List of objects whose (pre)initialize() and wrapup() methods
716     *  should be slaved to these.
717     */
718    private transient List<Initializable> _initializables;
719
720    /** Cached version of a shared parameter. */
721    private SharedParameter _sharedParameter;
722
723    /** Version for the cache. */
724    private long _sharedParameterVersion = -1L;
725
726    /** Cached version of the shared parameter set. */
727    private HashSet<SharedParameter> _sharedParameterSet;
728
729    /** Version for the cache. */
730    private long _sharedParameterSetVersion = -1L;
731
732    /** Registry by workspace. */
733    private static List<SharedParameterRegistry> _REGISTRY = new LinkedList<SharedParameterRegistry>();
734
735    /** Indicator to suppress propagation. */
736    private boolean _suppressingPropagation = false;
737
738    ///////////////////////////////////////////////////////////////////
739    ////                         inner classes                     ////
740
741    /** Registry of shared parameters. This is a data structure
742     *  that registers all shared parameters in a workspace. This is
743     *  more efficient than searching through a model to find all the
744     *  shared parameters. It stores one collection of shared parameters
745     *  for each name.
746     */
747    private static class SharedParameterRegistry {
748
749        public SharedParameterRegistry(Workspace workspace) {
750            _workspace = new WeakReference<Workspace>(workspace);
751        }
752
753        /** Return all shared parameters with the specified name.
754         *  This returns a collection of weak references.
755         */
756        public synchronized Collection<WeakReference<SharedParameter>> getSharedParametersWithName(
757                String name) {
758            Collection<WeakReference<SharedParameter>> set = _sharedParametersByName
759                    .get(name);
760            if (set == null) {
761                return _EMPTY_LIST;
762            } else {
763                return set;
764            }
765        }
766
767        /** Register the specified shared parameter. */
768        public synchronized void register(SharedParameter parameter) {
769            Collection<WeakReference<SharedParameter>> set = _sharedParametersByName
770                    .get(parameter.getName());
771            if (set == null) {
772                set = new LinkedList<WeakReference<SharedParameter>>();
773                _sharedParametersByName.put(parameter.getName(), set);
774            }
775            set.add(new WeakReference<SharedParameter>(parameter));
776        }
777
778        public synchronized void unregister(SharedParameter parameter) {
779            Collection<WeakReference<SharedParameter>> set = _sharedParametersByName
780                    .get(parameter.getName());
781            if (set != null) {
782                set.remove(new WeakReference<SharedParameter>(parameter));
783            }
784        }
785
786        /** Return the workspace. */
787        public Workspace workspace() {
788            return _workspace.get();
789        }
790
791        private HashMap<String, Collection<WeakReference<SharedParameter>>> _sharedParametersByName = new HashMap<String, Collection<WeakReference<SharedParameter>>>();
792
793        private WeakReference<Workspace> _workspace;
794    }
795}