Click here to Skip to main content
15,895,829 members
Articles / Web Development / HTML

Java Class Viewer

Rate me:
Please Sign up or sign in to vote.
4.93/5 (41 votes)
21 Jun 2016MIT8 min read 179.1K   7.8K   96  
Watch the Java class file visually & interactively for the meaning of every byte
/*
 * JBinaryViewer.java    September 3, 2007, 12:07 AM
 *
 * Copyright  2007, FreeInternals.org. All rights reserved.
 * Use is subject to license terms.
 */
package org.freeinternals.commonlib.ui;

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.SpringLayout;
import org.freeinternals.commonlib.ui.binviewer.JAsciiDataViewer;
import org.freeinternals.commonlib.ui.binviewer.JRawDataViewer;
import org.freeinternals.commonlib.ui.binviewer.JRowViewer;

/**
 *
 * @author Amos Shi
 * @since JDK 6.0
 */
public class JBinaryViewer extends JPanel {

    private static final long serialVersionUID = 4876543219876500005L;
    public static final Font FONT = new Font(Font.DIALOG_INPUT, Font.PLAIN, 14);
    public static final int ITEM_HEIGHT = 22; // 20;
    public static final int ROW_ITEM_MAX = 16;
    public static final int ROW_ITEM_MAX_INDEX = ROW_ITEM_MAX - 1;
    private final JRowViewer rowViewer;
    private final JRawDataViewer rawViewer;
    private final JAsciiDataViewer asciiViewer;
    private byte[] data = null;
    private static final int ROW_EMPTYROW_COUNT = 10;
    private JScrollBar vBar;
    private int rowMax;
    private int selectedStartIndex = 0;
    private int selectedLength = 0;

    public JBinaryViewer() {
        this.setLayout(new BorderLayout());
        // this.setFont(JBinaryViewer.FONT);
        this.addComponentListener(new ComponentResizedAdapter());
        this.addMouseWheelListener(new MouseWheelAdapter());

        // Vertical Bar
        this.vBar = new JScrollBar();
        this.vBar.addAdjustmentListener(new AdjustmentListener() {

            public void adjustmentValueChanged(AdjustmentEvent e) {
                updateViewContent();
            }
        });
        this.vBar.setVisible(false);

        this.add(this.vBar, BorderLayout.EAST);

        // Content Panel
        final JPanel panel = new JPanel();
        final SpringLayout panelLayout = new SpringLayout();
        int left, right;

        panel.setLayout(panelLayout);

        this.rowViewer = new JRowViewer();
        this.rawViewer = new JRawDataViewer();
        this.rawViewer.addKeyListener(new KeyboardAdapter());
        this.asciiViewer = new JAsciiDataViewer();
        this.asciiViewer.addKeyListener(new KeyboardAdapter());

        panel.add(this.rowViewer);
        panel.add(this.rawViewer);
        panel.add(this.asciiViewer);

        panelLayout.putConstraint(SpringLayout.WEST, this.rowViewer, 2, SpringLayout.WEST, panel);
        panelLayout.putConstraint(SpringLayout.NORTH, this.rowViewer, 2, SpringLayout.NORTH, panel);
        panelLayout.putConstraint(SpringLayout.SOUTH, this.rowViewer, -4, SpringLayout.SOUTH, panel);
        panelLayout.putConstraint(SpringLayout.EAST, this.rowViewer, JRowViewer.WIDTH_VALUE, SpringLayout.WEST, panel);

        left = 2 + JRowViewer.WIDTH_VALUE + 2;
        right = left + JRawDataViewer.WIDTH_VALUE;
        panelLayout.putConstraint(SpringLayout.WEST, this.rawViewer, left, SpringLayout.WEST, panel);
        panelLayout.putConstraint(SpringLayout.NORTH, this.rawViewer, 2, SpringLayout.NORTH, panel);
        panelLayout.putConstraint(SpringLayout.SOUTH, this.rawViewer, -4, SpringLayout.SOUTH, panel);
        panelLayout.putConstraint(SpringLayout.EAST, this.rawViewer, right, SpringLayout.WEST, panel);

        left = right + 2;
        right = left + JAsciiDataViewer.WIDTH_VALUE;
        panelLayout.putConstraint(SpringLayout.WEST, this.asciiViewer, left, SpringLayout.WEST, panel);
        panelLayout.putConstraint(SpringLayout.NORTH, this.asciiViewer, 2, SpringLayout.NORTH, panel);
        panelLayout.putConstraint(SpringLayout.SOUTH, this.asciiViewer, -4, SpringLayout.SOUTH, panel);
        panelLayout.putConstraint(SpringLayout.EAST, this.asciiViewer, right, SpringLayout.WEST, panel);

        this.add(panel, BorderLayout.CENTER);
    }

    public void setData(final byte[] data) {
        if (data == null) {
            return;
        }

        this.data = data.clone();

        // Calc the max row count
        this.rowMax = this.getRowMax();
        this.vBar.setMaximum(this.rowMax + JBinaryViewer.ROW_EMPTYROW_COUNT);
        this.vBar.setValue(0);

        this.updateViewContent();
    }

    private int getRowMax() {
        return (this.data != null)
                ? this.getRowCount(this.data.length)
                : 0;
    }

    /**
     * Return 1-based row count number.
     */
    private int getRowCount(int number) {
        int count = 0;
        while (number > 0) {
            count++;
            number -= ROW_ITEM_MAX;
        }
        return count;
    }

    private int getExtent() {
        return (int) Math.ceil(this.getSize().getHeight() / ITEM_HEIGHT);
    }

