📄 bytecodedisplay.java
字号:
/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the license, or (at your option) any later version.
*/
package ee.ioc.cs.jbe.browser.detail.attributes.code;
import org.gjt.jclasslib.bytecode.*;
import org.gjt.jclasslib.io.ByteCodeReader;
import org.gjt.jclasslib.structures.ClassFile;
import org.gjt.jclasslib.structures.InvalidByteCodeException;
import org.gjt.jclasslib.structures.attributes.CodeAttribute;
import ee.ioc.cs.jbe.browser.*;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.font.*;
import java.io.IOException;
import java.text.AttributedString;
import java.util.*;
/**
Bytecode renderer.
@author <a href="mailto:jclasslib@ej-technologies.com">Ingo Kegel</a>
@version $Revision: 1.2 $ $Date: 2006/09/04 15:43:18 $
*/
public class ByteCodeDisplay extends JPanel implements Scrollable {
/** Horizontal margin. */
public static final int MARGIN_X = 3;
/** Vertical margin. */
public static final int MARGIN_Y = 3;
/** Border for the renderer. */
public static final Border BORDER = new EmptyBorder(MARGIN_Y, MARGIN_X, MARGIN_Y, MARGIN_X);
private static Map<TextAttribute, Object> STYLE_BASE;
private static Map<TextAttribute, Object> STYLE_NORMAL;
private static Map<TextAttribute, Object> STYLE_SMALL;
private static Map<TextAttribute, Object> STYLE_LINK;
private static Map<TextAttribute, Object> STYLE_OFFSET;
private static Map<TextAttribute, Object> STYLE_INSTRUCTION;
private static Map<TextAttribute, Object> STYLE_IMMEDIATE_VALUE;
private static final String TAB_STRING = " ";
static {
initStyles(null);
}
public static void initStyles(Font baseFont) {
STYLE_BASE = new HashMap<TextAttribute, Object>(2);
if (baseFont != null) {
STYLE_BASE.put(TextAttribute.FAMILY, baseFont.getFamily());
} else {
baseFont = UIManager.getFont("TextArea.font");
STYLE_BASE.put(TextAttribute.FAMILY, "MonoSpaced");
}
STYLE_BASE.put(TextAttribute.SIZE, new Float(baseFont.getSize()));
STYLE_NORMAL = new HashMap<TextAttribute, Object>(0);
STYLE_SMALL = new HashMap<TextAttribute, Object>(1);
STYLE_SMALL.put(TextAttribute.SIZE, new Float(baseFont.getSize() - 1));
STYLE_LINK = new HashMap<TextAttribute, Object>(3);
STYLE_LINK.put(TextAttribute.FOREGROUND, new Color(0, 128, 0));
STYLE_LINK.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
STYLE_LINK.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
STYLE_OFFSET = new HashMap<TextAttribute, Object>(1);
STYLE_OFFSET.put(TextAttribute.FOREGROUND, new Color(128, 0, 0));
STYLE_INSTRUCTION = new HashMap<TextAttribute, Object>(1);
STYLE_INSTRUCTION.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
STYLE_IMMEDIATE_VALUE = new HashMap<TextAttribute, Object>(2);
STYLE_IMMEDIATE_VALUE.put(TextAttribute.FOREGROUND, Color.magenta);
STYLE_IMMEDIATE_VALUE.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
}
private ByteCodeDetailPane detailPane;
private CodeAttribute codeAttribute;
private ClassFile classFile;
private int offsetWidth;
private String offsetBlank;
private HashMap<Integer, Integer> offsetToLine = new HashMap<Integer, Integer>();
private ArrayList<AttributedString> lines = new ArrayList<AttributedString>();
private ArrayList<String> textLines = new ArrayList<String>();
private TextLayout[] textLayouts;
private Map<Integer, BytecodeLink> lineToLink = new HashMap<Integer, BytecodeLink>();
private LinkedList<LineCacheEntry> currentLineCache = new LinkedList<LineCacheEntry>();
private FontRenderContext frc;
private float currentHeight;
private float currentWidth;
private int lineHeight;
private int ascent;
private int characterWidth;
/**
* Get the left-padded value for a number.
* @param number the number
* @param width the total width.
* @return the padded string.
*/
public static String getPaddedValue(int number, int width) {
StringBuffer buffer = new StringBuffer();
String value = String.valueOf(number);
int valueLength = value.length();
for (int i = valueLength; i < width; i++) {
buffer.append(' ');
}
buffer.append(value);
return buffer.toString();
}
/**
* Constructor.
* @param detailPane the parent detail pane.
*/
public ByteCodeDisplay(ByteCodeDetailPane detailPane) {
this.detailPane = detailPane;
setupComponent();
setupEventHandlers();
}
// Scrollable
public Dimension getPreferredScrollableViewportSize() {
return null;
}
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
if (orientation == SwingConstants.HORIZONTAL) {
return 10;
} else {
if (lineHeight == 0) {
return 1;
}
int currentY = ((JViewport)getParent()).getViewPosition().y;
float line = 1f * (currentY - MARGIN_Y) / lineHeight;
int targetLine = (int)(direction < 0 ? Math.floor(line) - 1: Math.ceil(line) + 1);
int targetY = MARGIN_Y + targetLine * lineHeight + 1;
return Math.abs(currentY - targetY);
}
}
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
JViewport viewport = (JViewport)getParent();
if (orientation == SwingConstants.HORIZONTAL) {
return viewport.getWidth();
} else {
if (lineHeight == 0) {
return 1;
}
int currentY = viewport.getViewPosition().y;
int rawTargetY = currentY + (direction < 0 ? -1 : 1) * viewport.getHeight();
float line = 1f * (rawTargetY - MARGIN_Y) / lineHeight;
int targetLine = (int)(direction < 0 ? Math.ceil(line): Math.floor(line));
int targetY = MARGIN_Y + targetLine * lineHeight + 1;
return Math.abs(currentY - targetY);
}
}
public boolean getScrollableTracksViewportWidth() {
return false;
}
public boolean getScrollableTracksViewportHeight() {
return false;
}
// end Scrollable
/**
* Get the currently displayed code attribute.
* @return the code attribute.
*/
public CodeAttribute getCodeAttribute() {
return codeAttribute;
}
/**
* Get the current line count.
* @return the line count.
*/
public int getLineCount() {
return lines.size();
}
/**
* Get the current line height.
* @return the line height.
*/
public int getLineHeight() {
return lineHeight;
}
/**
* Get the curent line ascent.
* @return the line ascent.
*/
public int getAscent() {
return ascent;
}
/**
* Set the code attribute that is to be displayed.
* @param codeAttribute the code attribute.
* @param classFile the class file of the code attribute.
*/
public void setCodeAttribute(CodeAttribute codeAttribute, ClassFile classFile) {
this.codeAttribute = codeAttribute;
this.classFile = classFile;
frc = ((Graphics2D)getGraphics()).getFontRenderContext();
setupTextLayouts();
invalidate();
}
/**
* Perform a link operation at a given point. Does nothing if there is no
* link at this point.
* @param point the point.
*/
public void link(Point point) {
BytecodeLink link = getLink(point);
if (link == null) {
return;
}
updateHistory(link.sourceOffset);
if (link instanceof ConstantPoolLink) {
ConstantPoolHyperlinkListener.link(detailPane.getBrowserServices(), ((ConstantPoolLink)link).cpIndex);
} else if (link instanceof OffsetLink) {
int targetOffset = ((OffsetLink)link).targetOffset;
scrollToOffset(targetOffset);
updateHistory(targetOffset);
}
}
/**
* Returns whether there is a link below the given point.
* @param point the point.
* @return the value.
*/
public boolean isLink(Point point) {
return getLink(point) != null;
}
/**
* Scroll the view to a given bytecode offset.
* @param offset the bytecode offset.
*/
public void scrollToOffset(int offset) {
Integer line = (Integer)offsetToLine.get(new Integer(offset));
if (line == null) {
return;
}
Rectangle target = new Rectangle(0, line.intValue() * lineHeight + MARGIN_Y + 1, 10, getParent().getHeight());
scrollRectToVisible(target);
}
/**
* Copy the view text to the clipboard.
*/
public void copyToClipboard() {
StringBuffer buffer = new StringBuffer();
Iterator it = textLines.iterator();
while (it.hasNext()) {
String line = (String)it.next();
buffer.append(line);
buffer.append('\n');
}
StringSelection stringSelection = new StringSelection(buffer.toString());
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(stringSelection, stringSelection);
}
protected void paintComponent(Graphics graphics) {
if (lineHeight == 0) {
return;
}
Graphics2D g = (Graphics2D)graphics;
g.translate(MARGIN_X, MARGIN_Y);
Rectangle clipBounds = graphics.getClipBounds();
Paint oldPaint = g.getPaint();
g.setPaint(Color.WHITE);
g.fill(clipBounds);
g.setPaint(oldPaint);
int startLine = Math.max(0, clipBounds.y / lineHeight - 1);
int endLine = Math.min(lines.size(), (clipBounds.y + clipBounds.height) / lineHeight + 1);
for (int i = startLine; i < endLine; i++) {
TextLayout textLayout = getOrCreateTextLayout(i);
textLayout.draw(g, 0, i * lineHeight + textLayout.getAscent());
}
g.translate(-MARGIN_X, -MARGIN_Y);
}
private TextLayout getOrCreateTextLayout(int i) {
TextLayout textLayout = textLayouts[i];
if (textLayout == null) {
textLayout = textLayouts[i] = new TextLayout(((AttributedString)lines.get(i)).getIterator(), frc);
}
return textLayout;
}
private void setupComponent() {
setBorder(BORDER);
setDoubleBuffered(false);
setOpaque(false);
}
private void setupEventHandlers() {
}
private BytecodeLink getLink(Point point) {
if (lineHeight == 0) {
return null;
}
int x = point.x - MARGIN_X;
int y = point.y - MARGIN_Y;
int line = y / lineHeight;
BytecodeLink link = (BytecodeLink)lineToLink.get(new Integer(line));
if (link == null) {
return null;
}
TextLayout textLayout = getOrCreateTextLayout(line);
TextHitInfo textHitInfo = textLayout.hitTestChar(x, y - line * lineHeight);
int charIndex = textHitInfo.getCharIndex();
if (charIndex >= link.startCharIndex && charIndex < link.endCharIndex) {
return link;
} else {
return null;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -