001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.scxml;
018    
019    import java.io.Serializable;
020    import java.util.HashMap;
021    import java.util.Iterator;
022    import java.util.LinkedHashSet;
023    import java.util.Map;
024    import java.util.Set;
025    
026    import org.apache.commons.scxml.model.SCXML;
027    import org.apache.commons.scxml.model.Transition;
028    import org.apache.commons.scxml.model.TransitionTarget;
029    
030    /**
031     * The registry where SCXML listeners are recorded for nodes of
032     * interest such as the <code>SCXML</code> root,
033     * <code>TransitionTarget</code>s and <code>Transition</code>s.
034     * The notification registry keeps track of all
035     * <code>SCXMLListener</code>s attached and notifies relevant
036     * listeners of the events that interest them.
037     *
038     */
039    public final class NotificationRegistry implements Serializable {
040    
041        /**
042         * Serial version UID.
043         */
044        private static final long serialVersionUID = 1L;
045    
046        /**
047         * The Map of all listeners keyed by Observable.
048         */
049        private Map regs = new HashMap();
050    
051        /**
052         * Constructor.
053         */
054        public NotificationRegistry() {
055            super();
056        }
057    
058        /**
059         * Register this SCXMLListener for this Observable.
060         *
061         * @param source The observable this listener wants to listen to
062         * @param lst The listener
063         */
064        synchronized void addListener(final Object source,
065                final SCXMLListener lst) {
066            Set entries = (Set) regs.get(source);
067            if (entries == null) {
068                entries = new LinkedHashSet();
069                regs.put(source, entries);
070            }
071            entries.add(lst);
072        }
073    
074        /**
075         * Deregister this SCXMLListener for this Observable.
076         *
077         * @param source The observable this listener wants to stop listening to
078         * @param lst The listener
079         */
080        synchronized void removeListener(final Object source,
081                final SCXMLListener lst) {
082            Set entries = (Set) regs.get(source);
083            if (entries != null) {
084                entries.remove(lst);
085                if (entries.size() == 0) {
086                    regs.remove(source);
087                }
088            }
089        }
090    
091        /**
092         * Inform all relevant listeners that a TransitionTarget has been
093         * entered.
094         *
095         * @param observable The Observable
096         * @param state The TransitionTarget that was entered
097         */
098        public void fireOnEntry(final TransitionTarget observable,
099                final TransitionTarget state) {
100            Object source = observable;
101            fireOnEntry(source, state);
102        }
103    
104        /**
105         * Inform all relevant listeners that a TransitionTarget has been
106         * entered.
107         *
108         * @param observable The Observable
109         * @param state The TransitionTarget that was entered
110         */
111        public void fireOnEntry(final SCXML observable,
112                final TransitionTarget state) {
113            Object source = observable;
114            fireOnEntry(source, state);
115        }
116    
117        /**
118         * Inform all relevant listeners that a TransitionTarget has been
119         * entered.
120         *
121         * @param source The Observable
122         * @param state The TransitionTarget that was entered
123         */
124        private synchronized void fireOnEntry(final Object source,
125                final TransitionTarget state) {
126            Set entries = (Set) regs.get(source);
127            if (entries != null) {
128                for (Iterator iter = entries.iterator(); iter.hasNext();) {
129                    SCXMLListener lst = (SCXMLListener) iter.next();
130                    lst.onEntry(state);
131                }
132            }
133        }
134    
135        /**
136         * Inform all relevant listeners that a TransitionTarget has been
137         * exited.
138         *
139         * @param observable The Observable
140         * @param state The TransitionTarget that was exited
141         */
142        public void fireOnExit(final TransitionTarget observable,
143                final TransitionTarget state) {
144            Object source = observable;
145            fireOnExit(source, state);
146        }
147    
148        /**
149         * Inform all relevant listeners that a TransitionTarget has been
150         * exited.
151         *
152         * @param observable The Observable
153         * @param state The TransitionTarget that was exited
154         */
155        public void fireOnExit(final SCXML observable,
156                final TransitionTarget state) {
157            Object source = observable;
158            fireOnExit(source, state);
159        }
160    
161        /**
162         * Inform all relevant listeners that a TransitionTarget has been
163         * exited.
164         *
165         * @param source The Observable
166         * @param state The TransitionTarget that was exited
167         */
168        private synchronized void fireOnExit(final Object source,
169                final TransitionTarget state) {
170            Set entries = (Set) regs.get(source);
171            if (entries != null) {
172                for (Iterator iter = entries.iterator(); iter.hasNext();) {
173                    SCXMLListener lst = (SCXMLListener) iter.next();
174                    lst.onExit(state);
175                }
176            }
177        }
178    
179        /**
180         * Inform all relevant listeners of a transition that has occured.
181         *
182         * @param observable The Observable
183         * @param from The source TransitionTarget
184         * @param to The destination TransitionTarget
185         * @param transition The Transition that was taken
186         */
187        public void fireOnTransition(final Transition observable,
188                final TransitionTarget from, final TransitionTarget to,
189                final Transition transition) {
190            Object source = observable;
191            fireOnTransition(source, from, to, transition);
192        }
193    
194        /**
195         * Inform all relevant listeners of a transition that has occured.
196         *
197         * @param observable The Observable
198         * @param from The source TransitionTarget
199         * @param to The destination TransitionTarget
200         * @param transition The Transition that was taken
201         */
202        public void fireOnTransition(final SCXML observable,
203                final TransitionTarget from, final TransitionTarget to,
204                final Transition transition) {
205            Object source = observable;
206            fireOnTransition(source, from, to, transition);
207        }
208    
209        /**
210         * Inform all relevant listeners of a transition that has occured.
211         *
212         * @param source The Observable
213         * @param from The source TransitionTarget
214         * @param to The destination TransitionTarget
215         * @param transition The Transition that was taken
216         */
217        private synchronized void fireOnTransition(final Object source,
218                final TransitionTarget from, final TransitionTarget to,
219                final Transition transition) {
220            Set entries = (Set) regs.get(source);
221            if (entries != null) {
222                for (Iterator iter = entries.iterator(); iter.hasNext();) {
223                    SCXMLListener lst = (SCXMLListener) iter.next();
224                    lst.onTransition(from, to, transition);
225                }
226            }
227        }
228    
229    }
230