Start of token parsing.

This commit is contained in:
Cody 2024-09-05 17:51:09 -06:00
parent 65d11ab47b
commit db26cd2758

AI 샘플 코드 생성 중입니다

Loading...
12 changed files with 2707 additions and 316 deletions

10
pom.xml
View File

@ -372,6 +372,16 @@
<artifactId>org.abego.treelayout.core</artifactId>
<version>${treelayout.version}</version>
</dependency>
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-core</artifactId>
<version>3.26.1</version>
</dependency>
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-symbol-solver-core</artifactId>
<version>3.26.1</version>
</dependency>
<!-- TODO Re-add for Graal.JS support -->
<!--<dependency>

View File

@ -0,0 +1,359 @@
/*
* BSD 3-Clause "New" or "Revised" License
*
* Copyright (c) 2021, Robert Futrell All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the
* following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package the.bytecode.club.bytecodeviewer.gui.components;
import org.fife.ui.rsyntaxtextarea.DocumentRange;
import org.fife.ui.rsyntaxtextarea.ErrorStrip;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
import org.fife.ui.rsyntaxtextarea.parser.Parser;
import org.fife.ui.rsyntaxtextarea.parser.ParserNotice;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.text.BadLocationException;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* This is based on {@link ErrorStrip}, but with our own implementations to work with how occurrences are marked on
* the text area.
* <p>
* Created by Bl3nd.
* Date: 8/26/2024
*/
public class MyErrorStripe extends JPanel {
private final RSyntaxTextArea textArea;
private final transient Listener listener;
public MyErrorStripe(RSyntaxTextArea textArea) {
this.textArea = textArea;
setLayout(null);
listener = new Listener();
addMouseListener(listener);
}
private int lineToY(int line, Rectangle r) {
if (r == null)
r = new Rectangle();
textArea.computeVisibleRect(r);
int h = r.height;
float lineCount = textArea.getLineCount();
int lineHeight = textArea.getLineHeight();
int linesPerVisibleRect = h / lineHeight;
return Math.round((h - 1) * line / Math.max(lineCount, linesPerVisibleRect));
}
private int yToLine(int y) {
int line = -1;
int h = textArea.getVisibleRect().height;
int lineHeight = textArea.getLineHeight();
int linesPerVisibleRect = h / lineHeight;
int lineCount = textArea.getLineCount();
if (y < h) {
float at = y / (float) h;
line = Math.round((Math.max(lineCount, linesPerVisibleRect) - 1) * at);
}
return line;
}
private void paintParserNoticeMarker(Graphics2D g, ParserNotice notice, int width, int height) {
Color borderColor = notice.getColor();
if (borderColor == null)
borderColor = Color.BLACK;
Color fillColor = borderColor.brighter();
g.setColor(fillColor);
g.fillRect(0, 0, width, height);
g.setColor(borderColor);
g.drawRect(0, 0, width - 1, height - 1);
}
public void refreshMarkers() {
removeAll();
Map<Integer, Marker> markerMap = new HashMap<>();
List<DocumentRange> occurrences = textArea.getMarkedOccurrences();
addMarkersForRanges(occurrences, markerMap, textArea.getMarkOccurrencesColor());
revalidate();
repaint();
}
private void addMarkersForRanges(List<DocumentRange> occurrences, Map<Integer, Marker> markerMap, Color color) {
for (DocumentRange range : occurrences) {
int line;
try {
line = textArea.getLineOfOffset(range.getStartOffset());
} catch (BadLocationException e) {
continue;
}
ParserNotice notice = new MarkedOccurrenceNotice(range, color);
Integer key = line;
Marker m = markerMap.get(key);
if (m == null) {
m = new Marker(notice);
m.addMouseListener(listener);
markerMap.put(key, m);
add(m);
} else {
if (!m.containsMarkedOccurrence())
m.addNotice(notice);
}
}
}
@Override
public void updateUI() {
super.updateUI();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
}
@Override
protected void paintChildren(Graphics g) {
super.paintChildren(g);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(14, textArea.getPreferredScrollableViewportSize().height);
}
@Override
public void doLayout() {
for (int i = 0; i < getComponentCount(); i++) {
Marker m = (Marker) getComponent(i);
m.updateLocation();
}
}
@Override
public void addNotify() {
super.addNotify();
refreshMarkers();
}
@Override
public void removeNotify() {
super.removeNotify();
}
private class Listener extends MouseAdapter {
private final Rectangle r = new Rectangle();
@Override
public void mouseClicked(@NotNull MouseEvent e) {
Component source = (Component) e.getSource();
if (source instanceof MyErrorStripe.Marker) {
Marker m = (Marker) source;
m.mouseClicked(e);
return;
}
int line = yToLine(e.getY());
if (line > -1) {
try {
int offset = textArea.getLineOfOffset(line);
textArea.setCaretPosition(offset);
RSyntaxUtilities.selectAndPossiblyCenter(textArea, new DocumentRange(offset, offset), false);
} catch (BadLocationException exception) {
UIManager.getLookAndFeel().provideErrorFeedback(textArea);
}
}
}
}
private class MarkedOccurrenceNotice implements ParserNotice {
private final DocumentRange range;
private final Color color;
MarkedOccurrenceNotice(DocumentRange range, Color color) {
this.range = range;
this.color = color;
}
@Override
public boolean containsPosition(int pos) {
return pos >= range.getStartOffset() && pos < range.getEndOffset();
}
@Override
public Color getColor() {
return color;
}
@Override
public int getLength() {
return range.getEndOffset() - range.getStartOffset();
}
@Override
public Level getLevel() {
return Level.INFO;
}
@Override
public int getLine() {
try {
return textArea.getLineOfOffset(range.getStartOffset()) + 1;
} catch (BadLocationException e) {
return 0;
}
}
@Override
public boolean getKnowsOffsetAndLength() {
return false;
}
@Contract(pure = true)
@Override
public @NotNull String getMessage() {
return "";
}
@Override
public int getOffset() {
return range.getStartOffset();
}
@Override
public Parser getParser() {
return null;
}
@Override
public boolean getShowInEditor() {
return false;
}
@Override
public String getToolTipText() {
return null;
}
@Override
public int compareTo(@NotNull ParserNotice o) {
return 0;
}
@Override
public int hashCode() {
return 0;
}
}
private static final int MARKER_HEIGHT = 3;
private class Marker extends JComponent {
private final java.util.List<ParserNotice> notices;
Marker(ParserNotice notice) {
notices = new ArrayList<>();
addNotice(notice);
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
setSize(getPreferredSize());
}
private void addNotice(ParserNotice notice) {
notices.add(notice);
}
@Contract(value = " -> new", pure = true)
@Override
public @NotNull Dimension getPreferredSize() {
return new Dimension(12, MARKER_HEIGHT);
}
@Override
protected void paintComponent(Graphics g) {
final ParserNotice notice = getHighestPriorityNotice();
if (notice != null)
paintParserNoticeMarker((Graphics2D) g, notice, getWidth(), getHeight());
}
protected void mouseClicked(MouseEvent e) {
ParserNotice pn = notices.get(0);
int offs = pn.getOffset();
int len = pn.getLength();
if (offs > -1 && len > -1) // These values are optional
{
DocumentRange range = new DocumentRange(offs, offs + len);
RSyntaxUtilities.selectAndPossiblyCenter(textArea, range, true);
} else {
int line = pn.getLine();
try {
offs = textArea.getLineStartOffset(line);
textArea.getFoldManager().ensureOffsetNotInClosedFold(offs);
textArea.setCaretPosition(offs);
} catch (BadLocationException ble) // Never happens
{
UIManager.getLookAndFeel().provideErrorFeedback(textArea);
}
}
}
public boolean containsMarkedOccurrence() {
boolean result = false;
for (ParserNotice notice : notices) {
if (notice instanceof MarkedOccurrenceNotice) {
result = true;
break;
}
}
return result;
}
public ParserNotice getHighestPriorityNotice() {
ParserNotice selectedNotice = null;
int lowestLevel = Integer.MAX_VALUE;
for (ParserNotice notice : notices) {
if (notice.getLevel().getNumericValue() < lowestLevel) {
lowestLevel = notice.getLevel().getNumericValue();
selectedNotice = notice;
}
}
return selectedNotice;
}
public void updateLocation() {
int line = notices.get(0).getLine();
int y = lineToY(line - 1, null);
setLocation(2, y);
}
}
}

