Ejemplo n.º 1
0
/** Renderer class for the node chooser table. */
public final class CNodeChooserRenderer extends DefaultTableCellRenderer {
  /** Used for serialization. */
  private static final long serialVersionUID = -7732373934467520210L;

  /** Font used in the node chooser table. */
  private static final Font DEFAULT_CELL_FONT =
      new Font(GuiHelper.getDefaultFont(), Font.PLAIN, 12);

  /** Background color for nodes that are not currently displaying search results. */
  private static final Color DEFAULT_BACKGROUND = new Color(252, 252, 252);

  /** Background color for nodes that are currently displaying search results. */
  private static final Color SEARCH_HIT_BACKGROUND = new Color(252, 252, 200);

  /** Text color used to print the row text of selected nodes. */
  private static final Color SELECTED_FOREGROUND = new Color(160, 0, 0);

  /** Text color used to print the row text of unselected nodes. */
  private static final Color UNSELECTED_FOREGROUND = new Color(0, 0, 0);

  /** Text color used to print the row text of invisible nodes. */
  private static final Color INVISIBLE_FOREGROUND = new Color(128, 128, 128);

  /** Default monospace font used for tooltips. */
  private static final String MONOSPACE_FONT = GuiHelper.getMonospaceFont();

  /** Table that is rendered by this renderer. */
  private final CNodeChooserTable m_table;

  /** Graph that provides the nodes shown in the table. */
  private final ZyGraph m_graph;

  /** Searcher field that provides search results to be rendered in the table. */
  private final GraphSearcher m_searcher;

  /**
   * Creates a new renderer object.
   *
   * @param nodeChooserTable Table that is rendered by this renderer.
   * @param graph Graph that provides the nodes shown in the table.
   * @param searcher Searcher field that provides search results to be rendered in the table.
   */
  public CNodeChooserRenderer(
      final CNodeChooserTable nodeChooserTable, final ZyGraph graph, final GraphSearcher searcher) {
    m_table = Preconditions.checkNotNull(nodeChooserTable, "IE01770: Table can't be null.");
    m_graph = Preconditions.checkNotNull(graph, "IE01771: Graph can't be null.");
    m_searcher = Preconditions.checkNotNull(searcher, "IE01772: Graph searcher can't be null.");

    setOpaque(true);
  }

  /**
   * Builds the tooltip text of a table row from the content of a node.
   *
   * @param content The content of a node.
   * @return The tooltip text for the node.
   */
  private static String buildToolTip(final ZyLabelContent content) {
    return HtmlGenerator.getHtml(content, MONOSPACE_FONT, true);
  }

  /**
   * Returns the text to be shown in the node chooser table for a given node.
   *
   * @param node The node to display.
   * @return The display text of the node.
   */
  private static String getNodeText(final INaviViewNode node) {
    if (node instanceof INaviFunctionNode) {
      return ((INaviFunctionNode) node).getFunction().getName();
    } else if (node instanceof INaviCodeNode) {
      return Iterables.getFirst(((INaviCodeNode) node).getInstructions(), null)
          .getAddress()
          .toHexString();
    } else if (node instanceof INaviGroupNode) {
      return "GroupNode"; // ((INaviGroupNode) node).getComment(); //TODO fix this.
    } else if (node instanceof INaviTextNode) {
      return "Comment node";
    } else {
      throw new IllegalStateException("IE01152: Invalid node in node chooser");
    }
  }

  /**
   * Updates the background color of a row in case the corresponding node is currently displaying
   * search results.
   *
   * @param node The node that corresponds to the table row.
   */
  private void calculateBackgroundColor(final INaviViewNode node) {
    final List<SearchResult> results = m_searcher.getResults();

    for (final SearchResult result : results) {
      if ((result.getObject() instanceof NaviNode)
          && ((NaviNode) result.getObject()).getRawNode().equals(node)) {
        setBackground(SEARCH_HIT_BACKGROUND);
        return;
      }
    }
  }

  /**
   * Small helper function to return the node of a given node index.
   *
   * @param index The node index.
   * @return The node at the given index.
   */
  private INaviViewNode getNode(final int index) {
    return m_graph.getRawView().getGraph().getNodes().get(index);
  }

