blob: e6e892dad43f2af0ee133196351e1d148453ccfd [file] [log] [blame]
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed 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 com.intellij.internal.anomalies;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.objectTree.ObjectNode;
import com.intellij.openapi.util.objectTree.ObjectTree;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
/**
* User: Vassiliy.Kudryashov
*/
public class TopAnomaliesAction extends ActionGroup {
private static final Comparator<Pair<?, Integer>> COMPARATOR = new Comparator<Pair<?, Integer>>() {
@Override
public int compare(Pair<?, Integer> o1, Pair<?, Integer> o2) {
int i = o2.getSecond() - o1.getSecond();
if (i != 0) {
return i;
}
int h1 = o1.hashCode();
int h2 = o2.hashCode();
if (h1 > h2) {
return 1;
}
if (h1 < h2) {
return -1;
}
return 0;
}
};
private static final int LIMIT = 10;
private static final ResettableAction TOP_PARENTS = new ResettableAction("Parents") {
TreeSet<Pair<JComponent, Integer>> top = new TreeSet<Pair<JComponent, Integer>>(COMPARATOR);
TreeSet<Pair<JComponent, Integer>> old = new TreeSet<Pair<JComponent, Integer>>(COMPARATOR);
@Override
public void update(AnActionEvent e) {
e.getPresentation().setText("Top " + LIMIT + " component parents");
}
@Override
void reset() {
top.clear();
old.clear();
}
@Override
public void actionPerformed(AnActionEvent e) {
old = new TreeSet<Pair<JComponent, Integer>>(top);
top.clear();
Window[] windows = Window.getWindows();
for (Window window : windows) {
if (window.isVisible() && (window instanceof JFrame)) {
JFrame f = (JFrame)window;
checkParents((JComponent)f.getContentPane(), top, LIMIT);
}
}
System.out.println("Top " + LIMIT + " component parents");
for (Pair<JComponent, Integer> pair : top) {
System.out.println(
pair.first.getClass().getName() + " (" + pair.second + " children)" + getChange(old, pair.first, pair.second));
}
}
private void checkParents(JComponent component, Set<Pair<JComponent, Integer>> top, int limit) {
top.add(Pair.create(component, component.getComponentCount()));
trimToLimit(top, limit);
for (int i = 0; i < component.getComponentCount(); i++) {
Component child = component.getComponent(i);
if (child instanceof JComponent) {
checkParents((JComponent)child, top, limit);
}
}
}
};
private static final ResettableAction TOP_UI_PROPERTIES = new ResettableAction("ClientProperties") {
TreeSet<Pair<JComponent, Integer>> top = new TreeSet<Pair<JComponent, Integer>>(COMPARATOR);
TreeSet<Pair<JComponent, Integer>> old = new TreeSet<Pair<JComponent, Integer>>(COMPARATOR);
@Override
public void update(AnActionEvent e) {
e.getPresentation().setText("Top " + LIMIT + " ClientProperties");
}
@Override
void reset() {
top.clear();
old.clear();
}
@Override
public void actionPerformed(AnActionEvent e) {
old = new TreeSet<Pair<JComponent, Integer>>(top);
top.clear();
Window[] windows = Window.getWindows();
for (Window window : windows) {
if (window.isVisible() && (window instanceof JFrame)) {
JFrame f = (JFrame)window;
checkClientProperties((JComponent)f.getContentPane(), top, LIMIT);
}
}
System.out.println("Top " + LIMIT + " ClientProperties");
for (Pair<JComponent, Integer> pair : top) {
System.out.println(pair.first.getClass().getName() + " (" + pair.second + " properties)" + getChange(old, pair.first, pair.second));
}
}
private void checkClientProperties(JComponent component, Set<Pair<JComponent, Integer>> top, int limit) {
try {
Field clientProperties = JComponent.class.getDeclaredField("clientProperties");
clientProperties.setAccessible(true);
Object o = clientProperties.get(component);
if (o != null) {
Method size = o.getClass().getMethod("size");
size.setAccessible(true);
Object sizeResult = size.invoke(o);
if (sizeResult instanceof Integer) {
top.add(Pair.create(component, (Integer)sizeResult));
trimToLimit(top, limit);
}
}
}
catch (NoSuchMethodException e) {
}
catch (InvocationTargetException e) {
}
catch (IllegalAccessException e) {
}
catch (NoSuchFieldException e) {
}
for (int i = 0; i < component.getComponentCount(); i++) {
Component child = component.getComponent(i);
if (child instanceof JComponent) {
checkClientProperties((JComponent)child, top, limit);
}
}
}
};
private static final ResettableAction TOP_DISPOSABLE = new ResettableAction("Disposable") {
TreeSet<Pair<Object, Integer>> top = new TreeSet<Pair<Object, Integer>>(COMPARATOR);
TreeSet<Pair<Object, Integer>> old = new TreeSet<Pair<Object, Integer>>(COMPARATOR);
@Override
public void update(AnActionEvent e) {
e.getPresentation().setText("Top " + LIMIT + " disposables");
}
@Override
void reset() {
top.clear();
old.clear();
}
@Override
public void actionPerformed(AnActionEvent e) {
old = new TreeSet<Pair<Object, Integer>>(top);
top.clear();
ObjectTree<Disposable> tree = Disposer.getTree();
Set<Disposable> roots = tree.getRootObjects();
for (Disposable root : roots) {
checkDisposables(tree, root, top, LIMIT);
}
System.out.println("Top " + LIMIT + " disposables");
for (Pair<Object, Integer> pair : top) {
System.out.println(pair.first.getClass().getName() + " (" + pair.second + " related)" + getChange(old, pair.first, pair.second));
}
}
private void checkDisposables(ObjectTree tree, Object key, Set<Pair<Object, Integer>> top, int limit) {
ObjectNode node = tree.getNode(key);
if (node == null) {
return;
}
Collection children = node.getChildren();
top.add(Pair.create(key, children.size()));
trimToLimit(top, limit);
for (Object child : children) {
checkDisposables(tree, child, top, limit);
}
}
};
private static final ResettableAction RESET_THEM_ALL = new ResettableAction("Reset statistics") {
@Override
void reset() {
}
@Override
public void actionPerformed(AnActionEvent e) {
for (ResettableAction action : CHILDREN) {
action.reset();
}
}
};
private static ResettableAction[] CHILDREN = {TOP_PARENTS, TOP_UI_PROPERTIES, TOP_DISPOSABLE, RESET_THEM_ALL};
@Override
public void update(AnActionEvent e) {
e.getPresentation().setText("Top " + LIMIT);
}
@NotNull
@Override
public AnAction[] getChildren(@Nullable AnActionEvent e) {
return CHILDREN;
}
private static <K, V> void trimToLimit(Set<Pair<K, V>> top, int limit) {
int k = 0;
for (Iterator<Pair<K, V>> iterator = top.iterator(); iterator.hasNext(); ) {
k++;
iterator.next();
if (k >= limit) {
iterator.remove();
}
}
}
private static <K, V extends Integer> String getChange(Set<Pair<K, V>> old, K key, int newResult) {
for (Pair<K, V> oldPair : old) {
if (oldPair.first == key) {
int oldResult = oldPair.second.intValue();
if (oldResult != newResult) {
return (oldResult > newResult ? " -" : " +") + Math.abs(newResult - oldResult);
}
break;
}
}
return "";
}
private static abstract class ResettableAction extends AnAction {
protected ResettableAction(@Nullable String text) {
super(text);
}
abstract void reset();
}
}