View File

@ -0,0 +1,132 @@
/*
* BSD 3-Clause "New" or "Revised" License
*
* Copyright (c) 2021, Robert Futrell All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the
* following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package the.bytecode.club.bytecodeviewer.gui.components;
import org.fife.ui.rsyntaxtextarea.DocumentRange;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaHighlighter;
import org.fife.ui.rsyntaxtextarea.parser.ParserNotice;
import org.fife.ui.rtextarea.SmartHighlightPainter;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.plaf.TextUI;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.text.View;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
/**
* Extension from RSyntaxTextArea
*/
public class RSyntaxTextAreaHighlighterEx extends RSyntaxTextAreaHighlighter
{
private final List<SyntaxLayeredHighlightInfoImpl> markedOccurrences = new ArrayList<>();
private static final Color DEFAULT_PARSER_NOTICE_COLOR = Color.RED;
public Object addMarkedOccurrenceHighlight(int start, int end, @NotNull SmartHighlightPainter p) throws BadLocationException
{
Document doc = textArea.getDocument();
TextUI mapper = textArea.getUI();
// Always layered highlights for marked occurrences.
SyntaxLayeredHighlightInfoImpl i = new SyntaxLayeredHighlightInfoImpl();
p.setPaint(UIManager.getColor("ScrollBar.thumb"));
i.setPainter(p);
i.setStartOffset(doc.createPosition(start));
// HACK: Use "end-1" to prevent chars the user types at the "end" of
// the highlight to be absorbed into the highlight (default Highlight
// behavior).
i.setEndOffset(doc.createPosition(end - 1));
markedOccurrences.add(i);
mapper.damageRange(textArea, start, end);
return i;
}
@Override
public List<DocumentRange> getMarkedOccurrences()
{
List<DocumentRange> list = new ArrayList<>(markedOccurrences.size());
for (HighlightInfo info : markedOccurrences)
{
int start = info.getStartOffset();
int end = info.getEndOffset() + 1; // HACK
if (start <= end)
{
// Occasionally a Marked Occurrence can have a lost end offset
// but not start offset (replacing entire text content with
// new content, and a marked occurrence is on the last token
// in the document).
DocumentRange range = new DocumentRange(start, end);
list.add(range);
}
}
return list;
}
public void clearMarkOccurrencesHighlights()
{
// Don't remove via an iterator; since our List is an ArrayList, this
// implies tons of System.arrayCopy()s
for (HighlightInfo info : markedOccurrences)
{
repaintListHighlight(info);
}
markedOccurrences.clear();
}
@Override
public void paintLayeredHighlights(Graphics g, int lineStart, int lineEnd, Shape viewBounds, JTextComponent editor, View view)
{
paintListLayered(g, lineStart, lineEnd, viewBounds, editor, view, markedOccurrences);
super.paintLayeredHighlights(g, lineStart, lineEnd, viewBounds, editor, view);
}
private static class SyntaxLayeredHighlightInfoImpl extends LayeredHighlightInfoImpl
{
private ParserNotice notice;
@Override
public Color getColor()
{
Color color = null;
if (notice != null)
{
color = notice.getColor();
if (color == null)
color = DEFAULT_PARSER_NOTICE_COLOR;
}
return color;
}
@Override
public String toString()
{
return "[SyntaxLayeredHighlightInfoImpl: startOffs=" + getStartOffset() + ", endOffs=" + getEndOffset() + ", color=" + getColor() + "]";
}
}
}