  // ESCA-JAVA0138: Not our function
  @Override
  public Component getTableCellRendererComponent(
      final JTable table,
      final Object value,
      final boolean isSelected,
      final boolean hasFocus,
      final int row,
      final int column) {
    setFont(DEFAULT_CELL_FONT);

    final INaviViewNode node = getNode(m_table.modelIndex(row));

    Color textColor = UNSELECTED_FOREGROUND;

    if (node.isSelected()) {
      textColor = SELECTED_FOREGROUND;
    } else if (!node.isVisible()) {
      textColor = INVISIBLE_FOREGROUND;
    }

    setForeground(textColor);

    if (column == CNodeChooserModel.COLUMN_IN) {
      setText(String.valueOf(node.getIncomingEdges().size()));
    } else if (column == CNodeChooserModel.COLUMN_OUT) {
      setText(String.valueOf(node.getOutgoingEdges().size()));
    } else if (column == CNodeChooserModel.COLUMN_ADDRESS) {
      setText(getNodeText(node));
    } else {
      setText("");
    }

    setBackground(DEFAULT_BACKGROUND);

    if (column == CNodeChooserModel.COLUMN_COLOR) {
      setBackground(node.getColor());
    } else {
      calculateBackgroundColor(node);
    }

    setToolTipText(buildToolTip(m_graph.getNode(node).getNodeContent()));

    return this;
  }
}
Ejemplo n.º 2
0
/** This class can be used to display the stack of a target process. */
public final class JStackPanel extends JPanel {
  private static final long serialVersionUID = -7850318708757157383L;

  private static final int PADDING_OFFSETVIEW = 20;

  private static final int PADDING_LEFT = 10;

  private static final int SIZEOF_DWORD = 4;

  private static final int SIZEOF_QWORD = 8;

  /** Provides the stack data that is displayed in the view. */
  private final IStackModel m_model;

  /** Font used to draw the data. */
  private final Font m_font = new Font(GuiHelper.getMonospaceFont(), 0, 12);

  /** Height of individual rows in the display. */
  private int m_rowHeight;

  /** Height of a single char in the display. */
  private int m_charHeight;

  /** Width of a single char in the view. */
  private int m_charWidth;

  /** Width of the offset view. */
  private int m_offsetViewWidth;

  private boolean m_firstDraw = true;

  private static final int m_hexElementWidth = 10;

  /** Default internal listener that is used to handle various events. */
  private final InternalListener m_listener = new InternalListener();

  /** The first visible row. */
  private int m_firstRow = 0;

  /** Top-padding of all views in pixels. */
  private static final int m_paddingTop = 16;

  /** Font color of the offset view. */
  private final Color m_fontColorOffsets = Color.WHITE;

  private final Color m_fontColorValues = Color.BLACK;

  /** Color that is used to draw all text in disabled components. */
  private final Color m_disabledColor = Color.GRAY;

  /** Background color of the offset view. */
  private final Color m_bgColorOffset = Color.GRAY;

  /** Timer that is used to refresh the component if no data for the selected range is available. */
  private Timer m_updateTimer;

  private DefinitionStatus m_status = DefinitionStatus.UNDEFINED;

  private final AddressMode m_addressMode = AddressMode.BIT32;

  private int m_firstColumn = 0;

  /**
   * Creates a new stack view.
   *
   * @param model The model that provides the data displayed in the view.
   */
  public JStackPanel(final IStackModel model) {
    super(new BorderLayout());

    Preconditions.checkNotNull(model, "Error: Model argument can not be null");

    m_model = model;

    m_model.addListener(m_listener);

    // Necessary to receive input
    setFocusable(true);

    // Set the initial font
    setFont(m_font);

    setPreferredSize(new Dimension(400, 400));
  }

  /**
   * Returns the character size of a single character on the given graphics context.
   *
   * @param g The graphics context.
   * @return The size of a single character.
   */
  private static int getCharacterWidth(final Graphics g) {
    return (int) g.getFontMetrics().getStringBounds("0", g).getWidth();
  }

  /**
   * Determines the height of a character in a graphical context.
   *
   * @param g The graphical context.
   * @return The height of a character in the graphical context.
   */
  private static int getCharHeight(final Graphics g) {
    return g.getFontMetrics().getAscent();
  }

  /**
   * Determines the height of the current font in a graphical context.
   *
   * @param g The graphical context.
   * @return The height of the current font in the graphical context.
   */
  private static int getRowHeight(final Graphics g) {
    return g.getFontMetrics().getHeight();
  }

