1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.runtime;
  27 
  28 import java.util.Map;
  29 import java.util.WeakHashMap;
  30 
  31 /**
  32  * Helper class to manage property listeners and notification.
  33  */
  34 public class PropertyListenerManager implements PropertyListener {
  35     PropertyListenerManager() {}
  36 
  37     /** property listeners for this object. */
  38     private Map<PropertyListener,Boolean> listeners;
  39 
  40     // These counters are updated in debug mode
  41     private static int listenersAdded;
  42     private static int listenersRemoved;
  43 
  44     /**
  45      * Return aggregate listeners added to all PropertyListenerManagers
  46      * @return the listenersAdded
  47      */
  48     public static int getListenersAdded() {
  49         return listenersAdded;
  50     }
  51 
  52     /**
  53      * Return aggregate listeners removed from all PropertyListenerManagers
  54      * @return the listenersRemoved
  55      */
  56     public static int getListenersRemoved() {
  57         return listenersRemoved;
  58     }
  59 
  60     /**
  61      * Return listeners added to this PropertyListenerManager.
  62      * @return the listener count
  63      */
  64     public final int getListenerCount() {
  65         return listeners != null? listeners.size() : 0;
  66     }
  67 
  68     // Property listener management methods
  69 
  70     /**
  71      * Add a property listener to this object.
  72      *
  73      * @param listener The property listener that is added.
  74      */
  75     public synchronized final void addPropertyListener(final PropertyListener listener) {
  76         if (listeners == null) {
  77             listeners = new WeakHashMap<>();
  78         }
  79 
  80         if (Context.DEBUG) {
  81             listenersAdded++;
  82         }
  83         listeners.put(listener, Boolean.TRUE);
  84     }
  85 
  86     /**
  87      * Remove a property listener from this object.
  88      *
  89      * @param listener The property listener that is removed.
  90      */
  91     public synchronized final void removePropertyListener(final PropertyListener listener) {
  92         if (listeners != null) {
  93             if (Context.DEBUG) {
  94                 listenersRemoved++;
  95             }
  96             listeners.remove(listener);
  97         }
  98     }
  99 
 100     /**
 101      * This method can be called to notify property addition to this object's listeners.
 102      *
 103      * @param object The ScriptObject to which property was added.
 104      * @param prop The property being added.
 105      */
 106     protected synchronized final void notifyPropertyAdded(final ScriptObject object, final Property prop) {
 107         if (listeners != null) {
 108             for (PropertyListener listener : listeners.keySet()) {
 109                 listener.propertyAdded(object, prop);
 110             }
 111         }
 112     }
 113 
 114     /**
 115      * This method can be called to notify property deletion to this object's listeners.
 116      *
 117      * @param object The ScriptObject from which property was deleted.
 118      * @param prop The property being deleted.
 119      */
 120     protected synchronized final void notifyPropertyDeleted(final ScriptObject object, final Property prop) {
 121         if (listeners != null) {
 122             for (PropertyListener listener : listeners.keySet()) {
 123                 listener.propertyDeleted(object, prop);
 124             }
 125         }
 126     }
 127 
 128     /**
 129      * This method can be called to notify property modification to this object's listeners.
 130      *
 131      * @param object The ScriptObject to which property was modified.
 132      * @param oldProp The old property being replaced.
 133      * @param newProp The new property that replaces the old property.
 134      */
 135     protected synchronized final void notifyPropertyModified(final ScriptObject object, final Property oldProp, final Property newProp) {
 136         if (listeners != null) {
 137             for (PropertyListener listener : listeners.keySet()) {
 138                 listener.propertyModified(object, oldProp, newProp);
 139             }
 140         }
 141     }
 142 
 143     /**
 144      * This method can be called to notify __proto__ modification to this object's listeners.
 145      *
 146      * @param object The ScriptObject whose __proto__ was changed.
 147      * @param oldProto old __proto__
 148      * @param newProto new __proto__
 149      */
 150     protected synchronized final void notifyProtoChanged(final ScriptObject object, final ScriptObject oldProto, final ScriptObject newProto) {
 151         if (listeners != null) {
 152             for (PropertyListener listener : listeners.keySet()) {
 153                 listener.protoChanged(object, oldProto, newProto);
 154             }
 155         }
 156     }
 157 
 158     // PropertyListener methods
 159 
 160     @Override
 161     public final void propertyAdded(final ScriptObject object, final Property prop) {
 162         notifyPropertyAdded(object, prop);
 163     }
 164 
 165     @Override
 166     public final void propertyDeleted(final ScriptObject object, final Property prop) {
 167         notifyPropertyDeleted(object, prop);
 168     }
 169 
 170     @Override
 171     public final void propertyModified(final ScriptObject object, final Property oldProp, final Property newProp) {
 172         notifyPropertyModified(object, oldProp, newProp);
 173     }
 174 
 175     @Override
 176     public final void protoChanged(final ScriptObject object, final ScriptObject oldProto, final ScriptObject newProto) {
 177         notifyProtoChanged(object, oldProto, newProto);
 178     }
 179 }