View File

@ -27,6 +27,7 @@ import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.BytecodeViewPanel;
import the.bytecode.club.bytecodeviewer.resources.Resource;
import the.bytecode.club.bytecodeviewer.resources.ResourceContainer;
import the.bytecode.club.bytecodeviewer.resources.classcontainer.ClassFileContainer;
import the.bytecode.club.bytecodeviewer.util.MethodParser;
import javax.swing.*;
@ -37,6 +38,7 @@ import java.awt.event.ComponentEvent;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import static the.bytecode.club.bytecodeviewer.util.MethodParser.Method;
@ -57,6 +59,7 @@ public class ClassViewer extends ResourceViewer
public BytecodeViewPanel bytecodeViewPanel2 = new BytecodeViewPanel(1, this);
public BytecodeViewPanel bytecodeViewPanel3 = new BytecodeViewPanel(2, this);
public List<MethodParser> methods = Arrays.asList(new MethodParser(), new MethodParser(), new MethodParser());
public HashMap<String, ClassFileContainer> classFiles = new HashMap<>();
public ClassViewer(ResourceContainer container, String name)
{

View File

@ -18,7 +18,7 @@
package the.bytecode.club.bytecodeviewer.gui.util;
import java.awt.BorderLayout;
import java.awt.*;
import java.util.Objects;
import java.util.regex.Matcher;
import javax.swing.JButton;
@ -30,21 +30,26 @@ import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
import org.fife.ui.rsyntaxtextarea.*;
import org.fife.ui.rtextarea.SmartHighlightPainter;
import org.objectweb.asm.ClassWriter;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.compilers.Compiler;
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import the.bytecode.club.bytecodeviewer.gui.components.MethodsRenderer;
import the.bytecode.club.bytecodeviewer.gui.components.MyErrorStripe;
import the.bytecode.club.bytecodeviewer.gui.components.RSyntaxTextAreaHighlighterEx;
import the.bytecode.club.bytecodeviewer.gui.components.SearchableRSyntaxTextArea;
import the.bytecode.club.bytecodeviewer.gui.hexviewer.HexViewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.BytecodeViewPanel;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.resources.classcontainer.ClassFileContainer;
import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassFieldLocation;
import the.bytecode.club.bytecodeviewer.resources.classcontainer.parser.TokenUtil;
import the.bytecode.club.bytecodeviewer.util.MethodParser;
import static the.bytecode.club.bytecodeviewer.gui.resourceviewer.TabbedPane.BLANK_COLOR;
@ -61,326 +66,443 @@ import static the.bytecode.club.bytecodeviewer.translation.TranslatedStrings.EDI
public class BytecodeViewPanelUpdater implements Runnable
{
public final ClassViewer viewer;
public final BytecodeViewPanel bytecodeViewPanel;
private final JButton button;
private final byte[] classBytes;
public SearchableRSyntaxTextArea updateUpdaterTextArea;
public JComboBox<Integer> methodsList;
public boolean isPanelEditable;
public boolean waitingFor;
private Thread thread;
public BytecodeViewPanelUpdater(BytecodeViewPanel bytecodeViewPanel, ClassViewer cv, byte[] classBytes, boolean isPanelEditable, JButton button)
{
this.viewer = cv;
this.bytecodeViewPanel = bytecodeViewPanel;
this.classBytes = classBytes;
this.isPanelEditable = isPanelEditable;
this.button = button;
waitingFor = true;
}
public void processDisplay()
{
try
{
BytecodeViewer.updateBusyStatus(true);
if (bytecodeViewPanel.decompiler != Decompiler.NONE)
{
//hex viewer
if (bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER)
{
final ClassWriter cw = new ClassWriter(0);
viewer.resource.getResourceClassNode().accept(cw);
SwingUtilities.invokeLater(() ->
{
final HexViewer hex = new HexViewer(cw.toByteArray());
bytecodeViewPanel.add(hex);
});
}
else
{
final Decompiler decompiler = bytecodeViewPanel.decompiler;
//perform decompiling inside of this thread
final String decompiledSource = decompiler.getDecompiler().decompileClassNode(viewer.resource.getResourceClassNode(), classBytes);
//set the swing components on the swing thread
SwingUtilities.invokeLater(() ->
{
buildTextArea(decompiler, decompiledSource);
waitingFor = false;
});
//hold this thread until the swing thread has finished attaching the components
while (waitingFor)
{
try {
Thread.sleep(1);
} catch (Exception ignored) {}
}
}
}
}
catch (IndexOutOfBoundsException | NullPointerException e)
{
//ignore
}
catch (Exception e)
{
BytecodeViewer.handleException(e);
}
finally
{
viewer.resetDivider();
BytecodeViewer.updateBusyStatus(false);
SwingUtilities.invokeLater(() ->
{
if (button != null)
button.setEnabled(true);
});
}
}
public void startNewThread()
{
thread = new Thread(this, "Pane Update");
thread.start();
}
public final ClassViewer viewer;
public final BytecodeViewPanel bytecodeViewPanel;
private final JButton button;
private final byte[] classBytes;
@Override
public void run()
{
if(bytecodeViewPanel.decompiler == Decompiler.NONE)
return;
processDisplay();
if(bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER)
return;
//nullcheck broken pane
if(updateUpdaterTextArea == null || updateUpdaterTextArea.getScrollPane() == null
|| updateUpdaterTextArea.getScrollPane().getViewport() == null)
{
//build an error message
SwingUtilities.invokeLater(() ->
buildTextArea(bytecodeViewPanel.decompiler, "Critical BCV Error"));
return;
}
//this still freezes the swing UI
synchronizePane();
}
public SearchableRSyntaxTextArea updateUpdaterTextArea;
public JComboBox<Integer> methodsList;
public boolean isPanelEditable;
public boolean waitingFor;
private Thread thread;
public final CaretListener caretListener = new CaretListener()
{
@Override
public void caretUpdate(CaretEvent e)
{
MethodParser methods = viewer.methods.get(bytecodeViewPanel.panelIndex);
if (methods != null)
{
int methodLine = methods.findActiveMethod(updateUpdaterTextArea.getCaretLineNumber());
if (methodLine != -1) {
if (BytecodeViewer.viewer.showClassMethods.isSelected()) {
if (methodsList != null) {
if (methodLine != (int) Objects.requireNonNull(methodsList.getSelectedItem())) {
methodsList.setSelectedItem(methodLine);
}
}
}
if (BytecodeViewer.viewer.synchronizedViewing.isSelected()) {
int panes = 2;
if (viewer.bytecodeViewPanel3 != null)
panes = 3;
public BytecodeViewPanelUpdater(BytecodeViewPanel bytecodeViewPanel, ClassViewer cv, byte[] classBytes, boolean isPanelEditable, JButton button)
{
this.viewer = cv;
this.bytecodeViewPanel = bytecodeViewPanel;
this.classBytes = classBytes;
this.isPanelEditable = isPanelEditable;
this.button = button;
waitingFor = true;
}
for (int i = 0; i < panes; i++) {
if (i != bytecodeViewPanel.panelIndex) {
ClassViewer.selectMethod(viewer, i, methods.getMethod(methodLine));
}
}
}
}
}
}
};
public void processDisplay()
{
try
{
BytecodeViewer.updateBusyStatus(true);
public final ChangeListener viewportListener = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
int panes = 2;
if (viewer.bytecodeViewPanel3 != null)
panes = 3;
if (bytecodeViewPanel.decompiler != Decompiler.NONE)
{
//hex viewer
if (bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER)
{
final ClassWriter cw = new ClassWriter(0);
viewer.resource.getResourceClassNode().accept(cw);
if (BytecodeViewer.viewer.synchronizedViewing.isSelected()) {
if (updateUpdaterTextArea.isShowing() && (updateUpdaterTextArea.hasFocus() || updateUpdaterTextArea.getMousePosition() != null)) {
int caretLine = updateUpdaterTextArea.getCaretLineNumber();
int maxViewLine = ClassViewer.getMaxViewLine(updateUpdaterTextArea);
int activeViewLine = ClassViewer.getViewLine(updateUpdaterTextArea);
int activeLine = (activeViewLine == maxViewLine && caretLine > maxViewLine) ? caretLine :
activeViewLine;
int activeLineDelta = -1;
MethodParser.Method activeMethod = null;
MethodParser activeMethods = viewer.methods.get(bytecodeViewPanel.panelIndex);
if (activeMethods != null) {
int activeMethodLine = activeMethods.findActiveMethod(activeLine);
if (activeMethodLine != -1) {
activeLineDelta = activeLine - activeMethodLine;
activeMethod = activeMethods.getMethod(activeMethodLine);
ClassViewer.selectMethod(updateUpdaterTextArea, activeMethodLine);
}
}
for (int i = 0; i < panes; i++) {
if (i != bytecodeViewPanel.panelIndex) {
int setLine = -1;
SwingUtilities.invokeLater(() ->
{
final HexViewer hex = new HexViewer(cw.toByteArray());
bytecodeViewPanel.add(hex);
});
} else
{
final Decompiler decompiler = bytecodeViewPanel.decompiler;
RSyntaxTextArea area = null;
switch (i) {
case 0:
area = viewer.bytecodeViewPanel1.updateThread.updateUpdaterTextArea;
break;
case 1:
area = viewer.bytecodeViewPanel2.updateThread.updateUpdaterTextArea;
break;
case 2:
area = viewer.bytecodeViewPanel3.updateThread.updateUpdaterTextArea;
break;
}
//perform decompiling inside of this thread
final String decompiledSource = decompiler.getDecompiler().decompileClassNode(viewer.resource.getResourceClassNode(), classBytes);
if (area != null) {
if (activeMethod != null && activeLineDelta >= 0) {
MethodParser methods = viewer.methods.get(i);
if (methods != null) {
int methodLine = methods.findMethod(activeMethod);
if (methodLine != -1) {
int viewLine = ClassViewer.getViewLine(area);
if (activeLineDelta != viewLine - methodLine) {
setLine = methodLine + activeLineDelta;
}
}
}
} else if (activeLine != ClassViewer.getViewLine(area)) {
setLine = activeLine;
}
if (setLine >= 0) {
ClassViewer.setViewLine(area, setLine);
}
}
}
}
}
}
}
};
public void synchronizePane()
{
if(bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER
|| bytecodeViewPanel.decompiler == Decompiler.NONE)
return;
SwingUtilities.invokeLater(()->
{
JViewport viewport = updateUpdaterTextArea.getScrollPane().getViewport();
viewport.addChangeListener(viewportListener);
updateUpdaterTextArea.addCaretListener(caretListener);
});
final MethodParser methods = viewer.methods.get(bytecodeViewPanel.panelIndex);
for (int i = 0; i < updateUpdaterTextArea.getLineCount(); i++)
{
String lineText = updateUpdaterTextArea.getLineText(i);
Matcher regexMatcher = MethodParser.regex.matcher(lineText);
if (regexMatcher.find())
{
String methodName = regexMatcher.group("name");
String methodParams = regexMatcher.group("params");
methods.addMethod(i, methodName, methodParams);
}
}
ClassFileContainer container = new ClassFileContainer(viewer.resource.workingName + "-" + decompiler.getDecompilerName(), decompiledSource);
if (!container.hasBeenParsed)
{
container.parse();
viewer.classFiles.put(viewer.resource.workingName + "-" + decompiler.getDecompilerName(), container);
}
//TODO fix this
if (BytecodeViewer.viewer.showClassMethods.isSelected())
{
if (!methods.isEmpty())
{
methodsList = new JComboBox<>();
for (Integer line : methods.getMethodsLines())
methodsList.addItem(line);
methodsList.setRenderer(new MethodsRenderer(this));
methodsList.addActionListener(e ->
{
int line = (int) Objects.requireNonNull(methodsList.getSelectedItem());
//set the swing components on the swing thread
SwingUtilities.invokeLater(() ->
{
buildTextArea(decompiler, decompiledSource);
waitingFor = false;
});
RSyntaxTextArea area = null;
switch (bytecodeViewPanel.panelIndex)
{
case 0:
area = viewer.bytecodeViewPanel1.updateThread.updateUpdaterTextArea;
break;
case 1:
area = viewer.bytecodeViewPanel2.updateThread.updateUpdaterTextArea;
break;
case 2:
area = viewer.bytecodeViewPanel3.updateThread.updateUpdaterTextArea;
break;
}
//hold this thread until the swing thread has finished attaching the components
while (waitingFor)
{
try
{
Thread.sleep(1);
} catch (Exception ignored)
{
}
}
}
}
} catch (IndexOutOfBoundsException | NullPointerException e)
{
//ignore
} catch (Exception e)
{
BytecodeViewer.handleException(e);
} finally
{
viewer.resetDivider();
BytecodeViewer.updateBusyStatus(false);
SwingUtilities.invokeLater(() ->
{
if (button != null)
button.setEnabled(true);
});
}
}
if (area != null)
ClassViewer.selectMethod(area, line);
});
public void startNewThread()
{
thread = new Thread(this, "Pane Update");
thread.start();
}
JPanel panel = new JPanel(new BorderLayout());
panel.add(updateUpdaterTextArea.getScrollPane().getColumnHeader().getComponent(0), BorderLayout.NORTH);
panel.add(methodsList, BorderLayout.SOUTH);
methodsList.setBackground(BLANK_COLOR);
SwingUtilities.invokeLater(()->
{
updateUpdaterTextArea.getScrollPane().getColumnHeader().removeAll();
updateUpdaterTextArea.getScrollPane().getColumnHeader().add(panel);
});
}
}
}
public void buildTextArea(Decompiler decompiler, String decompiledSource)
{
updateUpdaterTextArea = new SearchableRSyntaxTextArea();
Configuration.rstaTheme.apply(updateUpdaterTextArea);
bytecodeViewPanel.add(updateUpdaterTextArea.getScrollPane());
bytecodeViewPanel.add(updateUpdaterTextArea.getTitleHeader(), BorderLayout.NORTH);
bytecodeViewPanel.textArea = updateUpdaterTextArea;
if (bytecodeViewPanel.decompiler != Decompiler.BYTECODE_DISASSEMBLER) {
bytecodeViewPanel.textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
} else {
AbstractTokenMakerFactory tokenMakerFactory = (AbstractTokenMakerFactory) TokenMakerFactory.getDefaultInstance();
tokenMakerFactory.putMapping("text/javaBytecode", "the.bytecode.club.bytecodeviewer.decompilers.bytecode.JavaBytecodeTokenMaker");
bytecodeViewPanel.textArea.setSyntaxEditingStyle("text/javaBytecode");
}
bytecodeViewPanel.textArea.setCodeFoldingEnabled(true);
bytecodeViewPanel.textArea.setAntiAliasingEnabled(true);
bytecodeViewPanel.textArea.setText(decompiledSource);
bytecodeViewPanel.textArea.setCaretPosition(0);
bytecodeViewPanel.textArea.setEditable(isPanelEditable);
if(isPanelEditable && decompiler == Decompiler.SMALI_DISASSEMBLER)
bytecodeViewPanel.compiler = Compiler.SMALI_ASSEMBLER;
else if(isPanelEditable && decompiler == Decompiler.KRAKATAU_DISASSEMBLER)
bytecodeViewPanel.compiler = Compiler.KRAKATAU_ASSEMBLER;
String editable = isPanelEditable ? " - " + EDITABLE : "";
bytecodeViewPanel.textArea.getTitleHeader().setText(decompiler.getDecompilerName() + editable);
}
@Override
public void run()
{
if (bytecodeViewPanel.decompiler == Decompiler.NONE)
return;
processDisplay();
if (bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER)
return;
//nullcheck broken pane
if (updateUpdaterTextArea == null || updateUpdaterTextArea.getScrollPane() == null
|| updateUpdaterTextArea.getScrollPane().getViewport() == null)
{
//build an error message
SwingUtilities.invokeLater(() ->
buildTextArea(bytecodeViewPanel.decompiler, "Critical BCV Error"));
return;
}
//this still freezes the swing UI
synchronizePane();
}
public final CaretListener caretListener = new CaretListener()
{
@Override
public void caretUpdate(CaretEvent e)
{
MethodParser methods = viewer.methods.get(bytecodeViewPanel.panelIndex);
if (methods != null)
{
int methodLine = methods.findActiveMethod(updateUpdaterTextArea.getCaretLineNumber());
if (methodLine != -1)
{
if (BytecodeViewer.viewer.showClassMethods.isSelected())
{
if (methodsList != null)
{
if (methodLine != (int) Objects.requireNonNull(methodsList.getSelectedItem()))
{
methodsList.setSelectedItem(methodLine);
}
}
}
if (BytecodeViewer.viewer.synchronizedViewing.isSelected())
{
int panes = 2;
if (viewer.bytecodeViewPanel3 != null)
panes = 3;
for (int i = 0; i < panes; i++)
{
if (i != bytecodeViewPanel.panelIndex)
{
ClassViewer.selectMethod(viewer, i, methods.getMethod(methodLine));
}
}
}
}
}
}
};
public final ChangeListener viewportListener = new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent e)
{
int panes = 2;
if (viewer.bytecodeViewPanel3 != null)
panes = 3;
if (BytecodeViewer.viewer.synchronizedViewing.isSelected())
{
if (updateUpdaterTextArea.isShowing() && (updateUpdaterTextArea.hasFocus() || updateUpdaterTextArea.getMousePosition() != null))
{
int caretLine = updateUpdaterTextArea.getCaretLineNumber();
int maxViewLine = ClassViewer.getMaxViewLine(updateUpdaterTextArea);
int activeViewLine = ClassViewer.getViewLine(updateUpdaterTextArea);
int activeLine = (activeViewLine == maxViewLine && caretLine > maxViewLine) ? caretLine :
activeViewLine;
int activeLineDelta = -1;
MethodParser.Method activeMethod = null;
MethodParser activeMethods = viewer.methods.get(bytecodeViewPanel.panelIndex);
if (activeMethods != null)
{
int activeMethodLine = activeMethods.findActiveMethod(activeLine);
if (activeMethodLine != -1)
{
activeLineDelta = activeLine - activeMethodLine;
activeMethod = activeMethods.getMethod(activeMethodLine);
ClassViewer.selectMethod(updateUpdaterTextArea, activeMethodLine);
}
}
for (int i = 0; i < panes; i++)
{
if (i != bytecodeViewPanel.panelIndex)
{
int setLine = -1;
RSyntaxTextArea area = null;
switch (i)
{
case 0:
area = viewer.bytecodeViewPanel1.updateThread.updateUpdaterTextArea;
break;
case 1:
area = viewer.bytecodeViewPanel2.updateThread.updateUpdaterTextArea;
break;
case 2:
area = viewer.bytecodeViewPanel3.updateThread.updateUpdaterTextArea;
break;
}
if (area != null)
{
if (activeMethod != null && activeLineDelta >= 0)
{
MethodParser methods = viewer.methods.get(i);
if (methods != null)
{
int methodLine = methods.findMethod(activeMethod);
if (methodLine != -1)
{
int viewLine = ClassViewer.getViewLine(area);
if (activeLineDelta != viewLine - methodLine)
{
setLine = methodLine + activeLineDelta;
}
}
}
} else if (activeLine != ClassViewer.getViewLine(area))
{
setLine = activeLine;
}
if (setLine >= 0)
{
ClassViewer.setViewLine(area, setLine);
}
}
}
}
}
}
}
};
public void synchronizePane()
{
if (bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER
|| bytecodeViewPanel.decompiler == Decompiler.NONE)
return;
SwingUtilities.invokeLater(() ->
{
JViewport viewport = updateUpdaterTextArea.getScrollPane().getViewport();
viewport.addChangeListener(viewportListener);
updateUpdaterTextArea.addCaretListener(caretListener);
});
final MethodParser methods = viewer.methods.get(bytecodeViewPanel.panelIndex);
for (int i = 0; i < updateUpdaterTextArea.getLineCount(); i++)
{
String lineText = updateUpdaterTextArea.getLineText(i);
Matcher regexMatcher = MethodParser.regex.matcher(lineText);
if (regexMatcher.find())
{
String methodName = regexMatcher.group("name");
String methodParams = regexMatcher.group("params");
methods.addMethod(i, methodName, methodParams);
}
}
//TODO fix this
if (BytecodeViewer.viewer.showClassMethods.isSelected())
{
if (!methods.isEmpty())
{
methodsList = new JComboBox<>();
for (Integer line : methods.getMethodsLines())
methodsList.addItem(line);
methodsList.setRenderer(new MethodsRenderer(this));
methodsList.addActionListener(e ->
{
int line = (int) Objects.requireNonNull(methodsList.getSelectedItem());
RSyntaxTextArea area = null;
switch (bytecodeViewPanel.panelIndex)
{
case 0:
area = viewer.bytecodeViewPanel1.updateThread.updateUpdaterTextArea;
break;
case 1:
area = viewer.bytecodeViewPanel2.updateThread.updateUpdaterTextArea;
break;
case 2:
area = viewer.bytecodeViewPanel3.updateThread.updateUpdaterTextArea;
break;
}
if (area != null)
ClassViewer.selectMethod(area, line);
});
JPanel panel = new JPanel(new BorderLayout());
panel.add(updateUpdaterTextArea.getScrollPane().getColumnHeader().getComponent(0), BorderLayout.NORTH);
panel.add(methodsList, BorderLayout.SOUTH);
methodsList.setBackground(BLANK_COLOR);
SwingUtilities.invokeLater(() ->
{
updateUpdaterTextArea.getScrollPane().getColumnHeader().removeAll();
updateUpdaterTextArea.getScrollPane().getColumnHeader().add(panel);
});
}
}
}
public void buildTextArea(Decompiler decompiler, String decompiledSource)
{
updateUpdaterTextArea = new SearchableRSyntaxTextArea();
Configuration.rstaTheme.apply(updateUpdaterTextArea);
bytecodeViewPanel.add(updateUpdaterTextArea.getScrollPane());
bytecodeViewPanel.add(updateUpdaterTextArea.getTitleHeader(), BorderLayout.NORTH);
bytecodeViewPanel.textArea = updateUpdaterTextArea;
bytecodeViewPanel.textArea.setMarkOccurrencesColor(Color.ORANGE);
bytecodeViewPanel.textArea.setCursor(new Cursor(Cursor.TEXT_CURSOR));
bytecodeViewPanel.textArea.setHighlighter(new RSyntaxTextAreaHighlighterEx());
if (bytecodeViewPanel.decompiler != Decompiler.BYTECODE_DISASSEMBLER)
{
bytecodeViewPanel.textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
} else
{
AbstractTokenMakerFactory tokenMakerFactory = (AbstractTokenMakerFactory) TokenMakerFactory.getDefaultInstance();
tokenMakerFactory.putMapping("text/javaBytecode", "the.bytecode.club.bytecodeviewer.decompilers.bytecode.JavaBytecodeTokenMaker");
bytecodeViewPanel.textArea.setSyntaxEditingStyle("text/javaBytecode");
}
bytecodeViewPanel.textArea.setCodeFoldingEnabled(true);
bytecodeViewPanel.textArea.setAntiAliasingEnabled(true);
bytecodeViewPanel.textArea.setText(decompiledSource);
bytecodeViewPanel.textArea.setCaretPosition(0);
bytecodeViewPanel.textArea.setEditable(isPanelEditable);
if (isPanelEditable && decompiler == Decompiler.SMALI_DISASSEMBLER)
bytecodeViewPanel.compiler = Compiler.SMALI_ASSEMBLER;
else if (isPanelEditable && decompiler == Decompiler.KRAKATAU_DISASSEMBLER)
bytecodeViewPanel.compiler = Compiler.KRAKATAU_ASSEMBLER;
String editable = isPanelEditable ? " - " + EDITABLE : "";
bytecodeViewPanel.textArea.getTitleHeader().setText(decompiler.getDecompilerName() + editable);
// TODO: Whenever you click on a mark, both text areas get scrolled. Also, the error strip isn't laid out correctly.
MyErrorStripe errorStripe = new MyErrorStripe(bytecodeViewPanel.textArea);
bytecodeViewPanel.add(errorStripe, BorderLayout.LINE_END);
bytecodeViewPanel.textArea.addCaretListener(e -> {
if (bytecodeViewPanel.textArea.isFocusOwner())
{
RSyntaxTextAreaHighlighterEx highlighterEx = (RSyntaxTextAreaHighlighterEx) bytecodeViewPanel.textArea.getHighlighter();
highlighterEx.clearMarkOccurrencesHighlights();
RSyntaxTextArea textArea = (RSyntaxTextArea) e.getSource();
String workingName = viewer.resource.workingName + "-" + decompiler.getDecompilerName();
markOccurrences(textArea, viewer.classFiles.get(workingName), errorStripe);
}
});
}
private void markOccurrences(RSyntaxTextArea textArea, ClassFileContainer classFileContainer, MyErrorStripe errorStripe)
{
RSyntaxTextAreaHighlighterEx highlighterEx = (RSyntaxTextAreaHighlighterEx) textArea.getHighlighter();
Token token = textArea.modelToToken(textArea.getCaretPosition() - 1);
if (token == null || token.getLexeme().equals(";"))
{
highlighterEx.clearMarkOccurrencesHighlights();
errorStripe.refreshMarkers();
return;
}
token = TokenUtil.getToken(textArea, token);
if (token == null)
{
highlighterEx.clearMarkOccurrencesHighlights();
errorStripe.refreshMarkers();
return;
}
int line = textArea.getCaretLineNumber() + 1;
int column = textArea.getCaretOffsetFromLineStart();
Token finalToken = token;
/*
Fields
*/
markField(textArea, classFileContainer, line, column, finalToken, highlighterEx);
errorStripe.refreshMarkers();
}
private void markField(RSyntaxTextArea textArea, ClassFileContainer classFileContainer, int line, int column, Token finalToken, RSyntaxTextAreaHighlighterEx highlighterEx)
{
classFileContainer.fieldMembers.values().forEach(fields -> fields.forEach(field -> {
String owner;
if (field.line == line && field.columnStart - 1 <= column && field.columnEnd >= column)
{
owner = field.owner;
try
{
Element root = textArea.getDocument().getDefaultRootElement();
for (
ClassFieldLocation location :
classFileContainer.getFieldLocationsFor(finalToken.getLexeme())
)
{
if (Objects.equals(owner, location.owner))
{
int startOffset = root
.getElement(location.line - 1)
.getStartOffset() + (location.columnStart - 1);
int endOffset = root
.getElement(location.line - 1)
.getStartOffset() + (location.columnEnd - 1);
highlighterEx.addMarkedOccurrenceHighlight(
startOffset, endOffset, new SmartHighlightPainter()
);
}
}
} catch (BadLocationException ex)
{
throw new RuntimeException(ex);
}
}
}));
}
}