  /**
   * Calculates current character and row sizes.
   *
   * @param g The graphical context.
   */
  private void calculateSizes(final Graphics g) {
    m_rowHeight = getRowHeight(g);
    m_charHeight = getCharHeight(g);
    m_charWidth = getCharacterWidth(g);
  }

  /**
   * Draws the background of the view.
   *
   * @param g The graphics context of the view.
   */
  private void drawBackground(final Graphics g) {
    // Draw the background of the offset view
    g.setColor(m_bgColorOffset);
    g.fillRect(-m_firstColumn * m_charWidth, 0, m_offsetViewWidth, getHeight());
  }

  /**
   * Draws the stack values onto the screen.
   *
   * @param g The graphics context to paint on.
   */
  private void drawElements(final Graphics g) {
    if (isEnabled()) {
      // Choose the right color for the offset text
      g.setColor(m_fontColorValues);
    } else {
      g.setColor(m_disabledColor != m_bgColorOffset ? m_disabledColor : Color.WHITE);
    }

    final int x = (10 + m_offsetViewWidth) - (m_charWidth * m_firstColumn);

    int linesToDraw = getNumberOfVisibleRows();

    if ((m_firstRow + linesToDraw) >= m_model.getNumberOfEntries()) {
      linesToDraw = m_model.getNumberOfEntries() - m_firstRow; // TODO: This can make linesToDraw
      // negative

      if (linesToDraw < 0) {
        // FIXME: This is a workaround for case 2337. The issue is real
        // but reproducing it can take hours. For this reason I am now
        // implementing this workaround, but in the future the underlying
        // cause of this behavior should be determined and fixed.

        return;
      }
    }

    if (m_model.getStartAddress() == -1) {
      return;
    }

    final long elementSize = getElementSize();

    if (m_status == DefinitionStatus.DEFINED) {
      final long startAddress = m_model.getStartAddress() + (m_firstRow * elementSize);

      final long numberOfBytes = linesToDraw * elementSize;

      if (!m_model.hasData(startAddress, numberOfBytes)) {
        setDefinitionStatus(DefinitionStatus.UNDEFINED);
        setEnabled(false);

        if (m_updateTimer != null) {
          m_updateTimer.setRepeats(false);
          m_updateTimer.stop();
        }

        m_updateTimer = new Timer(1000, new WaitingForDataAction(startAddress, numberOfBytes));
        m_updateTimer.setRepeats(true);
        m_updateTimer.start();

        return;
      }

      // Iterate over the data and print the offsets
      for (int i = 0; i < linesToDraw; i++) {
        final long elementAddress = startAddress + (i * elementSize);

        g.drawString(m_model.getElement(elementAddress), x, m_paddingTop + (i * m_rowHeight));
      }
    } else {
      // Iterate over the data and print the offsets
      for (int i = 0; i < linesToDraw; i++) {
        g.drawString(
            Strings.repeat("?", 2 * getElementSize()), x, m_paddingTop + (i * m_rowHeight));
      }
    }
  }

  /**
   * Draws the offsets in the offset view.
   *
   * @param g The graphics context of the hex panel.
   */
  private void drawOffsets(final Graphics g) {
    final int linesToDraw = getNumberOfVisibleRows();

    final String formatString = m_addressMode == AddressMode.BIT32 ? "%08X" : "%016X";
    final long elementSize = getElementSize();

    final long baseAddress = m_model.getStartAddress() == -1 ? 0 : m_model.getStartAddress();

    // Iterate over the data and print the offsets
    for (int i = 0; i < linesToDraw; i++) {
      final int elementIndex = m_firstRow + i;

      final long elementAddress = baseAddress + (elementIndex * elementSize);

      final String offsetString = String.format(formatString, elementAddress);

      if (elementAddress == m_model.getStackPointer()) {
        highlightStackPointer(g, i);
      }

      if (isEnabled()) {
        g.setColor(m_fontColorOffsets);
      } else {
        g.setColor(m_disabledColor != m_bgColorOffset ? m_disabledColor : Color.WHITE);
      }

      g.drawString(
          offsetString,
          PADDING_LEFT - (m_charWidth * m_firstColumn),
          m_paddingTop + (i * m_rowHeight));
    }
  }

  /**
   * Returns the size in bytes of a single stack element.
   *
   * @return The size in bytes of a single stack element.
   */
  private int getElementSize() {
    return m_addressMode == AddressMode.BIT32 ? SIZEOF_DWORD : SIZEOF_QWORD;
  }