    private void updateViewContent() {
        // Update Extent
        int extent = this.getExtent();
        if (extent <= 0) {
            return;                                                             // The window Hight is (nearly) zero
        }
        this.vBar.setVisibleAmount(extent);

        if ((extent + this.vBar.getValue()) > (this.rowMax + JBinaryViewer.ROW_EMPTYROW_COUNT)) {
            int diff = (this.rowMax + JBinaryViewer.ROW_EMPTYROW_COUNT) - extent;
            diff = (diff > 0) ? diff : 0;
            this.vBar.setValue(diff);
        }

        if (extent > this.rowMax + JBinaryViewer.ROW_EMPTYROW_COUNT) {
            this.vBar.setVisible(false);
        } else {
            this.vBar.setVisible(true);
        }

        // Revise row viewer, raw data viewer, ASCII data viewer
        this.rowViewer.setData(this.vBar.getValue(), extent, this.rowMax);
        if (this.data != null && this.data.length > 0) {
            // Calc the buffer data
            byte[] buf = null;
            int startPos = this.vBar.getValue() * JBinaryViewer.ROW_ITEM_MAX;
            int dataSize = extent * JBinaryViewer.ROW_ITEM_MAX;

            dataSize = Math.min(dataSize, this.data.length - startPos);

            if (dataSize > 0) {
                buf = new byte[dataSize];
                System.arraycopy(this.data, startPos, buf, 0, dataSize);
            }

            // Set the buffer
            this.rawViewer.setData(buf);
            this.asciiViewer.setData(buf);
        }

        // Revise selection
        this.updateSelection();
    }

    /**
     * Selects the bytes between the specified start position and length.
     *
     * @param selectionStart the start position of the bytes
     * @param length         the length of the bytes
     */
    public void setSelection(final int selectionStart, final int length) {
        if ((this.data == null) || (selectionStart < 0)) {
            return;
        }
        if (this.data.length < (selectionStart + length - 1)) {
            return;
        }

        this.selectedStartIndex = selectionStart;
        this.selectedLength = length;

        this.ensureVisible(selectionStart);
        this.updateSelection();
    }

    private void updateSelection() {
        final int startPos = this.selectedStartIndex - this.vBar.getValue() * JBinaryViewer.ROW_ITEM_MAX;
        final int lengtgMax = this.getExtent() * JBinaryViewer.ROW_ITEM_MAX;
        int length;

        if (startPos > 0) {
            length =  Math.min(this.selectedLength, lengtgMax);                 // Improve Performance
            this.rawViewer.setSelection(startPos, length);
            this.asciiViewer.setSelection(startPos, length);
        } else if ((length = startPos + this.selectedLength) > 0) {
            length =  Math.min(length, lengtgMax);                              // Improve Performance
            this.rawViewer.setSelection(0, length);
            this.asciiViewer.setSelection(0, length);
        } else {
            this.rawViewer.setSelection(0, 0);
            this.asciiViewer.setSelection(0, 0);
        }
    }

    /**
     * Ensure the byte at <code>startPos</code> is visible.
     *
     * @param startPos the start position of the byte(s) to be visible
     */
    private void ensureVisible(final int startPos) {
        if ((this.data == null) || (startPos < 0) || (this.data.length < (startPos - 1))) {
            return;
        }

        int rowId = this.getRowCount(startPos) - 1;
        int low = this.vBar.getValue();
        int high = low + this.getExtent() - 1;
        if (rowId < low || rowId > high) {
            this.vBar.setValue(rowId);
        }
    }

    @SuppressWarnings("PackageVisibleInnerClass")
    class ComponentResizedAdapter extends ComponentAdapter {

        @Override
        public void componentResized(ComponentEvent e) {
            super.componentResized(e);
            updateViewContent();
        }
    }

    @SuppressWarnings("PackageVisibleInnerClass")
    class MouseWheelAdapter implements MouseWheelListener {

        public void mouseWheelMoved(MouseWheelEvent e) {
            switch(e.getScrollType()){
                case MouseWheelEvent.WHEEL_UNIT_SCROLL:
                    JBinaryViewer.this.vBar.setValue(
                            JBinaryViewer.this.vBar.getValue()
                            + e.getUnitsToScroll());
                    break;
                case MouseWheelEvent.WHEEL_BLOCK_SCROLL:
                    break;
                default:
                    break;
            }
        }
    }

    @SuppressWarnings("PackageVisibleInnerClass")
    class KeyboardAdapter implements KeyListener {

        public void keyTyped(KeyEvent e) {
        }

        public void keyPressed(KeyEvent e) {

            switch(e.getKeyCode()){
                case KeyEvent.VK_HOME:
                    JBinaryViewer.this.vBar.setValue(JBinaryViewer.this.vBar.getMinimum());
                    break;

                case KeyEvent.VK_END:
                    JBinaryViewer.this.vBar.setValue(JBinaryViewer.this.vBar.getMaximum());
                    break;

                case KeyEvent.VK_UP:
                    JBinaryViewer.this.vBar.setValue(JBinaryViewer.this.vBar.getValue() - 1);
                    break;
                case KeyEvent.VK_DOWN:
                    JBinaryViewer.this.vBar.setValue(JBinaryViewer.this.vBar.getValue() + 1);
                    break;

                case KeyEvent.VK_LEFT:
                case KeyEvent.VK_PAGE_UP:
                    JBinaryViewer.this.vBar.setValue(JBinaryViewer.this.vBar.getValue() - JBinaryViewer.this.vBar.getVisibleAmount());
                    break;

                case KeyEvent.VK_RIGHT:
                case KeyEvent.VK_PAGE_DOWN:
                    JBinaryViewer.this.vBar.setValue(JBinaryViewer.this.vBar.getValue() + JBinaryViewer.this.vBar.getVisibleAmount());
                    break;

                default:
                    break;
            }
        }

        public void keyReleased(KeyEvent e) {
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer
United States United States
Deliver useful software to the world.

Comments and Discussions