View File

@ -0,0 +1,92 @@
package the.bytecode.club.bytecodeviewer.resources.classcontainer;
import com.github.javaparser.ParseProblemException;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassFieldLocation;
import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassLocalVariableLocation;
import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassMethodLocation;
import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassParameterLocation;
import the.bytecode.club.bytecodeviewer.resources.classcontainer.parser.MyVoidVisitor;
import java.util.ArrayList;
import java.util.List;
import java.util.NavigableMap;
import java.util.TreeMap;
/**
* Created by Bl3nd.
* Date: 8/26/2024
*/
public class ClassFileContainer
{
public transient NavigableMap<String, ArrayList<ClassFieldLocation>> fieldMembers = new TreeMap<>();
public transient NavigableMap<String, ArrayList<ClassParameterLocation>> methodParameterMembers = new TreeMap<>();
public transient NavigableMap<String, ArrayList<ClassLocalVariableLocation>> methodLocalMembers = new TreeMap<>();
public transient NavigableMap<String, ArrayList<ClassMethodLocation>> methodMembers = new TreeMap<>();
public boolean hasBeenParsed = false;
public final String className;
private final String content;
public ClassFileContainer(String className, String content)
{
this.className = className;
this.content = content;
}
public void parse()
{
try
{
StaticJavaParser.getParserConfiguration().setSymbolResolver(new JavaSymbolSolver(new ReflectionTypeSolver()));
CompilationUnit compilationUnit = StaticJavaParser.parse(this.content);
compilationUnit.accept(new MyVoidVisitor(this, compilationUnit), null);
} catch (ParseProblemException e)
{
System.err.println("Parsing error!");
}
}
public void putField(String key, ClassFieldLocation value)
{
this.fieldMembers.computeIfAbsent(key, v -> new ArrayList<>()).add(value);
}
public List<ClassFieldLocation> getFieldLocationsFor(String fieldName)
{
return fieldMembers.getOrDefault(fieldName, new ArrayList<>());
}
public void putParameter(String key, ClassParameterLocation value)
{
this.methodParameterMembers.computeIfAbsent(key, v -> new ArrayList<>()).add(value);
}
public List<ClassParameterLocation> getParameterLocationsFor(String key)
{
return methodParameterMembers.getOrDefault(key, new ArrayList<>());
}
public void putLocalVariable(String key, ClassLocalVariableLocation value)
{
this.methodLocalMembers.computeIfAbsent(key, v -> new ArrayList<>()).add(value);
}
public List<ClassLocalVariableLocation> getLocalLocationsFor(String key)
{
return methodLocalMembers.getOrDefault(key, new ArrayList<>());
}
public void putMethod(String key, ClassMethodLocation value)
{
this.methodMembers.computeIfAbsent(key, v -> new ArrayList<>()).add(value);
}
public List<ClassMethodLocation> getMethodLocationsFor(String key)
{
return methodMembers.getOrDefault(key, new ArrayList<>());
}
}

