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.Set; 30 import java.util.WeakHashMap; 31 32 /** 33 * Helper class to manage property listeners and notification. 34 */ 35 public class PropertyListeners { 36 37 private Map<String, WeakPropertyMapSet> listeners; 38 39 // These counters are updated in debug mode 40 private static int listenersAdded; 41 private static int listenersRemoved; 42 43 /** 44 * Copy constructor 45 * @param listener listener to copy 46 */ 47 PropertyListeners(final PropertyListeners listener) { 48 if (listener != null && listener.listeners != null) { 49 this.listeners = new WeakHashMap<>(listener.listeners); 50 } 51 } 52 53 /** 54 * Return aggregate listeners added to all PropertyListenerManagers 55 * @return the listenersAdded 56 */ 57 public static int getListenersAdded() { 58 return listenersAdded; 59 } 60 61 /** 62 * Return aggregate listeners removed from all PropertyListenerManagers 63 * @return the listenersRemoved 64 */ 65 public static int getListenersRemoved() { 66 return listenersRemoved; 67 } 68 69 /** 70 * Return listeners added to this ScriptObject. 71 * @param obj the object 72 * @return the listener count 73 */ 74 public static int getListenerCount(final ScriptObject obj) { 75 final PropertyListeners propertyListeners = obj.getMap().getListeners(); 76 if (propertyListeners != null) { 77 return propertyListeners.listeners == null ? 0 : propertyListeners.listeners.size(); 78 } 79 return 0; 80 } 81 82 // Property listener management methods 83 84 /** 85 * Add {@code propertyMap} as property listener to {@code listeners} using key {@code key} by 86 * creating and returning a new {@code PropertyListeners} instance. 87 * 88 * @param listeners the original property listeners instance, may be null 89 * @param key the property key 90 * @param propertyMap the property map 91 * @return the new property map 92 */ 93 public static PropertyListeners addListener(final PropertyListeners listeners, final String key, final PropertyMap propertyMap) { 94 final PropertyListeners newListeners; 95 if (listeners == null || !listeners.containsListener(key, propertyMap)) { 96 newListeners = new PropertyListeners(listeners); 97 newListeners.addListener(key, propertyMap); 98 return newListeners; 99 } 100 return listeners; 101 } 102 103 /** 104 * Checks whether {@code propertyMap} is registered as listener with {@code key}. 105 * 106 * @param key the property key 107 * @param propertyMap the property map 108 * @return true if property map is registered with property key 109 */ 110 synchronized boolean containsListener(final String key, final PropertyMap propertyMap) { 111 if (listeners == null) { 112 return false; 113 } 114 WeakPropertyMapSet set = listeners.get(key); 115 return set != null && set.contains(propertyMap); 116 } 117 118 /** 119 * Add a property listener to this object. 120 * 121 * @param propertyMap The property listener that is added. 122 */ 123 synchronized final void addListener(final String key, final PropertyMap propertyMap) { 124 if (Context.DEBUG) { 125 listenersAdded++; 126 } 127 if (listeners == null) { 128 listeners = new WeakHashMap<>(); 129 } 130 131 WeakPropertyMapSet set = listeners.get(key); 132 if (set == null) { 133 set = new WeakPropertyMapSet(); 134 listeners.put(key, set); 135 } 136 if (!set.contains(propertyMap)) { 137 set.add(propertyMap); 138 } 139 } 140 141 /** 142 * A new property is being added. 143 * 144 * @param prop The new Property added. 145 */ 146 public synchronized void propertyAdded(final Property prop) { 147 if (listeners != null) { 148 WeakPropertyMapSet set = listeners.get(prop.getKey()); 149 if (set != null) { 150 for (PropertyMap propertyMap : set.elements()) { 151 propertyMap.propertyAdded(prop); 152 } 153 listeners.remove(prop.getKey()); 154 } 155 } 156 } 157 158 /** 159 * An existing property is being deleted. 160 * 161 * @param prop The property being deleted. 162 */ 163 public synchronized void propertyDeleted(final Property prop) { 164 if (listeners != null) { 165 WeakPropertyMapSet set = listeners.get(prop.getKey()); 166 if (set != null) { 167 for (PropertyMap propertyMap : set.elements()) { 168 propertyMap.propertyDeleted(prop); 169 } 170 listeners.remove(prop.getKey()); 171 } 172 } 173 } 174 175 /** 176 * An existing Property is being replaced with a new Property. 177 * 178 * @param oldProp The old property that is being replaced. 179 * @param newProp The new property that replaces the old property. 180 * 181 */ 182 public synchronized void propertyModified(final Property oldProp, final Property newProp) { 183 if (listeners != null) { 184 WeakPropertyMapSet set = listeners.get(oldProp.getKey()); 185 if (set != null) { 186 for (PropertyMap propertyMap : set.elements()) { 187 propertyMap.propertyModified(oldProp, newProp); 188 } 189 listeners.remove(oldProp.getKey()); 190 } 191 } 192 } 193 194 public synchronized void protoChanged() { 195 if (listeners != null) { 196 for (WeakPropertyMapSet set : listeners.values()) { 197 for (PropertyMap propertyMap : set.elements()) { 198 propertyMap.protoChanged(); 199 } 200 } 201 listeners.clear(); 202 } 203 } 204 205 private static class WeakPropertyMapSet { 206 207 private WeakHashMap<PropertyMap, Boolean> map = new WeakHashMap<>(); 208 209 void add(final PropertyMap propertyMap) { 210 map.put(propertyMap, Boolean.TRUE); 211 } 212 213 boolean contains(final PropertyMap propertyMap) { 214 return map.containsKey(propertyMap); 215 } 216 217 Set<PropertyMap> elements() { 218 return map.keySet(); 219 } 220 221 } 222 }