All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.spf4j.ui.StackPanelBase Maven / Gradle / Ivy

Go to download

A simple swing UI that allows you to visualize/export diagnostic files generates with the spf4j-core library: tsdb, ssdump...

There is a newer version: 8.10.0
Show newest version
/*
 * Copyright (c) 2001-2017, Zoltan Farkas All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Additionally licensed with:
 *
 * 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 org.spf4j.ui;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.List;
import javax.annotation.Nullable;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.ToolTipManager;
import org.spf4j.base.Method;
import org.spf4j.base.Pair;
import org.spf4j.ds.RTree;
import org.spf4j.stackmonitor.SampleNode;

/**
 * @author zoly
 */
public abstract class StackPanelBase extends JPanel
        implements ActionListener, MouseListener {

  private static final long serialVersionUID = 1L;
  //CHECKSTYLE:OFF
  private SampleNode samples;
  private Method method;
  private RTree samplesRTree = new RTree<>();
  protected int xx;
  protected int yy;
  //CHECKSTYLE:ON
  private final JPopupMenu menu;
  private final LinkedList> history;
  private volatile BufferedImage img;
  private volatile int imgWidth;

  public static final Color LINK_COLOR = new Color(128, 128, 128, 128);

  public StackPanelBase(final Method method,
          final SampleNode samples, final LinkedList> history) {
    this.samples = samples;
    this.method = method;
    this.history = history;
    setPreferredSize(new Dimension(400, 20 * samples.height() + 10));
    final ToolTipManager sharedInstance = ToolTipManager.sharedInstance();
    sharedInstance.registerComponent(this);
    sharedInstance.setDismissDelay(30000);
    menu = buildPopupMenu(this);
    addMouseListener(this);
  }

  public final List search(final int x, final int y, final int w, final int h) {
    return samplesRTree.search(new float[]{x, y}, new float[]{w, h});
  }

  public final List search(final double x, final double y, final double w, final double h) {
    return samplesRTree.search(new float[]{(float) x, (float) y}, new float[]{(float) w, (float) h});
  }

  public final void insert(final int x, final int y, final int w, final int h, final T sampled) {
    samplesRTree.insert(new float[]{x, y}, new float[]{w, h}, sampled);
  }

  public final void insert(final double x, final double y, final double w, final double h, final T sampled) {
    samplesRTree.insert(new float[]{(float) x, (float) y}, new float[]{(float) w, (float) h}, sampled);
  }

  public final void insert(final Rectangle2D.Double rect, final T sampled) {
    samplesRTree.insert(new float[]{(float) rect.getX(), (float) rect.getY()},
            new float[]{(float) rect.getWidth(), (float) rect.getHeight()}, sampled);
  }

  // disable finbugs since I don't care about internationalization for now.
  @SuppressFBWarnings("S508C_NON_TRANSLATABLE_STRING")
  private static JPopupMenu buildPopupMenu(final ActionListener listener) {
    JPopupMenu result = new JPopupMenu("Actions");
    JMenuItem filter = new JMenuItem("Filter");
    filter.setActionCommand("FILTER");
    filter.addActionListener(listener);
    result.add(filter);
    JMenuItem drill = new JMenuItem("Drill");
    drill.setActionCommand("DRILL");
    drill.addActionListener(listener);
    result.add(drill);
    JMenuItem back = new JMenuItem("Back");
    back.setActionCommand("BACK");
    back.addActionListener(listener);
    result.add(back);
    JMenuItem copy = new JMenuItem("Copy");
    copy.setActionCommand("COPY");
    copy.addActionListener(listener);
    result.add(copy);
    return result;
  }

  @Override
  public final String getToolTipText(final MouseEvent event) {
    Point location = event.getPoint();
    return getDetail(location);
  }

  @Override
  @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY") //its ok in this case, since I am detecting changes.
  public final void paintComponent(final Graphics g) {
    super.paintComponent(g);
    Dimension size = getSize();
    Insets insets = getInsets();
    Graphics2D g2 = (Graphics2D) g.create();
    try {
      int width1 = (int) size.getWidth();
      if (img == null || imgWidth != width1) {
        double rowHeight = g2.getFont().getStringBounds("ROOT", g2.getFontRenderContext()).getHeight() + 2;
        img = paintImage(g2, width1 - insets.left - insets.right, rowHeight);
        Dimension dimension = new Dimension(img.getWidth(), img.getHeight());
        setPreferredSize(dimension);
        imgWidth = width1;
      }
      g2.drawImage(img, insets.left, insets.top, this);
    } finally {
      g2.dispose();
    }
  }

  private BufferedImage paintImage(final Graphics2D g2, final int width, final double rowHeight) {
    GraphicsConfiguration gc = g2.getDeviceConfiguration();
    int height = 5000;
    BufferedImage limg;
    do {
      limg = gc.createCompatibleImage(width, height, Transparency.TRANSLUCENT);
      Graphics2D gr = limg.createGraphics();
      gr.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
      int nheight = repaint(gr, width, rowHeight) + 10;
      gr.dispose();
      if (nheight < height) {
        return limg.getSubimage(0, 0, width, nheight);
      }
      height = nheight;
    } while (true);
  }

  private int repaint(final Graphics2D gr, final double width, final double rowHeight) {
    samplesRTree.clear();
    return paint(gr, width, rowHeight);
  }

  public abstract int paint(Graphics2D gr, double width, double rowHeight);

  @Override
  public final void actionPerformed(final ActionEvent e) {
    final String actionCommand = e.getActionCommand();
    switch (actionCommand) {
      case "FILTER":
        history.addLast(Pair.of(method, samples));
        filter();
        break;
      case "DRILL":
        history.addLast(Pair.of(method, samples));
        drill();
        break;
      case "BACK":
        Pair prev = history.pollLast();
        if (prev != null) {
          updateSamples(prev.getFirst(), prev.getSecond());
          repaint();
        }
        break;
      case "COPY":
        final String detail = getDetail(new Point(xx, yy));
        java.awt.Toolkit.getDefaultToolkit().getSystemClipboard().setContents(
                new Transferable() {
          @Override
          public DataFlavor[] getTransferDataFlavors() {
            return new DataFlavor[]{DataFlavor.stringFlavor};
          }

          @Override
          public boolean isDataFlavorSupported(final DataFlavor flavor) {
            return flavor.equals(DataFlavor.stringFlavor);
          }

          @Override
          public Object getTransferData(final DataFlavor flavor) {
            return detail;
          }
        }, new ClipboardOwner() {
          @Override
          public void lostOwnership(final Clipboard clipboard, final Transferable contents) {
          }
        });
        break;
      default:
        break;
    }
  }

  @Override
  public final void mousePressed(final MouseEvent e) {
    if (e.isPopupTrigger()) {
      xx = e.getX();
      yy = e.getY();
      menu.show(this, e.getX(), e.getY());
    }
  }

  @Override
  public final void mouseReleased(final MouseEvent e) {
    if (e.isPopupTrigger()) {
      xx = e.getX();
      yy = e.getY();
      menu.show(this, e.getX(), e.getY());
    }
  }

  @Override
  @SuppressFBWarnings
  public void mouseClicked(final MouseEvent e) {
    // default do nothing
  }

  @Override
  @SuppressFBWarnings
  public void mouseEntered(final MouseEvent e) {
    // default do nothing
  }

  @Override
  @SuppressFBWarnings
  public void mouseExited(final MouseEvent e) {
    // default do nothing
  }

  public static void setElementColor(final int depth, final Graphics2D g2) {
    if (depth % 2 == 0) {
      g2.setPaint(Color.YELLOW);
      g2.setBackground(Color.YELLOW);
    } else {
      g2.setPaint(Color.ORANGE);
      g2.setBackground(Color.ORANGE);
    }
  }

  //CHECKSTYLE:OFF
  public void updateSamples(final Method m, final SampleNode n) {
    //CHECKSTYLE:ON
    this.samples = n;
    this.method = m;
    this.img = null;
  }

  public final SampleNode getSamples() {
    return samples;
  }

  public final Method getMethod() {
    return method;
  }

  public final LinkedList> getHistory() {
    return history;
  }

  @Nullable
  public abstract String getDetail(Point location);

  public abstract void filter();

  public abstract void drill();

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy