blob: 5361e98dae52c5cda6060af5a69ade738365f210 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org;
import java.lang.ref.*;
import java.util.*;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import java.awt.event.InputEvent;
aspect ListenerCount
{
//
// pointcuts and advice
//
pointcut addAnyListener(Object t, Object l) : target(t) && args(l) && execution(* *..add*Listener(*Listener+));
pointcut addNamedPropertyChangeListener(Object t, Object l) : target(t) && execution(* *..addPropertyChangeListener(String, *Listener+)) && args(*, l);
pointcut removeAnyListener(Object t, Object l) : target(t) && args(l) && execution(* *..remove*Listener(*Listener+));
before(Object t, Object l): addAnyListener(t, l) || addNamedPropertyChangeListener(t, l) {
listenerAdded(t, thisJoinPointStaticPart.getSignature().getName().substring("add".length()), l);
}
before(Object t, Object l): removeAnyListener(t, l) {
listenerRemoved(t, thisJoinPointStaticPart.getSignature().getName().substring("remove".length()), l);
}
before() : execution(* org.netbeans.core.NbTopManager.exit()) {
dumpListenerCounts();
}
//
// implementation details
//
ListenerCount() {
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new MagicKeyHandler());
}
private class MagicKeyHandler implements KeyEventDispatcher
{
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_PRESSED
&& e.getModifiers() == (InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK)
&& e.getKeyCode() == KeyEvent.VK_L) {
ListenerCount.this.dumpListenerCounts();
}
return false;
}
}
private static final boolean DONT_REPORT_FAILED_REMOVE = Boolean.getBoolean("ListenerCount.dont_report_failed_remove");
private ThreadLocal lastFailedRemovedListener = new ThreadLocal();
private ThreadLocal lastFailedRemoveStackTrace = new ThreadLocal();
private Map sourceListeners = new HashMap(); // SourceListener -> Map(listener->count)
private Map sourceListenerPeaks = new HashMap();
private synchronized void listenerAdded(Object source, String listenerType, Object listener) {
//System.err.println("added: source = " + source + " listenerType = " + listenerType);
checkLastFailedRemove(listener);
SourceListener sl = new SourceListener(source, listenerType);
Map listeners = (Map) sourceListeners.get(sl);
int c = 1;
if (listeners == null) {
listeners = new IdentityHashMap();
sourceListeners.put(sl, listeners);
}
else {
Integer count = (Integer) listeners.get(listener);
if (count != null) {
c = count.intValue() + 1;
System.err.println("listenerCount> WARNING: registering the same listener for the "
+ (c == 2 ? "2nd" : (c == 3 ? "3rd" : ("" + c + "th")))
+ " time, not good");
System.err.println("listenerCount> source = " + source);
System.err.println("listenerCount> listener = " + listener);
Thread.dumpStack();
}
}
listeners.put(listener, new Integer(c));
Integer peak = (Integer) sourceListenerPeaks.get(sl);
if (peak != null) {
int count = listeners.size();
if (peak.intValue() < count)
sourceListenerPeaks.put(sl, new Integer(count));
}
else {
sourceListenerPeaks.put(sl, new Integer(1));
}
}
private synchronized void listenerRemoved(Object source, String listenerType, Object listener) {
//System.err.println("rm'ed: source = " + source + " listenerType = " + listenerType);
checkLastFailedRemove(null);
SourceListener sl = new SourceListener(source, listenerType);
Map listeners = (Map) sourceListeners.get(sl);
if (listeners == null || null != listeners.remove(listener)) {
lastFailedRemovedListener.set(listener);
lastFailedRemoveStackTrace.set(new Throwable());
}
}
private void checkLastFailedRemove(Object addedListener) {
Object listener = lastFailedRemovedListener.get();
if (! DONT_REPORT_FAILED_REMOVE && listener != null && listener != addedListener) {
System.err.println("listenerCount> WARNING: attempt to remove unregistered listener, may or may not be okay");
((Throwable)lastFailedRemoveStackTrace.get()).printStackTrace(System.err);
}
lastFailedRemovedListener.set(null);
lastFailedRemoveStackTrace.set(null);
}
private void dumpListenerCounts() {
System.gc();
System.gc();
System.gc();
HashMap sourceListenerPeaks;
synchronized (this) {
sourceListenerPeaks = new HashMap(this.sourceListenerPeaks);
}
Map sclcounts = new HashMap(); // SourceClassListener -> List of Integers
Iterator iter = sourceListenerPeaks.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
SourceListener sl = (SourceListener) entry.getKey();
Integer count = (Integer) entry.getValue();
SourceClassListener scl = new SourceClassListener(sl.getSourceClass(), sl.getListenerType());
List counts = (List) sclcounts.get(scl);
if (counts == null) {
counts = new ArrayList();
sclcounts.put(scl, counts);
}
counts.add(count);
}
System.out.println("listenerCount> --%<----%<----%<----%<----%<----%<----%<----%<----%<----%<----%<----%<----%<----%<--");
iter = sclcounts.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
SourceClassListener scl = (SourceClassListener) entry.getKey();
List counts = (List) entry.getValue();
Collections.sort(counts);
int num = -1;
int occur = 0;
Iterator iter2 = counts.iterator();
while (iter2.hasNext()) {
int c = ((Integer)iter2.next()).intValue();
if (num < 0) {
num = c;
}
else {
if (c != num) {
System.out.println("listenerCount: " + num + "\t" + occur + "\t" + scl.getSourceClass().getName() + "\t" + scl.getListenerType());
num = c;
occur = 1;
continue;
}
}
occur++;
}
if (num > 0 && occur > 0)
System.out.println("listenerCount: " + num + "\t" + occur + "\t" + scl.getSourceClass().getName() + "\t" + scl.getListenerType());
}
System.out.println("listenerCount> --%<----%<----%<----%<----%<----%<----%<----%<----%<----%<----%<----%<----%<----%<--");
}
private static class SourceClassListener {
private Class sourceClass;
private String listenerType;
SourceClassListener(Class sourceClass, String listenerType) {
this.sourceClass = sourceClass;
this.listenerType = listenerType.intern();
}
Class getSourceClass() {
return sourceClass;
}
String getListenerType() {
return listenerType;
}
public boolean equals(Object o) {
if (!(o instanceof SourceClassListener))
return false;
SourceClassListener scl = (SourceClassListener) o;
return sourceClass == scl.sourceClass && listenerType == scl.listenerType;
}
public int hashCode() {
return 37 * sourceClass.hashCode() + listenerType.hashCode();
}
}
private static class SourceListener {
private WeakReference sourceRef;
private int sourceHashCode;
private Class sourceClass;
private String listenerType;
SourceListener(Object source, String listenerType) {
this.sourceRef = new WeakReference(source);
this.sourceHashCode = source.hashCode();
this.sourceClass = source.getClass();
this.listenerType = listenerType.intern();
}
Object getSource() {
return sourceRef.get();
}
Class getSourceClass() {
return sourceClass;
}
String getListenerType() {
return listenerType;
}
public boolean equals(Object o) {
if (o instanceof SourceListener) {
SourceListener sl = (SourceListener) o;
return sourceRef.get() == sl.sourceRef.get() && listenerType == sl.listenerType;
}
else {
return false;
}
}
public int hashCode() {
return 37 * listenerType.hashCode() + sourceHashCode;
}
}
}