/* * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.nashorn.internal.runtime; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; /** * Helper class to manage property listeners and notification. */ public class PropertyListeners { private Map listeners; // These counters are updated in debug mode private static int listenersAdded; private static int listenersRemoved; /** * Copy constructor * @param listener listener to copy */ PropertyListeners(final PropertyListeners listener) { if (listener != null && listener.listeners != null) { this.listeners = new WeakHashMap<>(listener.listeners); } } /** * Return aggregate listeners added to all PropertyListenerManagers * @return the listenersAdded */ public static int getListenersAdded() { return listenersAdded; } /** * Return aggregate listeners removed from all PropertyListenerManagers * @return the listenersRemoved */ public static int getListenersRemoved() { return listenersRemoved; } /** * Return listeners added to this ScriptObject. * @param obj the object * @return the listener count */ public static int getListenerCount(final ScriptObject obj) { final PropertyListeners propertyListeners = obj.getMap().getListeners(); if (propertyListeners != null) { return propertyListeners.listeners == null ? 0 : propertyListeners.listeners.size(); } return 0; } // Property listener management methods /** * Add {@code propertyMap} as property listener to {@code listeners} using key {@code key} by * creating and returning a new {@code PropertyListeners} instance. * * @param listeners the original property listeners instance, may be null * @param key the property key * @param propertyMap the property map * @return the new property map */ public static PropertyListeners addListener(final PropertyListeners listeners, final String key, final PropertyMap propertyMap) { final PropertyListeners newListeners; if (listeners == null || !listeners.containsListener(key, propertyMap)) { newListeners = new PropertyListeners(listeners); newListeners.addListener(key, propertyMap); return newListeners; } return listeners; } /** * Checks whether {@code propertyMap} is registered as listener with {@code key}. * * @param key the property key * @param propertyMap the property map * @return true if property map is registered with property key */ synchronized boolean containsListener(final String key, final PropertyMap propertyMap) { if (listeners == null) { return false; } WeakPropertyMapSet set = listeners.get(key); return set != null && set.contains(propertyMap); } /** * Add a property listener to this object. * * @param propertyMap The property listener that is added. */ synchronized final void addListener(final String key, final PropertyMap propertyMap) { if (Context.DEBUG) { listenersAdded++; } if (listeners == null) { listeners = new WeakHashMap<>(); } WeakPropertyMapSet set = listeners.get(key); if (set == null) { set = new WeakPropertyMapSet(); listeners.put(key, set); } if (!set.contains(propertyMap)) { set.add(propertyMap); } } /** * A new property is being added. * * @param prop The new Property added. */ public synchronized void propertyAdded(final Property prop) { if (listeners != null) { WeakPropertyMapSet set = listeners.get(prop.getKey()); if (set != null) { for (PropertyMap propertyMap : set.elements()) { propertyMap.propertyAdded(prop); } listeners.remove(prop.getKey()); } } } /** * An existing property is being deleted. * * @param prop The property being deleted. */ public synchronized void propertyDeleted(final Property prop) { if (listeners != null) { WeakPropertyMapSet set = listeners.get(prop.getKey()); if (set != null) { for (PropertyMap propertyMap : set.elements()) { propertyMap.propertyDeleted(prop); } listeners.remove(prop.getKey()); } } } /** * An existing Property is being replaced with a new Property. * * @param oldProp The old property that is being replaced. * @param newProp The new property that replaces the old property. * */ public synchronized void propertyModified(final Property oldProp, final Property newProp) { if (listeners != null) { WeakPropertyMapSet set = listeners.get(oldProp.getKey()); if (set != null) { for (PropertyMap propertyMap : set.elements()) { propertyMap.propertyModified(oldProp, newProp); } listeners.remove(oldProp.getKey()); } } } public synchronized void protoChanged() { if (listeners != null) { for (WeakPropertyMapSet set : listeners.values()) { for (PropertyMap propertyMap : set.elements()) { propertyMap.protoChanged(); } } listeners.clear(); } } private static class WeakPropertyMapSet { private WeakHashMap map = new WeakHashMap<>(); void add(final PropertyMap propertyMap) { map.put(propertyMap, Boolean.TRUE); } boolean contains(final PropertyMap propertyMap) { return map.containsKey(propertyMap); } Set elements() { return map.keySet(); } } }