commit
5f1e75c284
10
pom.xml
10
pom.xml
|
@ -372,6 +372,16 @@
|
||||||
<artifactId>org.abego.treelayout.core</artifactId>
|
<artifactId>org.abego.treelayout.core</artifactId>
|
||||||
<version>${treelayout.version}</version>
|
<version>${treelayout.version}</version>
|
||||||
</dependency>
|
</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 -->
|
<!-- TODO Re-add for Graal.JS support -->
|
||||||
<!--<dependency>
|
<!--<dependency>
|
||||||
|
|
|
@ -0,0 +1,418 @@
|
||||||
|
/*
|
||||||
|
* 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 true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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() + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,197 +18,187 @@
|
||||||
|
|
||||||
package the.bytecode.club.bytecodeviewer.gui.components;
|
package the.bytecode.club.bytecodeviewer.gui.components;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Font;
|
|
||||||
import java.awt.event.InputEvent;
|
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
import java.awt.event.MouseWheelEvent;
|
|
||||||
import java.awt.event.MouseWheelListener;
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JCheckBox;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import javax.swing.text.BadLocationException;
|
|
||||||
|
|
||||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||||
import org.fife.ui.rtextarea.RTextScrollPane;
|
import org.fife.ui.rtextarea.RTextScrollPane;
|
||||||
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
import the.bytecode.club.bytecodeviewer.Configuration;
|
import the.bytecode.club.bytecodeviewer.Configuration;
|
||||||
import the.bytecode.club.bytecodeviewer.GlobalHotKeys;
|
import the.bytecode.club.bytecodeviewer.GlobalHotKeys;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.components.listeners.PressKeyListener;
|
import the.bytecode.club.bytecodeviewer.gui.components.listeners.PressKeyListener;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.components.listeners.ReleaseKeyListener;
|
|
||||||
import the.bytecode.club.bytecodeviewer.gui.theme.LAFTheme;
|
import the.bytecode.club.bytecodeviewer.gui.theme.LAFTheme;
|
||||||
import the.bytecode.club.bytecodeviewer.resources.IconResources;
|
|
||||||
import the.bytecode.club.bytecodeviewer.translation.TranslatedComponents;
|
|
||||||
import the.bytecode.club.bytecodeviewer.translation.components.TranslatedJCheckBox;
|
|
||||||
import the.bytecode.club.bytecodeviewer.util.JTextAreaUtils;
|
import the.bytecode.club.bytecodeviewer.util.JTextAreaUtils;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searching on an RSyntaxTextArea using swing highlighting
|
* Searching on an RSyntaxTextArea using swing highlighting
|
||||||
*
|
*
|
||||||
* @author Konloch
|
* @author Konloch
|
||||||
* @since 6/25/2021
|
* @since 6/25/2021
|
||||||
*/
|
*/
|
||||||
public class SearchableRSyntaxTextArea extends RSyntaxTextArea {
|
public class SearchableRSyntaxTextArea extends RSyntaxTextArea
|
||||||
|
{
|
||||||
|
|
||||||
private RTextScrollPane scrollPane = new RTextScrollPane(this);
|
private RTextScrollPane scrollPane = new RTextScrollPane(this);
|
||||||
private final JPanel searchPanel = new JPanel(new BorderLayout());
|
private final TextAreaSearchPanel textAreaSearchPanel;
|
||||||
private final JTextField searchInput = new JTextField();
|
private final Color darkScrollBackground = new Color(0x3c3f41);
|
||||||
private final JCheckBox caseSensitiveSearch = new TranslatedJCheckBox("Match case", TranslatedComponents.MATCH_CASE);
|
private final Color darkScrollForeground = new Color(0x575859);
|
||||||
private final JLabel titleHeader = new JLabel("");
|
private final Color blackScrollBackground = new Color(0x232323);
|
||||||
private final Color darkScrollBackground = new Color(0x3c3f41);
|
private final Color blackScrollForeground = new Color(0x575859);
|
||||||
private final Color darkScrollForeground = new Color(0x575859);
|
private Runnable onCtrlS;
|
||||||
private final Color blackScrollBackground = new Color(0x232323);
|
|
||||||
private final Color blackScrollForeground = new Color(0x575859);
|
|
||||||
private Runnable onCtrlS;
|
|
||||||
|
|
||||||
public SearchableRSyntaxTextArea() {
|
public SearchableRSyntaxTextArea()
|
||||||
if (Configuration.lafTheme == LAFTheme.HIGH_CONTRAST_DARK) {
|
{
|
||||||
//this fixes the white border on the jScrollBar panes
|
if (Configuration.lafTheme == LAFTheme.HIGH_CONTRAST_DARK)
|
||||||
scrollPane.getHorizontalScrollBar().setBackground(blackScrollBackground);
|
{
|
||||||
scrollPane.getHorizontalScrollBar().setForeground(blackScrollForeground);
|
//this fixes the white border on the jScrollBar panes
|
||||||
scrollPane.getVerticalScrollBar().setBackground(blackScrollBackground);
|
scrollPane.getHorizontalScrollBar().setBackground(blackScrollBackground);
|
||||||
scrollPane.getVerticalScrollBar().setForeground(blackScrollForeground);
|
scrollPane.getHorizontalScrollBar().setForeground(blackScrollForeground);
|
||||||
} else if (Configuration.lafTheme.isDark()) {
|
scrollPane.getVerticalScrollBar().setBackground(blackScrollBackground);
|
||||||
//this fixes the white border on the jScrollBar panes
|
scrollPane.getVerticalScrollBar().setForeground(blackScrollForeground);
|
||||||
scrollPane.getHorizontalScrollBar().setBackground(darkScrollBackground);
|
} else if (Configuration.lafTheme.isDark())
|
||||||
scrollPane.getHorizontalScrollBar().setForeground(darkScrollForeground);
|
{
|
||||||
scrollPane.getVerticalScrollBar().setBackground(darkScrollBackground);
|
//this fixes the white border on the jScrollBar panes
|
||||||
scrollPane.getVerticalScrollBar().setForeground(darkScrollForeground);
|
scrollPane.getHorizontalScrollBar().setBackground(darkScrollBackground);
|
||||||
}
|
scrollPane.getHorizontalScrollBar().setForeground(darkScrollForeground);
|
||||||
|
scrollPane.getVerticalScrollBar().setBackground(darkScrollBackground);
|
||||||
|
scrollPane.getVerticalScrollBar().setForeground(darkScrollForeground);
|
||||||
|
}
|
||||||
|
|
||||||
setAntiAliasingEnabled(true);
|
this.textAreaSearchPanel = new TextAreaSearchPanel(this);
|
||||||
|
|
||||||
scrollPane.setColumnHeaderView(searchPanel);
|
setAntiAliasingEnabled(true);
|
||||||
|
|
||||||
JButton searchNext = new JButton();
|
addKeyListener(new PressKeyListener(keyEvent ->
|
||||||
JButton searchPrev = new JButton();
|
{
|
||||||
JPanel buttonPane = new JPanel(new BorderLayout());
|
if ((keyEvent.getKeyCode() == KeyEvent.VK_F) && ((keyEvent.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0))
|
||||||
buttonPane.add(searchNext, BorderLayout.WEST);
|
this.textAreaSearchPanel.getSearchInput().requestFocusInWindow();
|
||||||
buttonPane.add(searchPrev, BorderLayout.EAST);
|
|
||||||
searchNext.setIcon(IconResources.nextIcon);
|
|
||||||
searchPrev.setIcon(IconResources.prevIcon);
|
|
||||||
searchPanel.add(buttonPane, BorderLayout.WEST);
|
|
||||||
searchPanel.add(searchInput, BorderLayout.CENTER);
|
|
||||||
searchPanel.add(caseSensitiveSearch, BorderLayout.EAST);
|
|
||||||
|
|
||||||
searchNext.addActionListener(arg0 -> search(searchInput.getText(), true, caseSensitiveSearch.isSelected()));
|
if (onCtrlS != null && (keyEvent.getKeyCode() == KeyEvent.VK_S) && ((keyEvent.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0))
|
||||||
searchPrev.addActionListener(arg0 -> search(searchInput.getText(), false, caseSensitiveSearch.isSelected()));
|
{
|
||||||
|
onCtrlS.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
searchInput.addKeyListener(new ReleaseKeyListener(keyEvent ->
|
GlobalHotKeys.keyPressed(keyEvent);
|
||||||
{
|
}));
|
||||||
if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER)
|
|
||||||
search(searchInput.getText(), true, caseSensitiveSearch.isSelected());
|
|
||||||
}));
|
|
||||||
|
|
||||||
addKeyListener(new PressKeyListener(keyEvent ->
|
setCursor(new Cursor(Cursor.TEXT_CURSOR));
|
||||||
{
|
getCaret().setBlinkRate(0);
|
||||||
if ((keyEvent.getKeyCode() == KeyEvent.VK_F) && ((keyEvent.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0))
|
getCaret().setVisible(true);
|
||||||
searchInput.requestFocus();
|
addFocusListener(new FocusAdapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void focusGained(FocusEvent e)
|
||||||
|
{
|
||||||
|
getCaret().setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (onCtrlS != null && (keyEvent.getKeyCode() == KeyEvent.VK_S) && ((keyEvent.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0)) {
|
@Override
|
||||||
onCtrlS.run();
|
public void focusLost(FocusEvent e)
|
||||||
return;
|
{
|
||||||
}
|
getCaret().setVisible(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
GlobalHotKeys.keyPressed(keyEvent);
|
final Font newFont = getFont().deriveFont((float) BytecodeViewer.viewer.getFontSize());
|
||||||
}));
|
|
||||||
|
|
||||||
final Font newFont = getFont().deriveFont((float) BytecodeViewer.viewer.getFontSize());
|
//set number-bar font
|
||||||
|
setFont(newFont);
|
||||||
|
|
||||||
//set number-bar font
|
SwingUtilities.invokeLater(() -> {
|
||||||
setFont(newFont);
|
//attach CTRL + Mouse Wheel Zoom
|
||||||
|
attachCtrlMouseWheelZoom();
|
||||||
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
//set text font
|
||||||
//attach CTRL + Mouse Wheel Zoom
|
setFont(newFont);
|
||||||
attachCtrlMouseWheelZoom();
|
});
|
||||||
|
|
||||||
//set text font
|
}
|
||||||
setFont(newFont);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
public void search(String search, boolean forwardSearchDirection, boolean caseSensitiveSearch)
|
||||||
|
{
|
||||||
|
JTextAreaUtils.search(this, search, forwardSearchDirection, caseSensitiveSearch);
|
||||||
|
}
|
||||||
|
|
||||||
public void search(String search, boolean forwardSearchDirection, boolean caseSensitiveSearch) {
|
public void highlight(String pattern, boolean caseSensitiveSearch)
|
||||||
JTextAreaUtils.search(this, search, forwardSearchDirection, caseSensitiveSearch);
|
{
|
||||||
}
|
JTextAreaUtils.highlight(this, pattern, caseSensitiveSearch);
|
||||||
|
}
|
||||||
|
|
||||||
public void highlight(String pattern, boolean caseSensitiveSearch) {
|
public void attachCtrlMouseWheelZoom()
|
||||||
JTextAreaUtils.highlight(this, pattern, caseSensitiveSearch);
|
{
|
||||||
}
|
scrollPane.addMouseWheelListener(e -> {
|
||||||
|
if (getText().isEmpty()) return;
|
||||||
|
if ((e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0)
|
||||||
|
{
|
||||||
|
Font font = getFont();
|
||||||
|
int size = font.getSize();
|
||||||
|
if (e.getWheelRotation() > 0)
|
||||||
|
setFont(new Font(font.getName(), font.getStyle(), --size >= 2 ? --size : 2));
|
||||||
|
else
|
||||||
|
setFont(new Font(font.getName(), font.getStyle(), ++size));
|
||||||
|
|
||||||
public void attachCtrlMouseWheelZoom() {
|
e.consume();
|
||||||
scrollPane.addMouseWheelListener(e -> {
|
}
|
||||||
if (getText().isEmpty()) return;
|
});
|
||||||
if ((e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0) {
|
|
||||||
Font font = getFont();
|
|
||||||
int size = font.getSize();
|
|
||||||
if (e.getWheelRotation() > 0)
|
|
||||||
setFont(new Font(font.getName(), font.getStyle(), --size >= 2 ? --size : 2));
|
|
||||||
else
|
|
||||||
setFont(new Font(font.getName(), font.getStyle(), ++size));
|
|
||||||
|
|
||||||
e.consume();
|
scrollPane = new RTextScrollPane()
|
||||||
}
|
{
|
||||||
});
|
@Override
|
||||||
|
protected void processMouseWheelEvent(MouseWheelEvent event)
|
||||||
|
{
|
||||||
|
if (!isWheelScrollingEnabled())
|
||||||
|
{
|
||||||
|
if (getParent() != null)
|
||||||
|
{
|
||||||
|
getParent().dispatchEvent(SwingUtilities.convertMouseEvent(this, event, getParent()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
scrollPane = new RTextScrollPane() {
|
super.processMouseWheelEvent(event);
|
||||||
@Override
|
}
|
||||||
protected void processMouseWheelEvent(MouseWheelEvent event) {
|
};
|
||||||
if (!isWheelScrollingEnabled()) {
|
|
||||||
if (getParent() != null) {
|
|
||||||
getParent().dispatchEvent(SwingUtilities.convertMouseEvent(this, event, getParent()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
super.processMouseWheelEvent(event);
|
scrollPane.setWheelScrollingEnabled(false);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
scrollPane.setWheelScrollingEnabled(false);
|
public String getLineText(int line)
|
||||||
}
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (line < getLineCount())
|
||||||
|
{
|
||||||
|
int start = getLineStartOffset(line);
|
||||||
|
int end = getLineEndOffset(line);
|
||||||
|
return getText(start, end - start).trim();
|
||||||
|
}
|
||||||
|
} catch (BadLocationException ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
public String getLineText(int line) {
|
public void setOnCtrlS(Runnable onCtrlS)
|
||||||
try {
|
{
|
||||||
if (line < getLineCount()) {
|
this.onCtrlS = onCtrlS;
|
||||||
int start = getLineStartOffset(line);
|
}
|
||||||
int end = getLineEndOffset(line);
|
|
||||||
return getText(start, end - start).trim();
|
|
||||||
}
|
|
||||||
} catch (BadLocationException ignored) {
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnCtrlS(Runnable onCtrlS) {
|
public RTextScrollPane getScrollPane()
|
||||||
this.onCtrlS = onCtrlS;
|
{
|
||||||
}
|
return scrollPane;
|
||||||
|
}
|
||||||
|
|
||||||
public RTextScrollPane getScrollPane() {
|
public TextAreaSearchPanel getTextAreaSearchPanel()
|
||||||
return scrollPane;
|
{
|
||||||
}
|
return textAreaSearchPanel;
|
||||||
|
}
|
||||||
|
|
||||||
public JPanel getSearchPanel() {
|
public Runnable getOnCtrlS()
|
||||||
return searchPanel;
|
{
|
||||||
}
|
return onCtrlS;
|
||||||
|
}
|
||||||
public JTextField getSearchInput() {
|
|
||||||
return searchInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JCheckBox getCaseSensitiveSearch() {
|
|
||||||
return caseSensitiveSearch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JLabel getTitleHeader() {
|
|
||||||
return titleHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Runnable getOnCtrlS() {
|
|
||||||
return onCtrlS;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
|
||||||
|
* Copyright (C) 2014 Konloch - Konloch.com / BytecodeViewer.com *
|
||||||
|
* *
|
||||||
|
* This program 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 3 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program 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 General Public License *
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||||
|
***************************************************************************/
|
||||||
|
package the.bytecode.club.bytecodeviewer.gui.components;
|
||||||
|
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.components.listeners.ReleaseKeyListener;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.IconResources;
|
||||||
|
import the.bytecode.club.bytecodeviewer.translation.TranslatedComponents;
|
||||||
|
import the.bytecode.club.bytecodeviewer.translation.components.TranslatedJCheckBox;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This panel represents the decompiler name and search box on the top of every {@link the.bytecode.club.bytecodeviewer.gui.resourceviewer.BytecodeViewPanel}
|
||||||
|
* <p>
|
||||||
|
* Created by Bl3nd.
|
||||||
|
* Date: 9/6/2024
|
||||||
|
*/
|
||||||
|
public class TextAreaSearchPanel extends JPanel
|
||||||
|
{
|
||||||
|
private final JCheckBox caseSensitiveSearch = new TranslatedJCheckBox("Match case", TranslatedComponents.MATCH_CASE);
|
||||||
|
private final JLabel titleHeader = new JLabel("");
|
||||||
|
private final JTextField searchInput = new JTextField();
|
||||||
|
private final JTextArea textArea;
|
||||||
|
|
||||||
|
public TextAreaSearchPanel(JTextArea textArea)
|
||||||
|
{
|
||||||
|
super(new BorderLayout());
|
||||||
|
this.textArea = textArea;
|
||||||
|
setup();
|
||||||
|
setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setup()
|
||||||
|
{
|
||||||
|
this.add(titleHeader, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
JPanel searchPanel = new JPanel();
|
||||||
|
searchPanel.setLayout(new BoxLayout(searchPanel, BoxLayout.X_AXIS));
|
||||||
|
|
||||||
|
searchPanel.add(Box.createHorizontalStrut(35));
|
||||||
|
JButton searchNext = new JButton(IconResources.nextIcon);
|
||||||
|
searchPanel.add(searchNext);
|
||||||
|
searchNext.addActionListener(arg0 -> ((SearchableRSyntaxTextArea) textArea).search(searchInput.getText(), true, caseSensitiveSearch.isSelected()));
|
||||||
|
|
||||||
|
JButton searchPrev = new JButton(IconResources.prevIcon);
|
||||||
|
searchPanel.add(searchPrev);
|
||||||
|
searchPrev.addActionListener(arg0 -> ((SearchableRSyntaxTextArea) textArea).search(searchInput.getText(), false, caseSensitiveSearch.isSelected()));
|
||||||
|
|
||||||
|
searchPanel.add(searchInput);
|
||||||
|
searchInput.addKeyListener(new ReleaseKeyListener(keyEvent ->
|
||||||
|
{
|
||||||
|
if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER)
|
||||||
|
((SearchableRSyntaxTextArea) textArea).search(searchInput.getText(), true, caseSensitiveSearch.isSelected());
|
||||||
|
}));
|
||||||
|
|
||||||
|
searchPanel.add(caseSensitiveSearch);
|
||||||
|
|
||||||
|
// This is needed to add more room to the right of the sensitive search check box
|
||||||
|
searchPanel.add(Box.createHorizontalStrut(2));
|
||||||
|
|
||||||
|
this.add(searchPanel, BorderLayout.SOUTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JLabel getTitleHeader()
|
||||||
|
{
|
||||||
|
return titleHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JTextField getSearchInput()
|
||||||
|
{
|
||||||
|
return searchInput;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,348 @@
|
||||||
|
package the.bytecode.club.bytecodeviewer.gui.components.actions;
|
||||||
|
|
||||||
|
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||||
|
import org.fife.ui.rsyntaxtextarea.Token;
|
||||||
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.BytecodeViewPanel;
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.util.BytecodeViewPanelUpdater;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.ResourceContainer;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.classcontainer.ClassFileContainer;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassFieldLocation;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassMethodLocation;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassReferenceLocation;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.classcontainer.parser.TokenUtil;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.event.CaretEvent;
|
||||||
|
import javax.swing.event.CaretListener;
|
||||||
|
import javax.swing.text.Element;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This action is triggered by a user typing (CTRL+B). This goes to a specific variables declaration whether it be in the opened class, or a class within the jar.
|
||||||
|
* <p>
|
||||||
|
* Created by Bl3nd.
|
||||||
|
* Date: 9/7/2024
|
||||||
|
*/
|
||||||
|
public class GoToAction extends AbstractAction
|
||||||
|
{
|
||||||
|
private final ClassFileContainer container;
|
||||||
|
|
||||||
|
public GoToAction(ClassFileContainer classFileContainer)
|
||||||
|
{
|
||||||
|
this.container = classFileContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e)
|
||||||
|
{
|
||||||
|
RSyntaxTextArea textArea = (RSyntaxTextArea) e.getSource();
|
||||||
|
int line = textArea.getCaretLineNumber() + 1;
|
||||||
|
int column = textArea.getCaretOffsetFromLineStart();
|
||||||
|
|
||||||
|
container.fieldMembers.values().forEach(fields -> fields.forEach(field -> {
|
||||||
|
if (field.line == line && field.columnStart - 1 <= column && field.columnEnd >= column)
|
||||||
|
{
|
||||||
|
Element root = textArea.getDocument().getDefaultRootElement();
|
||||||
|
// Open the class that is associated with the field's owner.
|
||||||
|
if (!field.owner.equals(container.getName()))
|
||||||
|
{
|
||||||
|
open(textArea, false, true, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassFieldLocation first = fields.get(0);
|
||||||
|
int startOffset = root.getElement(first.line - 1).getStartOffset() + (first.columnStart - 1);
|
||||||
|
textArea.setCaretPosition(startOffset);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
container.methodParameterMembers.values().forEach(parameters -> parameters.forEach(parameter -> {
|
||||||
|
if (parameter.line == line && parameter.columnStart - 1 <= column && parameter.columnEnd >= column)
|
||||||
|
{
|
||||||
|
Element root = textArea.getDocument().getDefaultRootElement();
|
||||||
|
if (parameter.decRef.equalsIgnoreCase("declaration"))
|
||||||
|
{
|
||||||
|
int startOffset = root.getElement(parameter.line - 1).getStartOffset() + (parameter.columnStart - 1);
|
||||||
|
textArea.setCaretPosition(startOffset);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
String method = parameter.method;
|
||||||
|
parameters.stream().filter(classParameterLocation -> classParameterLocation.method.equals(method)).forEach(classParameterLocation -> {
|
||||||
|
if (classParameterLocation.decRef.equalsIgnoreCase("declaration"))
|
||||||
|
{
|
||||||
|
int startOffset = root.getElement(classParameterLocation.line - 1).getStartOffset() + (classParameterLocation.columnStart - 1);
|
||||||
|
textArea.setCaretPosition(startOffset);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
container.methodLocalMembers.values().forEach(localMembers -> localMembers.forEach(localMember -> {
|
||||||
|
if (localMember.line == line && localMember.columnStart - 1 <= column && localMember.columnEnd >= column)
|
||||||
|
{
|
||||||
|
Element root = textArea.getDocument().getDefaultRootElement();
|
||||||
|
if (localMember.decRef.equals("declaration"))
|
||||||
|
{
|
||||||
|
int startOffset = root.getElement(localMember.line - 1).getStartOffset() + (localMember.columnStart - 1);
|
||||||
|
textArea.setCaretPosition(startOffset);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
String method = localMember.method;
|
||||||
|
localMembers.stream().filter(classLocalVariableLocation -> classLocalVariableLocation.method.equals(method)).forEach(classLocalVariableLocation -> {
|
||||||
|
if (classLocalVariableLocation.decRef.equalsIgnoreCase("declaration"))
|
||||||
|
{
|
||||||
|
int startOffset = root.getElement(classLocalVariableLocation.line - 1).getStartOffset() + (classLocalVariableLocation.columnStart - 1);
|
||||||
|
textArea.setCaretPosition(startOffset);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
container.methodMembers.values().forEach(methods -> methods.forEach(method -> {
|
||||||
|
if (method.line == line && method.columnStart - 1 <= column && method.columnEnd >= column)
|
||||||
|
{
|
||||||
|
Element root = textArea.getDocument().getDefaultRootElement();
|
||||||
|
if (method.decRef.equalsIgnoreCase("declaration"))
|
||||||
|
{
|
||||||
|
int startOffset = root.getElement(method.line - 1).getStartOffset() + (method.columnStart - 1);
|
||||||
|
textArea.setCaretPosition(startOffset);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
methods.stream().filter(classMethodLocation -> classMethodLocation.owner.equals(method.owner)).forEach(classMethodLocation -> {
|
||||||
|
if (classMethodLocation.decRef.equalsIgnoreCase("declaration"))
|
||||||
|
{
|
||||||
|
int startOffset = root.getElement(classMethodLocation.line - 1).getStartOffset() + (classMethodLocation.columnStart - 1);
|
||||||
|
textArea.setCaretPosition(startOffset);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
open(textArea, false, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
container.classReferences.values().forEach(classes -> classes.forEach(clazz -> {
|
||||||
|
String name;
|
||||||
|
if (clazz.line == line && clazz.columnStart - 1 <= column && clazz.columnEnd - 1 >= column)
|
||||||
|
{
|
||||||
|
name = clazz.owner;
|
||||||
|
Element root = textArea.getDocument().getDefaultRootElement();
|
||||||
|
if (clazz.type.equals("declaration"))
|
||||||
|
{
|
||||||
|
int startOffset = root.getElement(clazz.line - 1).getStartOffset() + (clazz.columnStart - 1);
|
||||||
|
textArea.setCaretPosition(startOffset);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
classes.stream().filter(classReferenceLocation -> classReferenceLocation.owner.equals(name)).forEach(classReferenceLocation -> {
|
||||||
|
if (classReferenceLocation.type.equals("declaration"))
|
||||||
|
{
|
||||||
|
int startOffset = root.getElement(classReferenceLocation.line - 1).getStartOffset() + (classReferenceLocation.columnStart - 1);
|
||||||
|
textArea.setCaretPosition(startOffset);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should not really do anything when the class is already open
|
||||||
|
open(textArea, true, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClassFileContainer openClass(String lexeme, boolean field, boolean method)
|
||||||
|
{
|
||||||
|
if (lexeme.equals(container.getName()))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
ResourceContainer resourceContainer = BytecodeViewer.getFileContainer(container.getParentContainer());
|
||||||
|
if (resourceContainer == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (field)
|
||||||
|
{
|
||||||
|
String className = container.getClassForField(lexeme);
|
||||||
|
BytecodeViewer.viewer.workPane.addClassResource(resourceContainer, className + ".class");
|
||||||
|
ClassViewer activeResource = (ClassViewer) BytecodeViewer.viewer.workPane.getActiveResource();
|
||||||
|
HashMap<String, ClassFileContainer> classFiles = BytecodeViewer.viewer.workPane.classFiles;
|
||||||
|
return wait(classFiles, activeResource);
|
||||||
|
} else if (method)
|
||||||
|
{
|
||||||
|
ClassMethodLocation classMethodLocation = container.getMethodLocationsFor(lexeme).get(0);
|
||||||
|
ClassReferenceLocation classReferenceLocation = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
classReferenceLocation = container.getClassReferenceLocationsFor(classMethodLocation.owner).get(0);
|
||||||
|
} catch (Exception ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classReferenceLocation == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
String packagePath = classReferenceLocation.packagePath;
|
||||||
|
if (packagePath.startsWith("java") || packagePath.startsWith("javax") || packagePath.startsWith("com.sun"))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
String resourceName = packagePath + "/" + classMethodLocation.owner;
|
||||||
|
if (resourceContainer.resourceClasses.containsKey(resourceName))
|
||||||
|
{
|
||||||
|
BytecodeViewer.viewer.workPane.addClassResource(resourceContainer, resourceName + ".class");
|
||||||
|
ClassViewer activeResource = (ClassViewer) BytecodeViewer.viewer.workPane.getActiveResource();
|
||||||
|
HashMap<String, ClassFileContainer> classFiles = BytecodeViewer.viewer.workPane.classFiles;
|
||||||
|
return wait(classFiles, activeResource);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
ClassReferenceLocation classReferenceLocation = container.getClassReferenceLocationsFor(lexeme).get(0);
|
||||||
|
String packagePath = classReferenceLocation.packagePath;
|
||||||
|
if (packagePath.startsWith("java") || packagePath.startsWith("javax") || packagePath.startsWith("com.sun"))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
String resourceName = packagePath + "/" + lexeme;
|
||||||
|
if (resourceContainer.resourceClasses.containsKey(resourceName))
|
||||||
|
{
|
||||||
|
BytecodeViewer.viewer.workPane.addClassResource(resourceContainer, resourceName + ".class");
|
||||||
|
ClassViewer activeResource = (ClassViewer) BytecodeViewer.viewer.workPane.getActiveResource();
|
||||||
|
HashMap<String, ClassFileContainer> classFiles = BytecodeViewer.viewer.workPane.classFiles;
|
||||||
|
return wait(classFiles, activeResource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void open(RSyntaxTextArea textArea, boolean isClass, boolean isField, boolean isMethod)
|
||||||
|
{
|
||||||
|
Thread thread = new Thread(() -> {
|
||||||
|
Token token = textArea.modelToToken(textArea.getCaretPosition());
|
||||||
|
token = TokenUtil.getToken(textArea, token);
|
||||||
|
String lexeme = token.getLexeme();
|
||||||
|
ClassFileContainer classFileContainer;
|
||||||
|
if (isClass)
|
||||||
|
{
|
||||||
|
classFileContainer = openClass(lexeme, false, false);
|
||||||
|
if (classFileContainer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
classFileContainer.classReferences.forEach((className, classReference) -> {
|
||||||
|
if (className.equals(lexeme))
|
||||||
|
{
|
||||||
|
classReference.forEach(classReferenceLocation -> {
|
||||||
|
if (classReferenceLocation.type.equals("declaration"))
|
||||||
|
{
|
||||||
|
moveCursor(classReferenceLocation.line, classReferenceLocation.columnStart);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (isField)
|
||||||
|
{
|
||||||
|
classFileContainer = openClass(lexeme, true, false);
|
||||||
|
if (classFileContainer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
classFileContainer.fieldMembers.forEach((fieldName, fields) -> {
|
||||||
|
if (fieldName.equals(lexeme))
|
||||||
|
{
|
||||||
|
fields.forEach(classFieldLocation -> {
|
||||||
|
if (classFieldLocation.type.equals("declaration"))
|
||||||
|
{
|
||||||
|
moveCursor(classFieldLocation.line, classFieldLocation.columnStart);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (isMethod)
|
||||||
|
{
|
||||||
|
classFileContainer = openClass(lexeme, false, true);
|
||||||
|
if (classFileContainer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
classFileContainer.methodMembers.forEach((methodName, methods) -> {
|
||||||
|
if (methodName.equals(lexeme))
|
||||||
|
{
|
||||||
|
methods.forEach(method -> {
|
||||||
|
if (method.decRef.equalsIgnoreCase("declaration"))
|
||||||
|
{
|
||||||
|
moveCursor(method.line, method.columnStart);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, "Open Class");
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClassFileContainer wait(HashMap<String, ClassFileContainer> classFiles, ClassViewer activeResource)
|
||||||
|
{
|
||||||
|
String containerName = activeResource.resource.workingName + "-" + this.container.getDecompiler();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
BytecodeViewer.updateBusyStatus(true);
|
||||||
|
Thread.getAllStackTraces().forEach((name, stackTrace) -> {
|
||||||
|
if (name.getName().equals("Pane Update"))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
name.join();
|
||||||
|
} catch (InterruptedException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally
|
||||||
|
{
|
||||||
|
BytecodeViewer.updateBusyStatus(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return classFiles.get(containerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveCursor(int line, int columnStart)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
BytecodeViewPanel panel = ((ClassViewer) BytecodeViewer.viewer.workPane.getActiveResource()).getPanel(i);
|
||||||
|
if (panel.decompiler.getDecompilerName().equals(this.container.getDecompiler()))
|
||||||
|
{
|
||||||
|
Element root = panel.textArea.getDocument().getDefaultRootElement();
|
||||||
|
int startOffset = root.getElement(line - 1).getStartOffset() + (columnStart - 1);
|
||||||
|
panel.textArea.setCaretPosition(startOffset);
|
||||||
|
for (CaretListener caretListener : panel.textArea.getCaretListeners())
|
||||||
|
{
|
||||||
|
if (caretListener instanceof BytecodeViewPanelUpdater.MarkerCaretListener)
|
||||||
|
{
|
||||||
|
BytecodeViewPanelUpdater.MarkerCaretListener markerCaretListener = (BytecodeViewPanelUpdater.MarkerCaretListener) caretListener;
|
||||||
|
markerCaretListener.caretUpdate(new CaretEvent(panel.textArea)
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public int getDot()
|
||||||
|
{
|
||||||
|
return panel.textArea.getCaret().getDot();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMark()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panel.textArea.requestFocusInWindow();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.FileViewer;
|
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.FileViewer;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ResourceViewer;
|
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ResourceViewer;
|
||||||
import the.bytecode.club.bytecodeviewer.resources.ResourceContainer;
|
import the.bytecode.club.bytecodeviewer.resources.ResourceContainer;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.classcontainer.ClassFileContainer;
|
||||||
import the.bytecode.club.bytecodeviewer.translation.TranslatedComponents;
|
import the.bytecode.club.bytecodeviewer.translation.TranslatedComponents;
|
||||||
import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings;
|
import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings;
|
||||||
import the.bytecode.club.bytecodeviewer.translation.components.TranslatedJButton;
|
import the.bytecode.club.bytecodeviewer.translation.components.TranslatedJButton;
|
||||||
|
@ -33,13 +34,10 @@ import the.bytecode.club.uikit.tabpopup.closer.PopupMenuTabsCloseConfiguration;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.MouseEvent;
|
import java.util.HashMap;
|
||||||
import java.awt.event.MouseListener;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static the.bytecode.club.bytecodeviewer.Constants.BLOCK_TAB_MENU;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This pane contains all the resources, as tabs.
|
* This pane contains all the resources, as tabs.
|
||||||
*
|
*
|
||||||
|
@ -54,6 +52,7 @@ public class Workspace extends TranslatedVisibleComponent {
|
||||||
public final JPanel buttonPanel;
|
public final JPanel buttonPanel;
|
||||||
public final JButton refreshClass;
|
public final JButton refreshClass;
|
||||||
public final Set<String> openedTabs = new HashSet<>();
|
public final Set<String> openedTabs = new HashSet<>();
|
||||||
|
public HashMap<String, ClassFileContainer> classFiles = new HashMap<>();
|
||||||
|
|
||||||
public Workspace() {
|
public Workspace() {
|
||||||
super("Workspace", TranslatedComponents.WORK_SPACE);
|
super("Workspace", TranslatedComponents.WORK_SPACE);
|
||||||
|
|
|
@ -51,306 +51,316 @@ import static the.bytecode.club.bytecodeviewer.util.MethodParser.Method;
|
||||||
|
|
||||||
public class ClassViewer extends ResourceViewer
|
public class ClassViewer extends ResourceViewer
|
||||||
{
|
{
|
||||||
public JSplitPane sp;
|
public JSplitPane sp;
|
||||||
public JSplitPane sp2;
|
public JSplitPane sp2;
|
||||||
public BytecodeViewPanel bytecodeViewPanel1 = new BytecodeViewPanel(0, this);
|
public BytecodeViewPanel bytecodeViewPanel1 = new BytecodeViewPanel(0, this);
|
||||||
public BytecodeViewPanel bytecodeViewPanel2 = new BytecodeViewPanel(1, this);
|
public BytecodeViewPanel bytecodeViewPanel2 = new BytecodeViewPanel(1, this);
|
||||||
public BytecodeViewPanel bytecodeViewPanel3 = new BytecodeViewPanel(2, this);
|
public BytecodeViewPanel bytecodeViewPanel3 = new BytecodeViewPanel(2, this);
|
||||||
public List<MethodParser> methods = Arrays.asList(new MethodParser(), new MethodParser(), new MethodParser());
|
public List<MethodParser> methods = Arrays.asList(new MethodParser(), new MethodParser(), new MethodParser());
|
||||||
|
|
||||||
public ClassViewer(ResourceContainer container, String name)
|
public ClassViewer(ResourceContainer container, String name)
|
||||||
{
|
{
|
||||||
super(new Resource(name, container.getWorkingName(name), container));
|
super(new Resource(name, container.getWorkingName(name), container));
|
||||||
|
|
||||||
this.setName(name);
|
this.setName(name);
|
||||||
this.setLayout(new BorderLayout());
|
this.setLayout(new BorderLayout());
|
||||||
this.sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, bytecodeViewPanel1, bytecodeViewPanel2);
|
this.sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, bytecodeViewPanel1, bytecodeViewPanel2);
|
||||||
this.sp2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, sp, bytecodeViewPanel3);
|
this.sp2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, sp, bytecodeViewPanel3);
|
||||||
this.add(sp2, BorderLayout.CENTER);
|
this.add(sp2, BorderLayout.CENTER);
|
||||||
|
|
||||||
this.addComponentListener(new ComponentAdapter()
|
this.addComponentListener(new ComponentAdapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void componentResized(ComponentEvent e)
|
public void componentResized(ComponentEvent e)
|
||||||
{
|
{
|
||||||
resetDivider();
|
resetDivider();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void refresh(JButton button)
|
public void refresh(JButton button)
|
||||||
{
|
{
|
||||||
setPanes();
|
setPanes();
|
||||||
refreshTitle();
|
refreshTitle();
|
||||||
|
|
||||||
bytecodeViewPanel1.createPane(this);
|
bytecodeViewPanel1.createPane(this);
|
||||||
bytecodeViewPanel2.createPane(this);
|
bytecodeViewPanel2.createPane(this);
|
||||||
bytecodeViewPanel3.createPane(this);
|
bytecodeViewPanel3.createPane(this);
|
||||||
|
|
||||||
byte[] classBytes = getResourceBytes();
|
byte[] classBytes = getResourceBytes();
|
||||||
|
|
||||||
//TODO remove this once all of the importers have been properly updated to use a FileContainerImporter
|
//TODO remove this once all of the importers have been properly updated to use a FileContainerImporter
|
||||||
if (classBytes == null || classBytes.length == 0 || Configuration.forceResourceUpdateFromClassNode)
|
if (classBytes == null || classBytes.length == 0 || Configuration.forceResourceUpdateFromClassNode)
|
||||||
{
|
{
|
||||||
//TODO remove this error message when all of the importers have been updated
|
//TODO remove this error message when all of the importers have been updated
|
||||||
// only APK and DEX are left
|
// only APK and DEX are left
|
||||||
if (!Configuration.forceResourceUpdateFromClassNode)
|
if (!Configuration.forceResourceUpdateFromClassNode)
|
||||||
{
|
{
|
||||||
System.err.println("WARNING: Class Resource imported using the old importer!");
|
System.err.println("WARNING: Class Resource imported using the old importer!");
|
||||||
System.err.println("TODO: Update it to use the FileContainerImporter");
|
System.err.println("TODO: Update it to use the FileContainerImporter");
|
||||||
}
|
}
|
||||||
|
|
||||||
classBytes = ASMUtil.nodeToBytes(resource.getResourceClassNode());
|
classBytes = ASMUtil.nodeToBytes(resource.getResourceClassNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
bytecodeViewPanel1.updatePane(this, classBytes, button, isPanel1Editable());
|
bytecodeViewPanel1.updatePane(this, classBytes, button, isPanel1Editable());
|
||||||
bytecodeViewPanel2.updatePane(this, classBytes, button, isPanel2Editable());
|
bytecodeViewPanel2.updatePane(this, classBytes, button, isPanel2Editable());
|
||||||
bytecodeViewPanel3.updatePane(this, classBytes, button, isPanel3Editable());
|
bytecodeViewPanel3.updatePane(this, classBytes, button, isPanel3Editable());
|
||||||
|
|
||||||
Thread dumpBuild = new Thread(() ->
|
Thread dumpBuild = new Thread(() ->
|
||||||
{
|
{
|
||||||
BytecodeViewer.updateBusyStatus(true);
|
BytecodeViewer.updateBusyStatus(true);
|
||||||
|
|
||||||
while (Configuration.currentlyDumping)
|
while (Configuration.currentlyDumping)
|
||||||
{
|
{
|
||||||
//wait until it's not dumping
|
//wait until it's not dumping
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
}
|
} catch (InterruptedException e)
|
||||||
catch (InterruptedException e)
|
{
|
||||||
{
|
e.printStackTrace();
|
||||||
e.printStackTrace();
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
BytecodeViewer.updateBusyStatus(false);
|
BytecodeViewer.updateBusyStatus(false);
|
||||||
|
|
||||||
if (bytecodeViewPanel1.decompiler != Decompiler.NONE)
|
if (bytecodeViewPanel1.decompiler != Decompiler.NONE)
|
||||||
bytecodeViewPanel1.updateThread.startNewThread();
|
bytecodeViewPanel1.updateThread.startNewThread();
|
||||||
|
|
||||||
if (bytecodeViewPanel2.decompiler != Decompiler.NONE)
|
if (bytecodeViewPanel2.decompiler != Decompiler.NONE)
|
||||||
bytecodeViewPanel2.updateThread.startNewThread();
|
bytecodeViewPanel2.updateThread.startNewThread();
|
||||||
|
|
||||||
if (bytecodeViewPanel3.decompiler != Decompiler.NONE)
|
if (bytecodeViewPanel3.decompiler != Decompiler.NONE)
|
||||||
bytecodeViewPanel3.updateThread.startNewThread();
|
bytecodeViewPanel3.updateThread.startNewThread();
|
||||||
}, "ClassViewer Temp Dump");
|
}, "ClassViewer Temp Dump");
|
||||||
|
|
||||||
dumpBuild.start();
|
dumpBuild.start();
|
||||||
|
|
||||||
if (isPanel1Editable() || isPanel2Editable() || isPanel3Editable())
|
if (isPanel1Editable() || isPanel2Editable() || isPanel3Editable())
|
||||||
{
|
{
|
||||||
if (Configuration.warnForEditing)
|
if (Configuration.warnForEditing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Configuration.warnForEditing = true;
|
Configuration.warnForEditing = true;
|
||||||
|
|
||||||
if (!BytecodeViewer.viewer.autoCompileOnRefresh.isSelected() && !BytecodeViewer.viewer.compileOnSave.isSelected())
|
if (!BytecodeViewer.viewer.autoCompileOnRefresh.isSelected() && !BytecodeViewer.viewer.compileOnSave.isSelected())
|
||||||
{
|
{
|
||||||
BytecodeViewer.showMessage("Make sure to compile (File>Compile or Ctrl + T) whenever you want to " + "test or export your changes.\nYou can set compile automatically on refresh or on save " + "in the settings menu.");
|
BytecodeViewer.showMessage("Make sure to compile (File>Compile or Ctrl + T) whenever you want to " + "test or export your changes.\nYou can set compile automatically on refresh or on save " + "in the settings menu.");
|
||||||
|
|
||||||
SettingsSerializer.saveSettingsAsync();
|
SettingsSerializer.saveSettingsAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPanes()
|
public void setPanes()
|
||||||
{
|
{
|
||||||
bytecodeViewPanel1.decompiler = BytecodeViewer.viewer.viewPane1.getSelectedDecompiler();
|
bytecodeViewPanel1.decompiler = BytecodeViewer.viewer.viewPane1.getSelectedDecompiler();
|
||||||
bytecodeViewPanel2.decompiler = BytecodeViewer.viewer.viewPane2.getSelectedDecompiler();
|
bytecodeViewPanel2.decompiler = BytecodeViewer.viewer.viewPane2.getSelectedDecompiler();
|
||||||
bytecodeViewPanel3.decompiler = BytecodeViewer.viewer.viewPane3.getSelectedDecompiler();
|
bytecodeViewPanel3.decompiler = BytecodeViewer.viewer.viewPane3.getSelectedDecompiler();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPanel1Editable()
|
public boolean isPanel1Editable()
|
||||||
{
|
{
|
||||||
setPanes();
|
setPanes();
|
||||||
return BytecodeViewer.viewer.viewPane1.isPaneEditable();
|
return BytecodeViewer.viewer.viewPane1.isPaneEditable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPanel2Editable()
|
public boolean isPanel2Editable()
|
||||||
{
|
{
|
||||||
setPanes();
|
setPanes();
|
||||||
return BytecodeViewer.viewer.viewPane2.isPaneEditable();
|
return BytecodeViewer.viewer.viewPane2.isPaneEditable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPanel3Editable()
|
public boolean isPanel3Editable()
|
||||||
{
|
{
|
||||||
setPanes();
|
setPanes();
|
||||||
return BytecodeViewer.viewer.viewPane3.isPaneEditable();
|
return BytecodeViewer.viewer.viewPane3.isPaneEditable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BytecodeViewPanel getPanel(int index)
|
||||||
|
{
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return bytecodeViewPanel1;
|
||||||
|
case 1:
|
||||||
|
return bytecodeViewPanel2;
|
||||||
|
case 2:
|
||||||
|
return bytecodeViewPanel3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void selectMethod(RSyntaxTextArea area, int methodLine)
|
public static void selectMethod(RSyntaxTextArea area, int methodLine)
|
||||||
{
|
{
|
||||||
if (methodLine != area.getCaretLineNumber())
|
if (methodLine != area.getCaretLineNumber())
|
||||||
{
|
{
|
||||||
setCaretLine(area, methodLine);
|
setCaretLine(area, methodLine);
|
||||||
setViewLine(area, methodLine);
|
setViewLine(area, methodLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void selectMethod(ClassViewer classViewer, int paneId, Method method)
|
public static void selectMethod(ClassViewer classViewer, int paneId, Method method)
|
||||||
{
|
{
|
||||||
RSyntaxTextArea area = null;
|
RSyntaxTextArea area = null;
|
||||||
switch (paneId)
|
switch (paneId)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
area = classViewer.bytecodeViewPanel1.updateThread.updateUpdaterTextArea;
|
area = classViewer.bytecodeViewPanel1.updateThread.updateUpdaterTextArea;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
area = classViewer.bytecodeViewPanel2.updateThread.updateUpdaterTextArea;
|
area = classViewer.bytecodeViewPanel2.updateThread.updateUpdaterTextArea;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
area = classViewer.bytecodeViewPanel3.updateThread.updateUpdaterTextArea;
|
area = classViewer.bytecodeViewPanel3.updateThread.updateUpdaterTextArea;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (area != null)
|
if (area != null)
|
||||||
{
|
{
|
||||||
MethodParser methods = classViewer.methods.get(paneId);
|
MethodParser methods = classViewer.methods.get(paneId);
|
||||||
if (methods != null)
|
if (methods != null)
|
||||||
{
|
{
|
||||||
int methodLine = methods.findMethod(method);
|
int methodLine = methods.findMethod(method);
|
||||||
|
|
||||||
if (methodLine != -1)
|
if (methodLine != -1)
|
||||||
selectMethod(area, methodLine);
|
selectMethod(area, methodLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getMaxViewLine(RSyntaxTextArea area)
|
public static int getMaxViewLine(RSyntaxTextArea area)
|
||||||
{
|
{
|
||||||
Container parent = area.getParent();
|
Container parent = area.getParent();
|
||||||
|
|
||||||
if (parent instanceof JViewport)
|
if (parent instanceof JViewport)
|
||||||
{
|
{
|
||||||
JViewport viewport = (JViewport) parent;
|
JViewport viewport = (JViewport) parent;
|
||||||
int y = viewport.getViewSize().height - viewport.getExtentSize().height;
|
int y = viewport.getViewSize().height - viewport.getExtentSize().height;
|
||||||
int lineHeight = area.getLineHeight();
|
int lineHeight = area.getLineHeight();
|
||||||
return y >= lineHeight ? y / lineHeight : 0;
|
return y >= lineHeight ? y / lineHeight : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getViewLine(RSyntaxTextArea area)
|
public static int getViewLine(RSyntaxTextArea area)
|
||||||
{
|
{
|
||||||
Container parent = area.getParent();
|
Container parent = area.getParent();
|
||||||
|
|
||||||
if (parent instanceof JViewport)
|
if (parent instanceof JViewport)
|
||||||
{
|
{
|
||||||
JViewport viewport = (JViewport) parent;
|
JViewport viewport = (JViewport) parent;
|
||||||
Point point = viewport.getViewPosition();
|
Point point = viewport.getViewPosition();
|
||||||
int lineHeight = area.getLineHeight();
|
int lineHeight = area.getLineHeight();
|
||||||
return point.y >= lineHeight ? point.y / lineHeight : 0;
|
return point.y >= lineHeight ? point.y / lineHeight : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setViewLine(RSyntaxTextArea area, int line)
|
public static void setViewLine(RSyntaxTextArea area, int line)
|
||||||
{
|
{
|
||||||
Container parent = area.getParent();
|
Container parent = area.getParent();
|
||||||
|
|
||||||
if (parent instanceof JViewport)
|
if (parent instanceof JViewport)
|
||||||
{
|
{
|
||||||
JViewport viewport = (JViewport) parent;
|
JViewport viewport = (JViewport) parent;
|
||||||
int maxLine = ClassViewer.getMaxViewLine(area);
|
int maxLine = ClassViewer.getMaxViewLine(area);
|
||||||
line = Math.min(line, maxLine);
|
line = Math.min(line, maxLine);
|
||||||
viewport.setViewPosition(new Point(0, line * area.getLineHeight()));
|
viewport.setViewPosition(new Point(0, line * area.getLineHeight()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setCaretLine(RSyntaxTextArea area, int line)
|
public static void setCaretLine(RSyntaxTextArea area, int line)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
area.setCaretPosition(area.getLineStartOffset(line));
|
area.setCaretPosition(area.getLineStartOffset(line));
|
||||||
}
|
} catch (BadLocationException ignored)
|
||||||
catch (BadLocationException ignored)
|
{
|
||||||
{
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void resetDivider()
|
public void resetDivider()
|
||||||
{
|
{
|
||||||
SwingUtilities.invokeLater(() ->
|
SwingUtilities.invokeLater(() ->
|
||||||
{
|
{
|
||||||
sp.setResizeWeight(0.5);
|
sp.setResizeWeight(0.5);
|
||||||
|
|
||||||
if (bytecodeViewPanel2.decompiler != Decompiler.NONE && bytecodeViewPanel1.decompiler != Decompiler.NONE)
|
if (bytecodeViewPanel2.decompiler != Decompiler.NONE && bytecodeViewPanel1.decompiler != Decompiler.NONE)
|
||||||
setDividerLocation(sp, 0.5);
|
setDividerLocation(sp, 0.5);
|
||||||
else if (bytecodeViewPanel1.decompiler != Decompiler.NONE)
|
else if (bytecodeViewPanel1.decompiler != Decompiler.NONE)
|
||||||
setDividerLocation(sp, 1);
|
setDividerLocation(sp, 1);
|
||||||
else if (bytecodeViewPanel2.decompiler != Decompiler.NONE)
|
else if (bytecodeViewPanel2.decompiler != Decompiler.NONE)
|
||||||
{
|
{
|
||||||
sp.setResizeWeight(1);
|
sp.setResizeWeight(1);
|
||||||
setDividerLocation(sp, 0);
|
setDividerLocation(sp, 0);
|
||||||
}
|
} else
|
||||||
else
|
setDividerLocation(sp, 0);
|
||||||
setDividerLocation(sp, 0);
|
|
||||||
|
|
||||||
if (bytecodeViewPanel3.decompiler != Decompiler.NONE)
|
if (bytecodeViewPanel3.decompiler != Decompiler.NONE)
|
||||||
{
|
{
|
||||||
sp2.setResizeWeight(0.7);
|
sp2.setResizeWeight(0.7);
|
||||||
setDividerLocation(sp2, 0.7);
|
setDividerLocation(sp2, 0.7);
|
||||||
if ((bytecodeViewPanel2.decompiler == Decompiler.NONE && bytecodeViewPanel1.decompiler != Decompiler.NONE) || (bytecodeViewPanel1.decompiler == Decompiler.NONE && bytecodeViewPanel2.decompiler != Decompiler.NONE))
|
if ((bytecodeViewPanel2.decompiler == Decompiler.NONE && bytecodeViewPanel1.decompiler != Decompiler.NONE) || (bytecodeViewPanel1.decompiler == Decompiler.NONE && bytecodeViewPanel2.decompiler != Decompiler.NONE))
|
||||||
setDividerLocation(sp2, 0.5);
|
setDividerLocation(sp2, 0.5);
|
||||||
else if (bytecodeViewPanel1.decompiler == Decompiler.NONE)
|
else if (bytecodeViewPanel1.decompiler == Decompiler.NONE)
|
||||||
setDividerLocation(sp2, 0);
|
setDividerLocation(sp2, 0);
|
||||||
}
|
} else
|
||||||
else
|
{
|
||||||
{
|
sp.setResizeWeight(1);
|
||||||
sp.setResizeWeight(1);
|
sp2.setResizeWeight(0);
|
||||||
sp2.setResizeWeight(0);
|
setDividerLocation(sp2, 1);
|
||||||
setDividerLocation(sp2, 1);
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whoever wrote this function, THANK YOU!
|
* Whoever wrote this function, THANK YOU!
|
||||||
*/
|
*/
|
||||||
public static JSplitPane setDividerLocation(JSplitPane splitter, double proportion)
|
public static JSplitPane setDividerLocation(JSplitPane splitter, double proportion)
|
||||||
{
|
{
|
||||||
if (splitter.isShowing())
|
if (splitter.isShowing())
|
||||||
{
|
{
|
||||||
if (splitter.getWidth() > 0 && splitter.getHeight() > 0)
|
if (splitter.getWidth() > 0 && splitter.getHeight() > 0)
|
||||||
splitter.setDividerLocation(proportion);
|
splitter.setDividerLocation(proportion);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
splitter.addComponentListener(new ComponentAdapter()
|
splitter.addComponentListener(new ComponentAdapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void componentResized(ComponentEvent ce)
|
public void componentResized(ComponentEvent ce)
|
||||||
{
|
{
|
||||||
splitter.removeComponentListener(this);
|
splitter.removeComponentListener(this);
|
||||||
setDividerLocation(splitter, proportion);
|
setDividerLocation(splitter, proportion);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
} else
|
||||||
else
|
{
|
||||||
{
|
splitter.addHierarchyListener(new HierarchyListener()
|
||||||
splitter.addHierarchyListener(new HierarchyListener()
|
{
|
||||||
{
|
@Override
|
||||||
@Override
|
public void hierarchyChanged(HierarchyEvent e)
|
||||||
public void hierarchyChanged(HierarchyEvent e)
|
{
|
||||||
{
|
if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 && splitter.isShowing())
|
||||||
if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 && splitter.isShowing())
|
{
|
||||||
{
|
splitter.removeHierarchyListener(this);
|
||||||
splitter.removeHierarchyListener(this);
|
setDividerLocation(splitter, proportion);
|
||||||
setDividerLocation(splitter, proportion);
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return splitter;
|
return splitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final long serialVersionUID = -8650495368920680024L;
|
private static final long serialVersionUID = -8650495368920680024L;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,35 +18,38 @@
|
||||||
|
|
||||||
package the.bytecode.club.bytecodeviewer.gui.util;
|
package the.bytecode.club.bytecodeviewer.gui.util;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import org.fife.ui.rsyntaxtextarea.*;
|
||||||
import java.util.Objects;
|
import org.fife.ui.rtextarea.SmartHighlightPainter;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JComboBox;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JViewport;
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import javax.swing.event.CaretEvent;
|
|
||||||
import javax.swing.event.CaretListener;
|
|
||||||
import javax.swing.event.ChangeEvent;
|
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
|
|
||||||
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.objectweb.asm.ClassWriter;
|
import org.objectweb.asm.ClassWriter;
|
||||||
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
import the.bytecode.club.bytecodeviewer.Configuration;
|
import the.bytecode.club.bytecodeviewer.Configuration;
|
||||||
import the.bytecode.club.bytecodeviewer.compilers.Compiler;
|
import the.bytecode.club.bytecodeviewer.compilers.Compiler;
|
||||||
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
|
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.components.MethodsRenderer;
|
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.components.SearchableRSyntaxTextArea;
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.components.actions.GoToAction;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.hexviewer.HexViewer;
|
import the.bytecode.club.bytecodeviewer.gui.hexviewer.HexViewer;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.BytecodeViewPanel;
|
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.BytecodeViewPanel;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
|
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.*;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.classcontainer.parser.TokenUtil;
|
||||||
import the.bytecode.club.bytecodeviewer.util.MethodParser;
|
import the.bytecode.club.bytecodeviewer.util.MethodParser;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
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 java.awt.*;
|
||||||
|
import java.awt.event.*;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
import static the.bytecode.club.bytecodeviewer.gui.resourceviewer.TabbedPane.BLANK_COLOR;
|
import static the.bytecode.club.bytecodeviewer.gui.resourceviewer.TabbedPane.BLANK_COLOR;
|
||||||
import static the.bytecode.club.bytecodeviewer.translation.TranslatedStrings.EDITABLE;
|
import static the.bytecode.club.bytecodeviewer.translation.TranslatedStrings.EDITABLE;
|
||||||
|
|
||||||
|
@ -66,7 +69,8 @@ public class BytecodeViewPanelUpdater implements Runnable
|
||||||
private final JButton button;
|
private final JButton button;
|
||||||
private final byte[] classBytes;
|
private final byte[] classBytes;
|
||||||
|
|
||||||
|
public MarkerCaretListener markerCaretListener;
|
||||||
|
private MyErrorStripe errorStripe;
|
||||||
public SearchableRSyntaxTextArea updateUpdaterTextArea;
|
public SearchableRSyntaxTextArea updateUpdaterTextArea;
|
||||||
public JComboBox<Integer> methodsList;
|
public JComboBox<Integer> methodsList;
|
||||||
public boolean isPanelEditable;
|
public boolean isPanelEditable;
|
||||||
|
@ -102,14 +106,21 @@ public class BytecodeViewPanelUpdater implements Runnable
|
||||||
final HexViewer hex = new HexViewer(cw.toByteArray());
|
final HexViewer hex = new HexViewer(cw.toByteArray());
|
||||||
bytecodeViewPanel.add(hex);
|
bytecodeViewPanel.add(hex);
|
||||||
});
|
});
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
final Decompiler decompiler = bytecodeViewPanel.decompiler;
|
final Decompiler decompiler = bytecodeViewPanel.decompiler;
|
||||||
|
|
||||||
//perform decompiling inside of this thread
|
//perform decompiling inside of this thread
|
||||||
final String decompiledSource = decompiler.getDecompiler().decompileClassNode(viewer.resource.getResourceClassNode(), classBytes);
|
final String decompiledSource = decompiler.getDecompiler().decompileClassNode(viewer.resource.getResourceClassNode(), classBytes);
|
||||||
|
|
||||||
|
ClassFileContainer container = new ClassFileContainer(viewer.resource.workingName + "-" + decompiler.getDecompilerName(), decompiledSource, viewer.resource.container);
|
||||||
|
if (!BytecodeViewer.viewer.workPane.classFiles.containsKey(viewer.resource.workingName + "-" + decompiler.getDecompilerName()))
|
||||||
|
{
|
||||||
|
container.parse();
|
||||||
|
BytecodeViewer.viewer.workPane.classFiles.put(viewer.resource.workingName + "-" + decompiler.getDecompilerName(), container);
|
||||||
|
container.hasBeenParsed = true;
|
||||||
|
}
|
||||||
|
|
||||||
//set the swing components on the swing thread
|
//set the swing components on the swing thread
|
||||||
SwingUtilities.invokeLater(() ->
|
SwingUtilities.invokeLater(() ->
|
||||||
{
|
{
|
||||||
|
@ -120,22 +131,22 @@ public class BytecodeViewPanelUpdater implements Runnable
|
||||||
//hold this thread until the swing thread has finished attaching the components
|
//hold this thread until the swing thread has finished attaching the components
|
||||||
while (waitingFor)
|
while (waitingFor)
|
||||||
{
|
{
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
Thread.sleep(1);
|
Thread.sleep(1);
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} catch (IndexOutOfBoundsException | NullPointerException e)
|
||||||
catch (IndexOutOfBoundsException | NullPointerException e)
|
|
||||||
{
|
{
|
||||||
//ignore
|
//ignore
|
||||||
}
|
} catch (Exception e)
|
||||||
catch (Exception e)
|
|
||||||
{
|
{
|
||||||
BytecodeViewer.handleException(e);
|
BytecodeViewer.handleException(e);
|
||||||
}
|
} finally
|
||||||
finally
|
|
||||||
{
|
{
|
||||||
viewer.resetDivider();
|
viewer.resetDivider();
|
||||||
BytecodeViewer.updateBusyStatus(false);
|
BytecodeViewer.updateBusyStatus(false);
|
||||||
|
@ -156,16 +167,16 @@ public class BytecodeViewPanelUpdater implements Runnable
|
||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
if(bytecodeViewPanel.decompiler == Decompiler.NONE)
|
if (bytecodeViewPanel.decompiler == Decompiler.NONE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
processDisplay();
|
processDisplay();
|
||||||
|
|
||||||
if(bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER)
|
if (bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//nullcheck broken pane
|
//nullcheck broken pane
|
||||||
if(updateUpdaterTextArea == null || updateUpdaterTextArea.getScrollPane() == null
|
if (updateUpdaterTextArea == null || updateUpdaterTextArea.getScrollPane() == null
|
||||||
|| updateUpdaterTextArea.getScrollPane().getViewport() == null)
|
|| updateUpdaterTextArea.getScrollPane().getViewport() == null)
|
||||||
{
|
{
|
||||||
//build an error message
|
//build an error message
|
||||||
|
@ -188,21 +199,28 @@ public class BytecodeViewPanelUpdater implements Runnable
|
||||||
{
|
{
|
||||||
int methodLine = methods.findActiveMethod(updateUpdaterTextArea.getCaretLineNumber());
|
int methodLine = methods.findActiveMethod(updateUpdaterTextArea.getCaretLineNumber());
|
||||||
|
|
||||||
if (methodLine != -1) {
|
if (methodLine != -1)
|
||||||
if (BytecodeViewer.viewer.showClassMethods.isSelected()) {
|
{
|
||||||
if (methodsList != null) {
|
if (BytecodeViewer.viewer.showClassMethods.isSelected())
|
||||||
if (methodLine != (int) Objects.requireNonNull(methodsList.getSelectedItem())) {
|
{
|
||||||
|
if (methodsList != null)
|
||||||
|
{
|
||||||
|
if (methodLine != (int) Objects.requireNonNull(methodsList.getSelectedItem()))
|
||||||
|
{
|
||||||
methodsList.setSelectedItem(methodLine);
|
methodsList.setSelectedItem(methodLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (BytecodeViewer.viewer.synchronizedViewing.isSelected()) {
|
if (BytecodeViewer.viewer.synchronizedViewing.isSelected())
|
||||||
|
{
|
||||||
int panes = 2;
|
int panes = 2;
|
||||||
if (viewer.bytecodeViewPanel3 != null)
|
if (viewer.bytecodeViewPanel3 != null)
|
||||||
panes = 3;
|
panes = 3;
|
||||||
|
|
||||||
for (int i = 0; i < panes; i++) {
|
for (int i = 0; i < panes; i++)
|
||||||
if (i != bytecodeViewPanel.panelIndex) {
|
{
|
||||||
|
if (i != bytecodeViewPanel.panelIndex)
|
||||||
|
{
|
||||||
ClassViewer.selectMethod(viewer, i, methods.getMethod(methodLine));
|
ClassViewer.selectMethod(viewer, i, methods.getMethod(methodLine));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,15 +230,19 @@ public class BytecodeViewPanelUpdater implements Runnable
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public final ChangeListener viewportListener = new ChangeListener() {
|
public final ChangeListener viewportListener = new ChangeListener()
|
||||||
|
{
|
||||||
@Override
|
@Override
|
||||||
public void stateChanged(ChangeEvent e) {
|
public void stateChanged(ChangeEvent e)
|
||||||
|
{
|
||||||
int panes = 2;
|
int panes = 2;
|
||||||
if (viewer.bytecodeViewPanel3 != null)
|
if (viewer.bytecodeViewPanel3 != null)
|
||||||
panes = 3;
|
panes = 3;
|
||||||
|
|
||||||
if (BytecodeViewer.viewer.synchronizedViewing.isSelected()) {
|
if (BytecodeViewer.viewer.synchronizedViewing.isSelected())
|
||||||
if (updateUpdaterTextArea.isShowing() && (updateUpdaterTextArea.hasFocus() || updateUpdaterTextArea.getMousePosition() != null)) {
|
{
|
||||||
|
if (updateUpdaterTextArea.isShowing() && (updateUpdaterTextArea.hasFocus() || updateUpdaterTextArea.getMousePosition() != null))
|
||||||
|
{
|
||||||
int caretLine = updateUpdaterTextArea.getCaretLineNumber();
|
int caretLine = updateUpdaterTextArea.getCaretLineNumber();
|
||||||
int maxViewLine = ClassViewer.getMaxViewLine(updateUpdaterTextArea);
|
int maxViewLine = ClassViewer.getMaxViewLine(updateUpdaterTextArea);
|
||||||
int activeViewLine = ClassViewer.getViewLine(updateUpdaterTextArea);
|
int activeViewLine = ClassViewer.getViewLine(updateUpdaterTextArea);
|
||||||
|
@ -229,47 +251,59 @@ public class BytecodeViewPanelUpdater implements Runnable
|
||||||
int activeLineDelta = -1;
|
int activeLineDelta = -1;
|
||||||
MethodParser.Method activeMethod = null;
|
MethodParser.Method activeMethod = null;
|
||||||
MethodParser activeMethods = viewer.methods.get(bytecodeViewPanel.panelIndex);
|
MethodParser activeMethods = viewer.methods.get(bytecodeViewPanel.panelIndex);
|
||||||
if (activeMethods != null) {
|
if (activeMethods != null)
|
||||||
|
{
|
||||||
int activeMethodLine = activeMethods.findActiveMethod(activeLine);
|
int activeMethodLine = activeMethods.findActiveMethod(activeLine);
|
||||||
if (activeMethodLine != -1) {
|
if (activeMethodLine != -1)
|
||||||
|
{
|
||||||
activeLineDelta = activeLine - activeMethodLine;
|
activeLineDelta = activeLine - activeMethodLine;
|
||||||
activeMethod = activeMethods.getMethod(activeMethodLine);
|
activeMethod = activeMethods.getMethod(activeMethodLine);
|
||||||
ClassViewer.selectMethod(updateUpdaterTextArea, activeMethodLine);
|
ClassViewer.selectMethod(updateUpdaterTextArea, activeMethodLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0; i < panes; i++) {
|
for (int i = 0; i < panes; i++)
|
||||||
if (i != bytecodeViewPanel.panelIndex) {
|
{
|
||||||
|
if (i != bytecodeViewPanel.panelIndex)
|
||||||
|
{
|
||||||
int setLine = -1;
|
int setLine = -1;
|
||||||
|
|
||||||
RSyntaxTextArea area = null;
|
RSyntaxTextArea area = null;
|
||||||
switch (i) {
|
switch (i)
|
||||||
case 0:
|
{
|
||||||
area = viewer.bytecodeViewPanel1.updateThread.updateUpdaterTextArea;
|
case 0:
|
||||||
break;
|
area = viewer.bytecodeViewPanel1.updateThread.updateUpdaterTextArea;
|
||||||
case 1:
|
break;
|
||||||
area = viewer.bytecodeViewPanel2.updateThread.updateUpdaterTextArea;
|
case 1:
|
||||||
break;
|
area = viewer.bytecodeViewPanel2.updateThread.updateUpdaterTextArea;
|
||||||
case 2:
|
break;
|
||||||
area = viewer.bytecodeViewPanel3.updateThread.updateUpdaterTextArea;
|
case 2:
|
||||||
break;
|
area = viewer.bytecodeViewPanel3.updateThread.updateUpdaterTextArea;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (area != null) {
|
if (area != null)
|
||||||
if (activeMethod != null && activeLineDelta >= 0) {
|
{
|
||||||
|
if (activeMethod != null && activeLineDelta >= 0)
|
||||||
|
{
|
||||||
MethodParser methods = viewer.methods.get(i);
|
MethodParser methods = viewer.methods.get(i);
|
||||||
if (methods != null) {
|
if (methods != null)
|
||||||
|
{
|
||||||
int methodLine = methods.findMethod(activeMethod);
|
int methodLine = methods.findMethod(activeMethod);
|
||||||
if (methodLine != -1) {
|
if (methodLine != -1)
|
||||||
|
{
|
||||||
int viewLine = ClassViewer.getViewLine(area);
|
int viewLine = ClassViewer.getViewLine(area);
|
||||||
if (activeLineDelta != viewLine - methodLine) {
|
if (activeLineDelta != viewLine - methodLine)
|
||||||
|
{
|
||||||
setLine = methodLine + activeLineDelta;
|
setLine = methodLine + activeLineDelta;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (activeLine != ClassViewer.getViewLine(area)) {
|
} else if (activeLine != ClassViewer.getViewLine(area))
|
||||||
|
{
|
||||||
setLine = activeLine;
|
setLine = activeLine;
|
||||||
}
|
}
|
||||||
if (setLine >= 0) {
|
if (setLine >= 0)
|
||||||
|
{
|
||||||
ClassViewer.setViewLine(area, setLine);
|
ClassViewer.setViewLine(area, setLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -282,11 +316,11 @@ public class BytecodeViewPanelUpdater implements Runnable
|
||||||
|
|
||||||
public void synchronizePane()
|
public void synchronizePane()
|
||||||
{
|
{
|
||||||
if(bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER
|
if (bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER
|
||||||
|| bytecodeViewPanel.decompiler == Decompiler.NONE)
|
|| bytecodeViewPanel.decompiler == Decompiler.NONE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SwingUtilities.invokeLater(()->
|
SwingUtilities.invokeLater(() ->
|
||||||
{
|
{
|
||||||
JViewport viewport = updateUpdaterTextArea.getScrollPane().getViewport();
|
JViewport viewport = updateUpdaterTextArea.getScrollPane().getViewport();
|
||||||
viewport.addChangeListener(viewportListener);
|
viewport.addChangeListener(viewportListener);
|
||||||
|
@ -344,7 +378,7 @@ public class BytecodeViewPanelUpdater implements Runnable
|
||||||
panel.add(methodsList, BorderLayout.SOUTH);
|
panel.add(methodsList, BorderLayout.SOUTH);
|
||||||
methodsList.setBackground(BLANK_COLOR);
|
methodsList.setBackground(BLANK_COLOR);
|
||||||
|
|
||||||
SwingUtilities.invokeLater(()->
|
SwingUtilities.invokeLater(() ->
|
||||||
{
|
{
|
||||||
updateUpdaterTextArea.getScrollPane().getColumnHeader().removeAll();
|
updateUpdaterTextArea.getScrollPane().getColumnHeader().removeAll();
|
||||||
updateUpdaterTextArea.getScrollPane().getColumnHeader().add(panel);
|
updateUpdaterTextArea.getScrollPane().getColumnHeader().add(panel);
|
||||||
|
@ -358,29 +392,312 @@ public class BytecodeViewPanelUpdater implements Runnable
|
||||||
updateUpdaterTextArea = new SearchableRSyntaxTextArea();
|
updateUpdaterTextArea = new SearchableRSyntaxTextArea();
|
||||||
|
|
||||||
Configuration.rstaTheme.apply(updateUpdaterTextArea);
|
Configuration.rstaTheme.apply(updateUpdaterTextArea);
|
||||||
|
bytecodeViewPanel.add(updateUpdaterTextArea.getTextAreaSearchPanel(), BorderLayout.NORTH);
|
||||||
bytecodeViewPanel.add(updateUpdaterTextArea.getScrollPane());
|
bytecodeViewPanel.add(updateUpdaterTextArea.getScrollPane());
|
||||||
bytecodeViewPanel.add(updateUpdaterTextArea.getTitleHeader(), BorderLayout.NORTH);
|
|
||||||
|
|
||||||
bytecodeViewPanel.textArea = updateUpdaterTextArea;
|
bytecodeViewPanel.textArea = updateUpdaterTextArea;
|
||||||
if (bytecodeViewPanel.decompiler != Decompiler.BYTECODE_DISASSEMBLER) {
|
|
||||||
|
bytecodeViewPanel.textArea.setMarkOccurrencesColor(Color.ORANGE);
|
||||||
|
bytecodeViewPanel.textArea.setHighlighter(new RSyntaxTextAreaHighlighterEx());
|
||||||
|
|
||||||
|
if (bytecodeViewPanel.decompiler != Decompiler.BYTECODE_DISASSEMBLER)
|
||||||
|
{
|
||||||
bytecodeViewPanel.textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
|
bytecodeViewPanel.textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
|
||||||
} else {
|
} else
|
||||||
|
{
|
||||||
AbstractTokenMakerFactory tokenMakerFactory = (AbstractTokenMakerFactory) TokenMakerFactory.getDefaultInstance();
|
AbstractTokenMakerFactory tokenMakerFactory = (AbstractTokenMakerFactory) TokenMakerFactory.getDefaultInstance();
|
||||||
tokenMakerFactory.putMapping("text/javaBytecode", "the.bytecode.club.bytecodeviewer.decompilers.bytecode.JavaBytecodeTokenMaker");
|
tokenMakerFactory.putMapping("text/javaBytecode", "the.bytecode.club.bytecodeviewer.decompilers.bytecode.JavaBytecodeTokenMaker");
|
||||||
bytecodeViewPanel.textArea.setSyntaxEditingStyle("text/javaBytecode");
|
bytecodeViewPanel.textArea.setSyntaxEditingStyle("text/javaBytecode");
|
||||||
}
|
}
|
||||||
bytecodeViewPanel.textArea.setCodeFoldingEnabled(true);
|
bytecodeViewPanel.textArea.setCodeFoldingEnabled(true);
|
||||||
bytecodeViewPanel.textArea.setAntiAliasingEnabled(true);
|
|
||||||
bytecodeViewPanel.textArea.setText(decompiledSource);
|
bytecodeViewPanel.textArea.setText(decompiledSource);
|
||||||
bytecodeViewPanel.textArea.setCaretPosition(0);
|
bytecodeViewPanel.textArea.setCaretPosition(0);
|
||||||
bytecodeViewPanel.textArea.setEditable(isPanelEditable);
|
bytecodeViewPanel.textArea.setEditable(isPanelEditable);
|
||||||
|
|
||||||
if(isPanelEditable && decompiler == Decompiler.SMALI_DISASSEMBLER)
|
if (isPanelEditable && decompiler == Decompiler.SMALI_DISASSEMBLER)
|
||||||
bytecodeViewPanel.compiler = Compiler.SMALI_ASSEMBLER;
|
bytecodeViewPanel.compiler = Compiler.SMALI_ASSEMBLER;
|
||||||
else if(isPanelEditable && decompiler == Decompiler.KRAKATAU_DISASSEMBLER)
|
else if (isPanelEditable && decompiler == Decompiler.KRAKATAU_DISASSEMBLER)
|
||||||
bytecodeViewPanel.compiler = Compiler.KRAKATAU_ASSEMBLER;
|
bytecodeViewPanel.compiler = Compiler.KRAKATAU_ASSEMBLER;
|
||||||
|
|
||||||
String editable = isPanelEditable ? " - " + EDITABLE : "";
|
String editable = isPanelEditable ? " - " + EDITABLE : "";
|
||||||
bytecodeViewPanel.textArea.getTitleHeader().setText(decompiler.getDecompilerName() + editable);
|
bytecodeViewPanel.textArea.getTextAreaSearchPanel().getTitleHeader().setText(decompiler.getDecompilerName() + editable);
|
||||||
|
|
||||||
|
errorStripe = new MyErrorStripe(bytecodeViewPanel.textArea);
|
||||||
|
bytecodeViewPanel.add(errorStripe, BorderLayout.LINE_END);
|
||||||
|
|
||||||
|
bytecodeViewPanel.revalidate();
|
||||||
|
bytecodeViewPanel.repaint();
|
||||||
|
|
||||||
|
String classContainerName = viewer.resource.workingName + "-" + decompiler.getDecompilerName();
|
||||||
|
ClassFileContainer classFileContainer = BytecodeViewer.viewer.workPane.classFiles.get(classContainerName);
|
||||||
|
bytecodeViewPanel.textArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_B, InputEvent.CTRL_DOWN_MASK), "goToAction");
|
||||||
|
bytecodeViewPanel.textArea.getActionMap().put("goToAction", new GoToAction(classFileContainer));
|
||||||
|
|
||||||
|
bytecodeViewPanel.textArea.addMouseMotionListener(new MouseMotionAdapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void mouseMoved(MouseEvent e)
|
||||||
|
{
|
||||||
|
if (e.isControlDown())
|
||||||
|
{
|
||||||
|
RSyntaxTextArea textArea = (RSyntaxTextArea) e.getSource();
|
||||||
|
Token token = textArea.viewToToken(e.getPoint());
|
||||||
|
if (token != null)
|
||||||
|
{
|
||||||
|
String lexeme = token.getLexeme();
|
||||||
|
if (classFileContainer.fieldMembers.containsKey(lexeme) || classFileContainer.methodMembers.containsKey(lexeme)
|
||||||
|
|| classFileContainer.methodLocalMembers.containsKey(lexeme) || classFileContainer.methodParameterMembers.containsKey(lexeme)
|
||||||
|
|| classFileContainer.classReferences.containsKey(lexeme))
|
||||||
|
{
|
||||||
|
textArea.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
if (bytecodeViewPanel.textArea.getCursor().getType() != Cursor.TEXT_CURSOR)
|
||||||
|
{
|
||||||
|
bytecodeViewPanel.textArea.setCursor(new Cursor(Cursor.TEXT_CURSOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
bytecodeViewPanel.textArea.addMouseListener(new MouseAdapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e)
|
||||||
|
{
|
||||||
|
if (e.isControlDown())
|
||||||
|
{
|
||||||
|
RSyntaxTextArea textArea = (RSyntaxTextArea) e.getSource();
|
||||||
|
textArea.getActionMap().get("goToAction").actionPerformed(new ActionEvent(textArea, ActionEvent.ACTION_PERFORMED, "goToAction"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
markerCaretListener = new MarkerCaretListener(classContainerName);
|
||||||
|
bytecodeViewPanel.textArea.addCaretListener(markerCaretListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markOccurrences(RSyntaxTextArea textArea, ClassFileContainer classFileContainer, MyErrorStripe errorStripe)
|
||||||
|
{
|
||||||
|
RSyntaxTextAreaHighlighterEx highlighterEx = (RSyntaxTextAreaHighlighterEx) textArea.getHighlighter();
|
||||||
|
Token token = textArea.modelToToken(textArea.getCaretPosition());
|
||||||
|
if (token == null)
|
||||||
|
{
|
||||||
|
token = textArea.modelToToken(textArea.getCaretPosition() - 1);
|
||||||
|
if (token == null)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Methods
|
||||||
|
*/
|
||||||
|
markMethod(textArea, classFileContainer, line, column, finalToken, highlighterEx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Method parameters
|
||||||
|
*/
|
||||||
|
markMethodParameter(textArea, classFileContainer, line, column, finalToken, highlighterEx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Method local variables
|
||||||
|
*/
|
||||||
|
markMethodLocalVariable(textArea, classFileContainer, line, column, finalToken, highlighterEx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Class references
|
||||||
|
*/
|
||||||
|
markClasses(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 -> {
|
||||||
|
if (field.line == line && field.columnStart - 1 <= column && field.columnEnd >= column)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Element root = textArea.getDocument().getDefaultRootElement();
|
||||||
|
for (ClassFieldLocation location : classFileContainer.getFieldLocationsFor(finalToken.getLexeme()))
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markMethod(RSyntaxTextArea textArea, ClassFileContainer classFileContainer, int line, int column, Token finalToken, RSyntaxTextAreaHighlighterEx highlighterEx)
|
||||||
|
{
|
||||||
|
classFileContainer.methodMembers.values().forEach(methods -> methods.forEach(method -> {
|
||||||
|
String owner;
|
||||||
|
String parameters;
|
||||||
|
if (method.line == line && method.columnStart - 1 <= column && method.columnEnd >= column)
|
||||||
|
{
|
||||||
|
owner = method.owner;
|
||||||
|
parameters = method.methodParameterTypes;
|
||||||
|
Element root = textArea.getDocument().getDefaultRootElement();
|
||||||
|
for (ClassMethodLocation location : classFileContainer.getMethodLocationsFor(finalToken.getLexeme()))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Objects.equals(owner, location.owner) && Objects.equals(parameters, location.methodParameterTypes))
|
||||||
|
{
|
||||||
|
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 e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search through the text area and mark all occurrences that match the selected token.
|
||||||
|
*
|
||||||
|
* @param textArea the text area
|
||||||
|
* @param classFileContainer the container
|
||||||
|
* @param line the caret line
|
||||||
|
* @param column the caret column
|
||||||
|
* @param finalToken the token
|
||||||
|
* @param highlighterEx the highlighter
|
||||||
|
*/
|
||||||
|
private static void markMethodParameter(RSyntaxTextArea textArea, ClassFileContainer classFileContainer, int line, int column, Token finalToken, RSyntaxTextAreaHighlighterEx highlighterEx)
|
||||||
|
{
|
||||||
|
classFileContainer.methodParameterMembers.values().forEach(parameters -> parameters.forEach(parameter -> {
|
||||||
|
String method;
|
||||||
|
if (parameter.line == line && parameter.columnStart - 1 <= column && parameter.columnEnd >= column)
|
||||||
|
{
|
||||||
|
method = parameter.method;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Element root = textArea.getDocument().getDefaultRootElement();
|
||||||
|
for (ClassParameterLocation location : classFileContainer.getParameterLocationsFor(finalToken.getLexeme()))
|
||||||
|
{
|
||||||
|
if (Objects.equals(method, location.method))
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search through the text area and mark all occurrences that match the selected token.
|
||||||
|
*
|
||||||
|
* @param textArea the text area
|
||||||
|
* @param classFileContainer the container
|
||||||
|
* @param line the caret line
|
||||||
|
* @param column the caret column
|
||||||
|
* @param finalToken the token
|
||||||
|
* @param highlighterEx the highlighter
|
||||||
|
*/
|
||||||
|
private static void markMethodLocalVariable(RSyntaxTextArea textArea, ClassFileContainer classFileContainer, int line, int column, Token finalToken, RSyntaxTextAreaHighlighterEx highlighterEx)
|
||||||
|
{
|
||||||
|
classFileContainer.methodLocalMembers.values().forEach(localVariables -> localVariables.forEach(localVariable -> {
|
||||||
|
String method;
|
||||||
|
if (localVariable.line == line && localVariable.columnStart - 1 <= column && localVariable.columnEnd >= column)
|
||||||
|
{
|
||||||
|
method = localVariable.method;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Element root = textArea.getDocument().getDefaultRootElement();
|
||||||
|
for (ClassLocalVariableLocation location : classFileContainer.getLocalLocationsFor(finalToken.getLexeme()))
|
||||||
|
{
|
||||||
|
if (Objects.equals(method, location.method))
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markClasses(RSyntaxTextArea textArea, ClassFileContainer classFileContainer, int line, int column, Token finalToken, RSyntaxTextAreaHighlighterEx highlighterEx)
|
||||||
|
{
|
||||||
|
classFileContainer.classReferences.values().forEach(classes -> classes.forEach(clazz -> {
|
||||||
|
if (clazz.line == line && clazz.columnStart - 1 <= column && clazz.columnEnd - 1 >= column)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Element root = textArea.getDocument().getDefaultRootElement();
|
||||||
|
for (ClassReferenceLocation location : classFileContainer.getClassReferenceLocationsFor(finalToken.getLexeme()))
|
||||||
|
{
|
||||||
|
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 (Exception ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MarkerCaretListener implements CaretListener
|
||||||
|
{
|
||||||
|
private final String classContainerName;
|
||||||
|
|
||||||
|
public MarkerCaretListener(String classContainerName)
|
||||||
|
{
|
||||||
|
this.classContainerName = classContainerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void caretUpdate(CaretEvent e)
|
||||||
|
{
|
||||||
|
SearchableRSyntaxTextArea textArea = (SearchableRSyntaxTextArea) e.getSource();
|
||||||
|
RSyntaxTextAreaHighlighterEx highlighterEx = (RSyntaxTextAreaHighlighterEx) bytecodeViewPanel.textArea.getHighlighter();
|
||||||
|
highlighterEx.clearMarkOccurrencesHighlights();
|
||||||
|
markOccurrences(textArea, BytecodeViewer.viewer.workPane.classFiles.get(classContainerName), errorStripe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
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.resolution.TypeSolver;
|
||||||
|
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
|
||||||
|
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
|
||||||
|
import com.github.javaparser.symbolsolver.resolution.typesolvers.JarTypeSolver;
|
||||||
|
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.ResourceContainer;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.*;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.classcontainer.parser.MyVoidVisitor;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NavigableMap;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a container for a specific class. The container name is based on the actual class name and the decompiler used.
|
||||||
|
* <p>
|
||||||
|
* 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 transient NavigableMap<String, ArrayList<ClassReferenceLocation>> classReferences = new TreeMap<>();
|
||||||
|
|
||||||
|
public boolean hasBeenParsed = false;
|
||||||
|
public final String className;
|
||||||
|
private final String content;
|
||||||
|
private final String parentContainer;
|
||||||
|
private final String path;
|
||||||
|
|
||||||
|
public ClassFileContainer(String className, String content, ResourceContainer resourceContainer)
|
||||||
|
{
|
||||||
|
this.className = className;
|
||||||
|
this.content = content;
|
||||||
|
this.parentContainer = resourceContainer.name;
|
||||||
|
this.path = resourceContainer.file.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the class content with JavaParser.
|
||||||
|
*/
|
||||||
|
public void parse()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TypeSolver typeSolver = new CombinedTypeSolver(new ReflectionTypeSolver(false), new JarTypeSolver(path));
|
||||||
|
StaticJavaParser.getParserConfiguration().setSymbolResolver(new JavaSymbolSolver(typeSolver));
|
||||||
|
CompilationUnit compilationUnit = StaticJavaParser.parse(this.content);
|
||||||
|
compilationUnit.accept(new MyVoidVisitor(this, compilationUnit), null);
|
||||||
|
} catch (ParseProblemException e)
|
||||||
|
{
|
||||||
|
System.err.println("Parsing error!");
|
||||||
|
} catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return this.className.substring(this.className.lastIndexOf('/') + 1, this.className.lastIndexOf('.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDecompiler()
|
||||||
|
{
|
||||||
|
return this.className.substring(this.className.lastIndexOf('-') + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParentContainer()
|
||||||
|
{
|
||||||
|
return this.parentContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putClassReference(String key, ClassReferenceLocation value)
|
||||||
|
{
|
||||||
|
this.classReferences.computeIfAbsent(key, v -> new ArrayList<>()).add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ClassReferenceLocation> getClassReferenceLocationsFor(String key)
|
||||||
|
{
|
||||||
|
return classReferences.getOrDefault(key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClassForField(String fieldName)
|
||||||
|
{
|
||||||
|
AtomicReference<String> className = new AtomicReference<>("");
|
||||||
|
this.classReferences.forEach((s, v) -> {
|
||||||
|
v.forEach(classReferenceLocation -> {
|
||||||
|
if (classReferenceLocation.fieldName.equals(fieldName))
|
||||||
|
{
|
||||||
|
className.set(classReferenceLocation.packagePath + "/" + s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return className.get();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 + '}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
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 signature;
|
||||||
|
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 signature, String methodParameterTypes, String decRef, int line, int columnStart, int columnEnd)
|
||||||
|
{
|
||||||
|
this.owner = owner;
|
||||||
|
this.signature = signature;
|
||||||
|
this.methodParameterTypes = methodParameterTypes;
|
||||||
|
this.decRef = decRef;
|
||||||
|
this.line = line;
|
||||||
|
this.columnStart = columnStart;
|
||||||
|
this.columnEnd = columnEnd;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package the.bytecode.club.bytecodeviewer.resources.classcontainer.locations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Bl3nd.
|
||||||
|
* Date: 9/20/2024
|
||||||
|
*/
|
||||||
|
public class ClassReferenceLocation
|
||||||
|
{
|
||||||
|
public final String owner;
|
||||||
|
public final String packagePath;
|
||||||
|
public final String fieldName;
|
||||||
|
public final String type;
|
||||||
|
public final int line;
|
||||||
|
public final int columnStart;
|
||||||
|
public final int columnEnd;
|
||||||
|
|
||||||
|
public ClassReferenceLocation(String owner, String packagePath, String fieldName, String type, int line, int columnStart, int columnEnd)
|
||||||
|
{
|
||||||
|
this.owner = owner;
|
||||||
|
this.packagePath = packagePath;
|
||||||
|
this.fieldName = fieldName;
|
||||||
|
this.type = type;
|
||||||
|
this.line = line;
|
||||||
|
this.columnStart = columnStart;
|
||||||
|
this.columnEnd = columnEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "ClassClassLocation{" + "owner='" + owner + '\'' + ", fieldName='" + fieldName + '\'' + ", type='" + type + '\'' + ", line=" + line + ", columnStart=" + columnStart + ", columnEnd=" + columnEnd + '}';
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,31 @@
|
||||||
|
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(" ")
|
||||||
|
|| lexeme.equals(";")
|
||||||
|
|| lexeme.equals(",")
|
||||||
|
|| lexeme.equals(">")
|
||||||
|
? textArea.modelToToken(textArea.getCaretPosition() - 1)
|
||||||
|
: token;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user