  /**
   * Highlights the address of the stack pointer.
   *
   * @param g The graphics context to draw to.
   * @param row Row where the stack pointer is shown.
   */
  private void highlightStackPointer(final Graphics g, final int row) {
    g.setColor(Color.RED);

    final double width =
        g.getFontMetrics().getStringBounds(Strings.repeat("0", 2 * getElementSize()), g).getWidth();

    g.fillRect(
        PADDING_LEFT - 2 - (m_charWidth * m_firstColumn),
        (m_paddingTop + (row * m_rowHeight)) - m_charHeight,
        (int) width + 4,
        m_charHeight + 2);
  }

  /**
   * Calculates and sets the size of the offset view depending on the currently selected address
   * mode.
   */
  private void updateOffsetViewWidth() {
    final int addressBytes = m_addressMode == AddressMode.BIT32 ? 8 : 16;
    m_offsetViewWidth = PADDING_OFFSETVIEW + (m_charWidth * addressBytes);
  }

  /** Calculates and sets the preferred size of the component. */
  private void updatePreferredSize() {
    // TODO: Improve this
    final int width = m_offsetViewWidth + m_hexElementWidth + (18 * m_charWidth);
    setPreferredSize(new Dimension(width, getHeight()));
    revalidate();
  }

  /**
   * Returns the number of visible rows.
   *
   * @return The number of visible rows.
   */
  protected int getNumberOfVisibleRows() {
    if (m_rowHeight == 0) {
      return 0;
    }

    final int rawHeight = getHeight() - m_paddingTop;
    return (rawHeight / m_rowHeight) + ((rawHeight % m_rowHeight) == 0 ? 0 : 1);
  }

  protected void setFirstRow(final int value) {
    m_firstRow = value;

    repaint();
  }

  public int getCharWidth() {
    return m_charWidth;
  }

  public int getOffsetViewWidth() {
    return m_offsetViewWidth;
  }

  public String getValueAt(final Point point) {
    final int line = ((point.y - m_paddingTop) + m_rowHeight) / m_rowHeight;

    final long elementSize = getElementSize();

    final long startAddress = m_model.getStartAddress() + (m_firstRow * elementSize);

    final long elementAddress = startAddress + (line * elementSize);

    return m_model.hasData(elementAddress, elementSize) ? m_model.getElement(elementAddress) : null;
  }

  /**
   * Scrolls to the given offset.
   *
   * @param offset The offset to scroll to.
   */
  public void gotoOffset(final long offset) {
    // setCurrentPosition(offset);
  }

  @Override
  public void paint(final Graphics g) {
    super.paint(g);

    // Calculate current sizes of characters and rows
    calculateSizes(g);

    updateOffsetViewWidth();

    if (m_firstDraw) {
      m_firstDraw = false;

      // The first time the component is drawn, its size must be set.
      updatePreferredSize();
    }

    // Draw the background of the hex panel
    drawBackground(g);

    // Draw the offsets column
    drawOffsets(g);

    drawElements(g);
  }

  /**
   * Changes the definition status of the view data.
   *
   * @param status The new definition status.
   */
  public void setDefinitionStatus(final DefinitionStatus status) {
    Preconditions.checkNotNull(status, "Error: Status argument can not be null");

    m_status = status;

    repaint();
  }

  public void setFirstColumn(final int value) {
    m_firstColumn = value;
  }

  private class InternalListener implements IStackModelListener {
    @Override
    public void dataChanged() {
      repaint();
    }
  }

  private class WaitingForDataAction extends AbstractAction {
    private static final long serialVersionUID = -610823391617272365L;
    private final long m_startAddress;
    private final long m_numberOfBytes;

    private WaitingForDataAction(final long startAddress, final long numberOfBytes) {
      m_startAddress = startAddress;
      m_numberOfBytes = numberOfBytes;
    }

    @Override
    public void actionPerformed(final ActionEvent event) {
      if (m_model.hasData(m_startAddress, m_numberOfBytes)) {
        JStackPanel.this.setEnabled(true);
        setDefinitionStatus(DefinitionStatus.DEFINED);

        ((Timer) event.getSource()).stop();
      } else if (!m_model.keepTrying()) {
        ((Timer) event.getSource()).stop();
      }
    }
  }
}