Coverage Report - org.argouml.application.events.ArgoEventPump
 
Classes in this File Line Coverage Branch Coverage Complexity
ArgoEventPump
46%
63/136
42%
41/97
3.296
ArgoEventPump$1
0%
0/3
N/A
3.296
ArgoEventPump$2
0%
0/3
N/A
3.296
ArgoEventPump$Pair
37%
6/16
0%
0/8
3.296
 
 1  
 /* $Id: ArgoEventPump.java 17749 2010-01-11 18:49:17Z linus $
 2  
  *******************************************************************************
 3  
  * Copyright (c) 2009 Contributors - see below
 4  
  * All rights reserved. This program and the accompanying materials
 5  
  * are made available under the terms of the Eclipse Public License v1.0
 6  
  * which accompanies this distribution, and is available at
 7  
  * http://www.eclipse.org/legal/epl-v10.html
 8  
  *
 9  
  * Contributors:
 10  
  *    tfmorris
 11  
  *******************************************************************************
 12  
  *
 13  
  * Some portions of this file was previously release using the BSD License:
 14  
  */
 15  
 
 16  
 // Copyright (c) 1996-2008 The Regents of the University of California. All
 17  
 // Rights Reserved. Permission to use, copy, modify, and distribute this
 18  
 // software and its documentation without fee, and without a written
 19  
 // agreement is hereby granted, provided that the above copyright notice
 20  
 // and this paragraph appear in all copies.  This software program and
 21  
 // documentation are copyrighted by The Regents of the University of
 22  
 // California. The software program and documentation are supplied "AS
 23  
 // IS", without any accompanying services from The Regents. The Regents
 24  
 // does not warrant that the operation of the program will be
 25  
 // uninterrupted or error-free. The end-user understands that the program
 26  
 // was developed for research purposes and is advised not to rely
 27  
 // exclusively on the program for any reason.  IN NO EVENT SHALL THE
 28  
 // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
 29  
 // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
 30  
 // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
 31  
 // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
 32  
 // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
 33  
 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 34  
 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
 35  
 // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
 36  
 // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
 37  
 // UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 38  
 
 39  
 package org.argouml.application.events;
 40  
 
 41  
 import java.util.ArrayList;
 42  
 import java.util.List;
 43  
 
 44  
 import javax.swing.SwingUtilities;
 45  
 
 46  
 import org.apache.log4j.Logger;
 47  
 import org.argouml.application.api.ArgoEventListener;
 48  
 
 49  
 /**
 50  
  * ArgoEventPump is an event dispatcher which handles events that are global
 51  
  * in nature for the entire application.
 52  
  * <p>
 53  
  * TODO: DiagramAppearance and Notation events are not application-wide and will
 54  
  * be moved from here to someplace more specific in the future so that they can
 55  
  * be managed on a per-project or per-diagram basis.
 56  
  */
 57  0
 public final class ArgoEventPump {
 58  
     /**
 59  
      * Logger.
 60  
      */
 61  900
     private static final Logger LOG = Logger.getLogger(ArgoEventPump.class);
 62  
 
 63  
     /**
 64  
      * <code>listeners</code> contains the list of register listeners.
 65  
      *
 66  
      * It is a list of {@link Pair}.
 67  
      */
 68  
     private List<Pair> listeners;
 69  
 
 70  
     /**
 71  
      * The singleton.
 72  
      */
 73  900
     static final ArgoEventPump SINGLETON = new ArgoEventPump();
 74  
 
 75  
     /**
 76  
      * @return the singleton
 77  
      */
 78  
     public static ArgoEventPump getInstance() {
 79  0
         return SINGLETON;
 80  
     }
 81  
 
 82  
     /**
 83  
      * Constructor.
 84  
      */
 85  900
     private ArgoEventPump() {
 86  900
     }
 87  
 
 88  
     /**
 89  
      * @param listener The listener to be added.
 90  
      */
 91  
     public static void addListener(ArgoEventListener listener) {
 92  0
         SINGLETON.doAddListener(ArgoEventTypes.ANY_EVENT, listener);
 93  0
     }
 94  
 
 95  
     /**
 96  
      * @param event the event-type to what the listener will listen
 97  
      * @param listener the listener to be added
 98  
      */
 99  
     public static void addListener(int event, ArgoEventListener listener) {
 100  7951
         SINGLETON.doAddListener(event, listener);
 101  7951
     }
 102  
 
 103  
     /**
 104  
      * @param listener the listener to be removed
 105  
      */
 106  
     public static void removeListener(ArgoEventListener listener) {
 107  0
         SINGLETON.doRemoveListener(ArgoEventTypes.ANY_EVENT, listener);
 108  0
     }
 109  
 
 110  
     /**
 111  
      * @param event the event to which the listener will not listen any more
 112  
      * @param listener the listener to be removed
 113  
      */
 114  
     public static void removeListener(int event, ArgoEventListener listener) {
 115  0
         SINGLETON.doRemoveListener(event, listener);
 116  0
     }
 117  
 
 118  
     /**
 119  
      * @param event the event to what the listener will listen (?)
 120  
      * @param listener the listener to be added
 121  
      */
 122  
     protected void doAddListener(int event, ArgoEventListener listener) {
 123  7951
         if (listeners == null) {
 124  900
             listeners = new ArrayList<Pair>();
 125  
         }
 126  7951
         synchronized (listeners) {
 127  7951
             listeners.add(new Pair(event, listener));
 128  7951
         }
 129  7951
     }
 130  
 
 131  
     /**
 132  
      * Removes a listener, eventtype pair from the listener list.
 133  
      *
 134  
      * TODO: replace the listener implementation with a EventListenerList
 135  
      * for better performance
 136  
      *
 137  
      * @param event the event to which the listener will not listen any more
 138  
      * @param listener the listener to be removed
 139  
      */
 140  
     protected void doRemoveListener(int event, ArgoEventListener listener) {
 141  0
         if (listeners == null) {
 142  0
             return;
 143  
         }
 144  0
         synchronized (listeners) {
 145  0
             List<Pair> removeList = new ArrayList<Pair>();
 146  0
             if (event == ArgoEventTypes.ANY_EVENT) {
 147  
                 // TODO: This is a linear search of a list that contain many
 148  
                 // thousands of items (one for every Fig in the entire project)
 149  0
                 for (Pair p : listeners) {
 150  0
                     if (p.listener == listener) {
 151  0
                         removeList.add(p);
 152  
                     }
 153  
                 }
 154  
             } else {
 155  0
                 Pair test = new Pair(event, listener);
 156  
                 // TODO: This is a linear search of a list that contain many
 157  
                 // thousands of items (one for every Fig in the entire project)
 158  0
                 for (Pair p : listeners) {
 159  0
                     if (p.equals(test)) {
 160  0
                         removeList.add(p);
 161  
                     }
 162  
                 }
 163  
             }
 164  0
             listeners.removeAll(removeList);
 165  0
         }
 166  0
     }
 167  
 
 168  
     /**
 169  
      * Handle firing a notation event.
 170  
      * <p>
 171  
      * TODO: This needs to be managed on a per-diagram or per-project basis.
 172  
      *
 173  
      * @param event The event to be fired.
 174  
      * @param listener The listener.
 175  
      */
 176  
     private void handleFireNotationEvent(
 177  
         final ArgoNotationEvent event,
 178  
         final ArgoNotationEventListener listener) {
 179  
        
 180  
         // Notation events are likely to cause GEF/Swing operations, so we
 181  
         // dispatch them on the Swing event thread as a convenience so that 
 182  
         // the receiving notationChanged() methods don't need to deal with it
 183  2
         if (SwingUtilities.isEventDispatchThread()) {
 184  2
             fireNotationEventInternal(event, listener);
 185  
         } else {
 186  0
             SwingUtilities.invokeLater(new Runnable() {
 187  
                 public void run() {
 188  0
                     fireNotationEventInternal(event, listener);
 189  0
                 }
 190  
             });
 191  
         }
 192  2
     }
 193  
 
 194  
     private void fireNotationEventInternal(ArgoNotationEvent event,
 195  
             ArgoNotationEventListener listener) {
 196  2
         switch (event.getEventType()) {
 197  
         case ArgoEventTypes.NOTATION_CHANGED :
 198  2
             listener.notationChanged(event);
 199  
             /* Remark: The code in 
 200  
              * ProjectSettings.init() currently presumes
 201  
              * that nobody is using this event. */
 202  2
             break;
 203  
 
 204  
         case ArgoEventTypes.NOTATION_ADDED :
 205  0
             listener.notationAdded(event);
 206  0
             break;
 207  
 
 208  
         case ArgoEventTypes.NOTATION_REMOVED :
 209  0
             listener.notationRemoved(event);
 210  0
             break;
 211  
 
 212  
         case ArgoEventTypes.NOTATION_PROVIDER_ADDED :
 213  0
             listener.notationProviderAdded(event);
 214  0
             break;
 215  
 
 216  
         case ArgoEventTypes.NOTATION_PROVIDER_REMOVED :
 217  0
             listener.notationProviderRemoved(event);
 218  0
             break;
 219  
 
 220  
         default :
 221  0
             LOG.error("Invalid event:" + event.getEventType());
 222  
             break;
 223  
         }
 224  2
     }
 225  
 
 226  
     /**
 227  
      * Handle firing a diagram appearance event.
 228  
      * <p>
 229  
      * TODO: This needs to be managed on a per-diagram or per-project basis.
 230  
      * 
 231  
      * @param event The event to be fired.
 232  
      * @param listener The listener.
 233  
      */
 234  
     private void handleFireDiagramAppearanceEvent(
 235  
         final ArgoDiagramAppearanceEvent event,
 236  
         final ArgoDiagramAppearanceEventListener listener) {
 237  24
         if (SwingUtilities.isEventDispatchThread()) {
 238  24
             fireDiagramAppearanceEventInternal(event, listener);
 239  
         } else {
 240  0
             SwingUtilities.invokeLater(new Runnable() {
 241  
                 public void run() {
 242  0
                     fireDiagramAppearanceEventInternal(event, listener);
 243  0
                 }
 244  
             });
 245  
         }        
 246  24
     }
 247  
 
 248  
     private void fireDiagramAppearanceEventInternal(
 249  
             final ArgoDiagramAppearanceEvent event,
 250  
             final ArgoDiagramAppearanceEventListener listener) {
 251  24
         switch (event.getEventType()) {
 252  
         case ArgoEventTypes.DIAGRAM_FONT_CHANGED :
 253  24
             listener.diagramFontChanged(event);
 254  24
             break;
 255  
         default :
 256  0
             LOG.error("Invalid event:" + event.getEventType());
 257  
             break;
 258  
         }
 259  24
     }
 260  
 
 261  
     /**
 262  
      * Handle firing a help text event.
 263  
      *
 264  
      * @param event The event to be fired.
 265  
      * @param listener The listener.
 266  
      */
 267  
     private void handleFireHelpEvent(
 268  
         ArgoHelpEvent event,
 269  
         ArgoHelpEventListener listener) {
 270  0
         switch (event.getEventType()) {
 271  
         case ArgoEventTypes.HELP_CHANGED :
 272  0
             listener.helpChanged(event);
 273  0
             break;
 274  
 
 275  
         case ArgoEventTypes.HELP_REMOVED :
 276  0
             listener.helpRemoved(event);
 277  0
             break;
 278  
 
 279  
         default :
 280  0
             LOG.error("Invalid event:" + event.getEventType());
 281  
             break;
 282  
         }
 283  0
     }
 284  
 
 285  
 
 286  
     /**
 287  
      * Handle firing a status text event.
 288  
      *
 289  
      * @param event The event to be fired.
 290  
      * @param listener The listener.
 291  
      */
 292  
     private void handleFireStatusEvent(
 293  
         ArgoStatusEvent event,
 294  
         ArgoStatusEventListener listener) {
 295  1920
         switch (event.getEventType()) {
 296  
         case ArgoEventTypes.STATUS_TEXT :
 297  52
             listener.statusText(event);
 298  52
             break;
 299  
 
 300  
         case ArgoEventTypes.STATUS_CLEARED :
 301  0
             listener.statusCleared(event);
 302  0
             break;
 303  
 
 304  
         case ArgoEventTypes.STATUS_PROJECT_SAVED :
 305  0
             listener.projectSaved(event);
 306  0
             break;
 307  
 
 308  
         case ArgoEventTypes.STATUS_PROJECT_LOADED :
 309  1868
             listener.projectLoaded(event);
 310  1868
             break;
 311  
 
 312  
         case ArgoEventTypes.STATUS_PROJECT_MODIFIED :
 313  0
             listener.projectModified(event);
 314  0
             break;
 315  
             
 316  
         default :
 317  0
             LOG.error("Invalid event:" + event.getEventType());
 318  
             break;
 319  
         }
 320  1920
     }
 321  
 
 322  
     /**
 323  
      * Handle firing a profile event.
 324  
      *
 325  
      * @param event The event to be fired.
 326  
      * @param listener The listener.
 327  
      */
 328  
     private void handleFireProfileEvent(
 329  
         ArgoProfileEvent event,
 330  
         ArgoProfileEventListener listener) {
 331  2862
         switch (event.getEventType()) {
 332  
         case ArgoEventTypes.PROFILE_ADDED:
 333  2862
             listener.profileAdded(event);
 334  2862
             break;
 335  
 
 336  
         case ArgoEventTypes.PROFILE_REMOVED:
 337  0
             listener.profileRemoved(event);
 338  0
             break;
 339  
 
 340  
         default:
 341  0
             LOG.error("Invalid event:" + event.getEventType());
 342  
             break;
 343  
         }
 344  2862
     }
 345  
 
 346  
     /**
 347  
      * Handle firing a generator event.
 348  
      *
 349  
      * @param event The event to be fired.
 350  
      * @param listener The listener.
 351  
      */
 352  
     private void handleFireGeneratorEvent(
 353  
         ArgoGeneratorEvent event,
 354  
         ArgoGeneratorEventListener listener) {
 355  0
         switch (event.getEventType()) {
 356  
         case ArgoEventTypes.GENERATOR_CHANGED:
 357  0
             listener.generatorChanged(event);
 358  0
             break;
 359  
 
 360  
         case ArgoEventTypes.GENERATOR_ADDED:
 361  0
             listener.generatorAdded(event);
 362  0
             break;
 363  
 
 364  
         case ArgoEventTypes.GENERATOR_REMOVED:
 365  0
             listener.generatorRemoved(event);
 366  0
             break;
 367  
 
 368  
         default:
 369  0
             LOG.error("Invalid event:" + event.getEventType());
 370  
             break;
 371  
         }
 372  0
     }
 373  
 
 374  
     private void handleFireEvent(ArgoEvent event, ArgoEventListener listener) {
 375  4808
         if (event.getEventType() == ArgoEventTypes.ANY_EVENT) {
 376  0
             if (listener instanceof ArgoNotationEventListener) {
 377  0
                 handleFireNotationEvent((ArgoNotationEvent) event,
 378  
                                         (ArgoNotationEventListener) listener);
 379  
             }
 380  0
             if (listener instanceof ArgoHelpEventListener) {
 381  0
                 handleFireHelpEvent((ArgoHelpEvent) event,
 382  
                                         (ArgoHelpEventListener) listener);
 383  
             }
 384  0
             if (listener instanceof ArgoStatusEventListener) {
 385  0
                 handleFireStatusEvent((ArgoStatusEvent) event,
 386  
                         (ArgoStatusEventListener) listener);
 387  
             }
 388  
         } else {
 389  4808
             if (event.getEventType() >= ArgoEventTypes.ANY_NOTATION_EVENT
 390  
                 && event.getEventType() < ArgoEventTypes.LAST_NOTATION_EVENT) {
 391  2
                 if (listener instanceof ArgoNotationEventListener) {
 392  2
                     handleFireNotationEvent((ArgoNotationEvent) event,
 393  
                                         (ArgoNotationEventListener) listener);
 394  
                 }
 395  
             }
 396  4808
             if (event.getEventType() >= ArgoEventTypes
 397  
                             .ANY_DIAGRAM_APPEARANCE_EVENT
 398  
                     && event.getEventType() < ArgoEventTypes
 399  
                             .LAST_DIAGRAM_APPEARANCE_EVENT) {
 400  24
                 if (listener instanceof ArgoDiagramAppearanceEventListener) {
 401  24
                     handleFireDiagramAppearanceEvent(
 402  
                             (ArgoDiagramAppearanceEvent) event,
 403  
                             (ArgoDiagramAppearanceEventListener) listener);
 404  
                 }
 405  
             }
 406  4808
             if (event.getEventType() >= ArgoEventTypes.ANY_HELP_EVENT
 407  
                     && event.getEventType() < ArgoEventTypes.LAST_HELP_EVENT) {
 408  0
                 if (listener instanceof ArgoHelpEventListener) {
 409  0
                     handleFireHelpEvent((ArgoHelpEvent) event,
 410  
                             (ArgoHelpEventListener) listener);
 411  
                 }
 412  
             }
 413  4808
             if (event.getEventType() >= ArgoEventTypes.ANY_GENERATOR_EVENT
 414  
                 && event.getEventType() < ArgoEventTypes.LAST_GENERATOR_EVENT) {
 415  0
                 if (listener instanceof ArgoGeneratorEventListener) {
 416  0
                     handleFireGeneratorEvent((ArgoGeneratorEvent) event,
 417  
                             (ArgoGeneratorEventListener) listener);
 418  
                 }
 419  
             }
 420  4808
             if (event.getEventType() >= ArgoEventTypes.ANY_STATUS_EVENT
 421  
                     && event.getEventType() < ArgoEventTypes
 422  
                             .LAST_STATUS_EVENT) {
 423  1920
                 if (listener instanceof ArgoStatusEventListener) {
 424  1920
                     handleFireStatusEvent((ArgoStatusEvent) event,
 425  
                             (ArgoStatusEventListener) listener);
 426  
                 }
 427  
             }
 428  4808
             if (event.getEventType() >= ArgoEventTypes.ANY_PROFILE_EVENT
 429  
                     && event.getEventType() < ArgoEventTypes
 430  
                             .LAST_PROFILE_EVENT) {
 431  2862
                 if (listener instanceof ArgoProfileEventListener) {
 432  2862
                     handleFireProfileEvent((ArgoProfileEvent) event,
 433  
                             (ArgoProfileEventListener) listener);
 434  
                 }
 435  
             }
 436  
         }
 437  4808
     }
 438  
 
 439  
     /**
 440  
      * @param event the event to be fired
 441  
      */
 442  
     public static void fireEvent(ArgoEvent event) {
 443  8395
         SINGLETON.doFireEvent(event);
 444  8395
     }
 445  
 
 446  
     /**
 447  
      * @param event the event to be fired
 448  
      */
 449  
     protected void doFireEvent(ArgoEvent event) {
 450  8395
         if (listeners == null) {
 451  0
             return;
 452  
         }
 453  
 
 454  
         // Make a read-only copy of the listeners list so that reentrant calls
 455  
         // back to add/removeListener won't mess us up.
 456  
         // TODO: Potential performance issue, but we need the correctness - tfm
 457  
         List<Pair> readOnlyListeners;
 458  8395
         synchronized (listeners) {
 459  8395
             readOnlyListeners = new ArrayList<Pair>(listeners);
 460  8395
         }
 461  
 
 462  8395
         for (Pair pair : readOnlyListeners) {
 463  39516
             if (pair.getEventType() == ArgoEventTypes.ANY_EVENT) {
 464  0
                 handleFireEvent(event, pair.getListener());
 465  39516
             } else if (pair.getEventType() == event.getEventStartRange()
 466  
                     || pair.getEventType() == event.getEventType()) {
 467  4808
                 handleFireEvent(event, pair.getListener());
 468  
             }
 469  
         }
 470  
 
 471  8395
     }
 472  
 
 473  
     /**
 474  
      * Data structure handling listener registrations.
 475  
      */
 476  0
     static class Pair {
 477  
         private int eventType;
 478  
         private ArgoEventListener listener;
 479  
 
 480  
         /**
 481  
          * Constructor.
 482  
          *
 483  
          * @param myEventType The event type.
 484  
          * @param myListener The listener.
 485  
          */
 486  7951
         Pair(int myEventType, ArgoEventListener myListener) {
 487  7951
             eventType = myEventType;
 488  7951
             listener = myListener;
 489  7951
         }
 490  
 
 491  
         /**
 492  
          * @return The event type.
 493  
          */
 494  
         int getEventType() {
 495  113740
             return eventType;
 496  
         }
 497  
 
 498  
         /**
 499  
          * @return The listener.
 500  
          */
 501  
         ArgoEventListener getListener() {
 502  4808
             return listener;
 503  
         }
 504  
 
 505  
 
 506  
         @Override
 507  
         public String toString() {
 508  0
             return "{Pair(" + eventType + "," + listener + ")}";
 509  
         }
 510  
 
 511  
 
 512  
         @Override
 513  
         public int hashCode() {
 514  0
             if (listener != null) {
 515  0
                 return eventType + listener.hashCode();
 516  
             }
 517  0
             return eventType;
 518  
         }
 519  
 
 520  
 
 521  
         @Override
 522  
         public boolean equals(Object o) {
 523  0
             if (o instanceof Pair) {
 524  0
                 Pair p = (Pair) o;
 525  0
                 if (p.eventType == eventType && p.listener == listener) {
 526  0
                     return true;
 527  
                 }
 528  
             }
 529  0
             return false;
 530  
         }
 531  
     }
 532  
 }