View File

@ -0,0 +1,29 @@
package the.bytecode.club.bytecodeviewer.resources.classcontainer.locations;
/**
* Created by Bl3nd.
* Date: 8/26/2024
*/
public class ClassFieldLocation
{
public final String owner;
public final String type;
public final int line;
public final int columnStart;
public final int columnEnd;
public ClassFieldLocation(final String owner, final String type, final int line, final int columnStart, final int columnEnd)
{
this.owner = owner;
this.type = type;
this.line = line;
this.columnStart = columnStart;
this.columnEnd = columnEnd;
}
@Override
public String toString()
{
return "ClassFieldLocation{" + "owner='" + owner + '\'' + ", type='" + type + '\'' + ", line=" + line + ", columnStart=" + columnStart + ", columnEnd=" + columnEnd + '}';
}
}

View File

@ -0,0 +1,25 @@
package the.bytecode.club.bytecodeviewer.resources.classcontainer.locations;
/**
* Created by Bl3nd.
* Date: 9/5/2024
*/
public class ClassLocalVariableLocation
{
public final String owner;
public final String method;
public final String decRef;
public final int line;
public final int columnStart;
public final int columnEnd;
public ClassLocalVariableLocation(String owner, String method, String decRef, int line, int columnStart, int columnEnd)
{
this.owner = owner;
this.method = method;
this.decRef = decRef;
this.line = line;
this.columnStart = columnStart;
this.columnEnd = columnEnd;
}
}

