--- /dev/null 2015-03-24 15:44:44.000000000 -0500 +++ new/jdk/src/jdk.accessibility/share/classes/com/sun/java/accessibility/util/AccessibilityEventMonitor.java 2015-03-24 15:44:43.101931900 -0500 @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2002, 2015, 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 com.sun.java.accessibility.util; + +import java.util.*; +import java.beans.*; +import java.awt.*; +import java.awt.event.*; +import javax.accessibility.*; + +/** + *

{@code AccessibilityEventMonitor} implements a PropertyChange listener + * on every UI object that implements interface {@code Accessible} in the Java + * Virtual Machine. The events captured by these listeners are made available + * through listeners supported by {@code AccessibilityEventMonitor}. + * With this, all the individual events on each of the UI object + * instances are funneled into one set of PropertyChange listeners. + *

This class depends upon {@link EventQueueMonitor}, which provides the base + * level support for capturing the top-level containers as they are created. + * + */ + +@jdk.Exported +public class AccessibilityEventMonitor { + + // listeners + /** + * The current list of registered {@link java.beans.PropertyChangeListener + * PropertyChangeListener} classes. + * + * @see #addPropertyChangeListener + * @see #removePropertyChangeListener + */ + static protected final AccessibilityListenerList listenerList = + new AccessibilityListenerList(); + + + /** + * The actual listener that is installed on the component instances. + * This listener calls the other registered listeners when an event + * occurs. By doing things this way, the actual number of listeners + * installed on a component instance is drastically reduced. + */ + static protected final AccessibilityEventListener accessibilityListener = + new AccessibilityEventListener(); + + /** + * Adds the specified listener to receive all PropertyChange events on + * each UI object instance in the Java Virtual Machine as they occur. + *

Note: This listener is automatically added to all component + * instances created after this method is called. In addition, it + * is only added to UI object instances that support this listener type. + * + * @param l the listener to add + * + * @see #removePropertyChangeListener + */ + static public void addPropertyChangeListener(PropertyChangeListener l) { + if (listenerList.getListenerCount(PropertyChangeListener.class) == 0) { + accessibilityListener.installListeners(); + } + listenerList.add(PropertyChangeListener.class, l); + } + + /** + * Removes the specified listener so it no longer receives PropertyChange + * events when they occur. + * @see #addPropertyChangeListener + * @param l the listener to remove + */ + static public void removePropertyChangeListener(PropertyChangeListener l) { + listenerList.remove(PropertyChangeListener.class, l); + if (listenerList.getListenerCount(PropertyChangeListener.class) == 0) { + accessibilityListener.removeListeners(); + } + } + + + /** + * AccessibilityEventListener is the class that does all the work for + * AccessibilityEventMonitor. It is not intended for use by any other + * class except AccessibilityEventMonitor. + * + */ + + static class AccessibilityEventListener implements TopLevelWindowListener, + PropertyChangeListener { + + /** + * Create a new instance of this class and install it on each component + * instance in the virtual machine that supports any of the currently + * registered listeners in AccessibilityEventMonitor. Also registers + * itself as a TopLevelWindowListener with EventQueueMonitor so it can + * automatically add new listeners to new components. + * @see EventQueueMonitor + * @see AccessibilityEventMonitor + */ + public AccessibilityEventListener() { + EventQueueMonitor.addTopLevelWindowListener(this); + } + + /** + * Installs PropertyChange listeners on all Accessible objects based + * upon the current topLevelWindows cached by EventQueueMonitor. + * @see EventQueueMonitor + * @see AWTEventMonitor + */ + protected void installListeners() { + Window topLevelWindows[] = EventQueueMonitor.getTopLevelWindows(); + if (topLevelWindows != null) { + for (int i = 0; i < topLevelWindows.length; i++) { + if (topLevelWindows[i] instanceof Accessible) { + installListeners((Accessible) topLevelWindows[i]); + } + } + } + } + + /** + * Installs PropertyChange listeners to the Accessible object, and it's + * children (so long as the object isn't of TRANSIENT state). + * @param a the Accessible object to add listeners to + */ + protected void installListeners(Accessible a) { + installListeners(a.getAccessibleContext()); + } + + /** + * Installs PropertyChange listeners to the AccessibleContext object, + * and it's * children (so long as the object isn't of TRANSIENT state). + * @param a the Accessible object to add listeners to + */ + private void installListeners(AccessibleContext ac) { + + if (ac != null) { + AccessibleStateSet states = ac.getAccessibleStateSet(); + if (!states.contains(AccessibleState.TRANSIENT)) { + ac.addPropertyChangeListener(this); + /* + * Don't add listeners to transient children. Components + * with transient children should return an AccessibleStateSet + * containing AccessibleState.MANAGES_DESCENDANTS. Components + * may not explicitly return the MANAGES_DESCENDANTS state. + * In this case, don't add listeners to the children of + * lists, tables and trees. + */ + AccessibleStateSet set = ac.getAccessibleStateSet(); + if (set.contains(_AccessibleState.MANAGES_DESCENDANTS)) { + return; + } + AccessibleRole role = ac.getAccessibleRole(); + if (role == AccessibleRole.LIST || + role == AccessibleRole.TREE) { + return; + } + if (role == AccessibleRole.TABLE) { + // handle Oracle tables containing tables + Accessible child = ac.getAccessibleChild(0); + if (child != null) { + AccessibleContext ac2 = child.getAccessibleContext(); + if (ac2 != null) { + role = ac2.getAccessibleRole(); + if (role != null && role != AccessibleRole.TABLE) { + return; + } + } + } + } + int count = ac.getAccessibleChildrenCount(); + for (int i = 0; i < count; i++) { + Accessible child = ac.getAccessibleChild(i); + if (child != null) { + installListeners(child); + } + } + } + } + } + + /** + * Removes PropertyChange listeners on all Accessible objects based + * upon the topLevelWindows cached by EventQueueMonitor. + * @param eventID the event ID + * @see EventID + */ + protected void removeListeners() { + Window topLevelWindows[] = EventQueueMonitor.getTopLevelWindows(); + if (topLevelWindows != null) { + for (int i = 0; i < topLevelWindows.length; i++) { + if (topLevelWindows[i] instanceof Accessible) { + removeListeners((Accessible) topLevelWindows[i]); + } + } + } + } + + /** + * Removes PropertyChange listeners for the given Accessible object, + * it's children (so long as the object isn't of TRANSIENT state). + * @param a the Accessible object to remove listeners from + */ + protected void removeListeners(Accessible a) { + removeListeners(a.getAccessibleContext()); + } + + /** + * Removes PropertyChange listeners for the given AccessibleContext + * object, it's children (so long as the object isn't of TRANSIENT + * state). + * @param a the Accessible object to remove listeners from + */ + private void removeListeners(AccessibleContext ac) { + + + if (ac != null) { + // Listeners are not added to transient components. + AccessibleStateSet states = ac.getAccessibleStateSet(); + if (!states.contains(AccessibleState.TRANSIENT)) { + ac.removePropertyChangeListener(this); + /* + * Listeners are not added to transient children. Components + * with transient children should return an AccessibleStateSet + * containing AccessibleState.MANAGES_DESCENDANTS. Components + * may not explicitly return the MANAGES_DESCENDANTS state. + * In this case, don't remove listeners from the children of + * lists, tables and trees. + */ + if (states.contains(_AccessibleState.MANAGES_DESCENDANTS)) { + return; + } + AccessibleRole role = ac.getAccessibleRole(); + if (role == AccessibleRole.LIST || + role == AccessibleRole.TABLE || + role == AccessibleRole.TREE) { + return; + } + int count = ac.getAccessibleChildrenCount(); + for (int i = 0; i < count; i++) { + Accessible child = ac.getAccessibleChild(i); + if (child != null) { + removeListeners(child); + } + } + } + } + } + + /********************************************************************/ + /* */ + /* Listener Interface Methods */ + /* */ + /********************************************************************/ + + /* TopLevelWindow Methods ***************************************/ + + /** + * Called when top level window is created. + * @see EventQueueMonitor + * @see EventQueueMonitor#addTopLevelWindowListener + */ + public void topLevelWindowCreated(Window w) { + if (w instanceof Accessible) { + installListeners((Accessible) w); + } + } + + /** + * Called when top level window is destroyed. + * @see EventQueueMonitor + * @see EventQueueMonitor#addTopLevelWindowListener + */ + public void topLevelWindowDestroyed(Window w) { + if (w instanceof Accessible) { + removeListeners((Accessible) w); + } + } + + + /* PropertyChangeListener Methods **************************************/ + + public void propertyChange(PropertyChangeEvent e) { + // propogate the event + Object[] listeners = + AccessibilityEventMonitor.listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==PropertyChangeListener.class) { + ((PropertyChangeListener)listeners[i+1]).propertyChange(e); + } + } + + // handle childbirth/death + String name = e.getPropertyName(); + if (name.compareTo(AccessibleContext.ACCESSIBLE_CHILD_PROPERTY) == 0) { + Object oldValue = e.getOldValue(); + Object newValue = e.getNewValue(); + + if ((oldValue == null) ^ (newValue == null)) { // one null, not both + if (oldValue != null) { + // this Accessible is a child that's going away + if (oldValue instanceof Accessible) { + Accessible a = (Accessible) oldValue; + removeListeners(a.getAccessibleContext()); + } else if (oldValue instanceof AccessibleContext) { + removeListeners((AccessibleContext) oldValue); + } + } else if (newValue != null) { + // this Accessible is a child was just born + if (newValue instanceof Accessible) { + Accessible a = (Accessible) newValue; + installListeners(a.getAccessibleContext()); + } else if (newValue instanceof AccessibleContext) { + installListeners((AccessibleContext) newValue); + } + } + } else { + System.out.println("ERROR in usage of PropertyChangeEvents for: " + e.toString()); + } + } + } + } +} + +/* + * workaround for no public AccessibleState constructor + */ +class _AccessibleState extends AccessibleState { + /** + * Indicates this object is responsible for managing its + * subcomponents. This is typically used for trees and tables + * that have a large number of subcomponents and where the + * objects are created only when needed and otherwise remain virtual. + * The application should not manage the subcomponents directly. + */ + public static final _AccessibleState MANAGES_DESCENDANTS + = new _AccessibleState ("managesDescendants"); + + /** + * Creates a new AccessibleState using the given locale independent key. + * This should not be a public method. Instead, it is used to create + * the constants in this file to make it a strongly typed enumeration. + * Subclasses of this class should enforce similar policy. + *

+ * The key String should be a locale independent key for the state. + * It is not intended to be used as the actual String to display + * to the user. To get the localized string, use toDisplayString. + * + * @param key the locale independent name of the state. + * @see AccessibleBundle#toDisplayString + */ + protected _AccessibleState(String key) { + super(key); + } +}