View File

@ -0,0 +1,25 @@
package the.bytecode.club.bytecodeviewer.resources.classcontainer.locations;
/**
* Created by Bl3nd.
* Date: 9/5/2024
*/
public class ClassMethodLocation
{
public final String owner;
public final String methodParameterTypes;
public final String decRef;
public final int line;
public final int columnStart;
public final int columnEnd;
public ClassMethodLocation(String owner, String methodParameterTypes, String decRef, int line, int columnStart, int columnEnd)
{
this.owner = owner;
this.methodParameterTypes = methodParameterTypes;
this.decRef = decRef;
this.line = line;
this.columnStart = columnStart;
this.columnEnd = columnEnd;
}
}

View File

@ -0,0 +1,25 @@
package the.bytecode.club.bytecodeviewer.resources.classcontainer.locations;
/**
* Created by Bl3nd.
* Date: 9/5/2024
*/
public class ClassParameterLocation
{
public final String owner;
public final String method;
public final String decRef;
public final int line;
public final int columnStart;
public final int columnEnd;
public ClassParameterLocation(String owner, String method, String decRef, int line, int columnStart, int columnEnd)
{
this.owner = owner;
this.method = method;
this.decRef = decRef;
this.line = line;
this.columnStart = columnStart;
this.columnEnd = columnEnd;
}
}

View File

@ -0,0 +1,28 @@
package the.bytecode.club.bytecodeviewer.resources.classcontainer.parser;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Token;
import org.jetbrains.annotations.NotNull;
/**
* Created by Bl3nd.
* Date: 9/5/2024
*/
public class TokenUtil
{
public static Token getToken(final RSyntaxTextArea textArea, final @NotNull Token token)
{
String lexeme = token.getLexeme();
return lexeme.isEmpty()
|| lexeme.equals(".")
|| lexeme.equals("(")
|| lexeme.equals(")")
|| lexeme.equals("[")
|| lexeme.equals("~")
|| lexeme.equals("-")
|| lexeme.equals("+")
|| lexeme.equals(" ")
? textArea.modelToToken(textArea.getCaretPosition() + 1)
: token;
}
}