未验证 提交 aa8a7c03 编写于 作者: S skylot 提交者: GitHub

style: enforce strict style rules with editorconfig (PR #510)

上级 36ee994e
# EditorConfig is awesome: https://EditorConfig.org
root = true
[*]
end_of_line = lf
insert_final_newline = true
indent_style = tab
tab_width = 4
charset = utf-8
trim_trailing_whitespace = true
[*.xml]
indent_size = 1
[*.yml]
indent_style = space
indent_size = 2
[*.bat]
end_of_line = crlf
plugins {
id 'org.sonarqube' version '2.7'
id 'com.github.ben-manes.versions' version '0.21.0'
id 'org.sonarqube' version '2.7'
id 'com.github.ben-manes.versions' version '0.21.0'
id 'org.ec4j.editorconfig' version '0.0.3'
}
ext.jadxVersion = System.getenv('JADX_VERSION') ?: "dev"
......@@ -8,125 +9,137 @@ version = jadxVersion
println("jadx version: ${jadxVersion}")
allprojects {
apply plugin: 'java'
apply plugin: 'groovy'
apply plugin: 'jacoco'
version = jadxVersion
tasks.withType(JavaCompile) {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
if (!"$it".contains(':jadx-samples:')) {
options.compilerArgs << '-Xlint' << '-Xlint:unchecked' << '-Xlint:deprecation'
}
}
compileJava {
options.encoding = "UTF-8"
}
jar {
version = jadxVersion
manifest {
mainAttributes('jadx-version': jadxVersion)
}
}
dependencies {
compile 'org.slf4j:slf4j-api:1.7.26'
testCompile 'ch.qos.logback:logback-classic:1.2.3'
testCompile 'org.hamcrest:hamcrest-library:2.1'
testCompile 'org.mockito:mockito-core:2.25.1'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.4.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.4.1'
}
test {
useJUnitPlatform()
}
repositories {
mavenLocal()
mavenCentral()
jcenter()
google()
}
jacoco {
toolVersion = "0.8.2"
}
jacocoTestReport {
reports {
xml.enabled = true
html.enabled = true
}
}
apply plugin: 'java'
apply plugin: 'groovy'
apply plugin: 'jacoco'
version = jadxVersion
tasks.withType(JavaCompile) {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
if (!"$it".contains(':jadx-samples:')) {
options.compilerArgs << '-Xlint' << '-Xlint:unchecked' << '-Xlint:deprecation'
}
}
compileJava {
options.encoding = "UTF-8"
}
jar {
version = jadxVersion
manifest {
mainAttributes('jadx-version': jadxVersion)
}
}
dependencies {
compile 'org.slf4j:slf4j-api:1.7.26'
testCompile 'ch.qos.logback:logback-classic:1.2.3'
testCompile 'org.hamcrest:hamcrest-library:2.1'
testCompile 'org.mockito:mockito-core:2.25.1'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.4.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.4.1'
}
test {
useJUnitPlatform()
}
repositories {
mavenLocal()
mavenCentral()
jcenter()
google()
}
jacoco {
toolVersion = "0.8.2"
}
jacocoTestReport {
reports {
xml.enabled = true
html.enabled = true
}
}
}
sonarqube {
properties {
property 'sonar.exclusions', '**/jadx/samples/**/*,**/test-app/**/*'
property 'sonar.coverage.exclusions', '**/jadx/gui/**/*'
}
properties {
property 'sonar.exclusions', '**/jadx/samples/**/*,**/test-app/**/*'
property 'sonar.coverage.exclusions', '**/jadx/gui/**/*'
}
}
editorconfig {
excludes = ['gradle/'
, 'jadx-test-app/test-app' // ignore issues in submodule
, '**/out/' // IntelliJ Idea build dirs
, '**/certificate-test/' // binary test files (.RSA)
, '**/*.svg'
, '**/*.arsc'
]
}
dependencyUpdates.resolutionStrategy = {
componentSelection { rules ->
rules.all { ComponentSelection selection ->
boolean rejected = ['alpha', 'beta', 'rc', 'cr', 'm', 'atlassian'].any { qualifier ->
selection.candidate.version ==~ /(?i).*[.-]${qualifier}[.\d-]*/
}
if (rejected) {
selection.reject('Release candidate')
}
}
}
componentSelection { rules ->
rules.all { ComponentSelection selection ->
boolean rejected = ['alpha', 'beta', 'rc', 'cr', 'm', 'atlassian'].any { qualifier ->
selection.candidate.version ==~ /(?i).*[.-]${qualifier}[.\d-]*/
}
if (rejected) {
selection.reject('Release candidate')
}
}
}
}
task copyArtifacts(type: Sync, dependsOn: ['jadx-cli:installDist', 'jadx-gui:installDist']) {
destinationDir file("$buildDir/jadx")
['jadx-cli', 'jadx-gui'].each {
from tasks.getByPath(":${it}:installDist").destinationDir
}
destinationDir file("$buildDir/jadx")
['jadx-cli', 'jadx-gui'].each {
from tasks.getByPath(":${it}:installDist").destinationDir
}
}
task pack(type: Zip, dependsOn: copyArtifacts) {
destinationDir buildDir
archiveName "jadx-${jadxVersion}.zip"
from copyArtifacts.destinationDir
destinationDir buildDir
archiveName "jadx-${jadxVersion}.zip"
from copyArtifacts.destinationDir
}
task copyExe(type: Copy, dependsOn: 'jadx-gui:createExe') {
group 'jadx'
description = 'Copy exe to build dir'
destinationDir buildDir
from tasks.getByPath('jadx-gui:createExe').outputs
include '*.exe'
group 'jadx'
description = 'Copy exe to build dir'
destinationDir buildDir
from tasks.getByPath('jadx-gui:createExe').outputs
include '*.exe'
}
task dist(dependsOn: [pack, copyExe]) {
group 'jadx'
description = 'Build jadx distribution zip'
group 'jadx'
description = 'Build jadx distribution zip'
}
task samples(dependsOn: 'jadx-samples:samples') {
group 'jadx'
group 'jadx'
}
task testAppCheck(dependsOn: 'jadx-test-app:testAppCheck') {
group 'jadx'
group 'jadx'
}
task cleanBuildDir(type: Delete) {
group 'jadx'
delete buildDir
group 'jadx'
delete buildDir
}
build.dependsOn(dist, samples)
check.dependsOn editorconfigCheck
test.dependsOn(samples)
clean.dependsOn(cleanBuildDir)
......@@ -125,7 +125,7 @@ public class JadxCLIArgs {
}
if (verbose) {
ch.qos.logback.classic.Logger rootLogger =
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
// remove INFO ThresholdFilter
Appender<ILoggingEvent> appender = rootLogger.getAppender("STDOUT");
if (appender != null) {
......
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>%-5level - %msg%n</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>%-5level - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
</root>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
......@@ -137,7 +137,7 @@ public class JadxArgs {
public boolean isDebugInfo() {
return debugInfo;
}
public void setDebugInfo(boolean debugInfo) {
this.debugInfo = debugInfo;
}
......@@ -257,5 +257,4 @@ public class JadxArgs {
sb.append('}');
return sb.toString();
}
}
......@@ -7,14 +7,14 @@ import java.lang.annotation.Target;
/**
* Indicates a test which is known to fail.
*
*
* <p>This would cause a failure to be considered as success and a success as failure,
* with the benefit of updating the related issue when it has been resolved even unintentionally.</p>
*
*
* <p>To have an effect, the test class must be annotated with:
*
*
* <code>
* &#064;ExtendWith(NotYetImplementedExtension.class)
* &#064;ExtendWith(NotYetImplementedExtension.class)
* </code>
* </p>
*/
......
package jadx.tests.integration.generics;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
public class TestImportGenericMap extends IntegrationTest {
@Test
......@@ -25,16 +25,15 @@ public class TestImportGenericMap extends IntegrationTest {
final class SuperClass<O extends SuperClass.ToImport> {
interface ToImport {
}
interface NotToImport {
}
interface ToImport {
}
static final class Class1<C extends NotToImport> {
}
interface NotToImport {
}
public <C extends NotToImport> SuperClass(Class1<C> zzf) {
}
static final class Class1<C extends NotToImport> {
}
}
\ No newline at end of file
public <C extends NotToImport> SuperClass(Class1<C> zzf) {
}
}
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss} %-5level - %msg%n</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss} %-5level - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
</root>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
package jadx.gui.treemodel;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import org.jetbrains.annotations.NotNull;
import jadx.api.JavaClass;
......@@ -47,7 +45,7 @@ public class JPackage extends JNode implements Comparable<JPackage> {
List<String> excludedPackages = wrapper.getExcludedPackages();
this.enabled = excludedPackages.isEmpty()
|| excludedPackages.stream().filter(p -> !p.isEmpty())
.noneMatch(p -> name.equals(p) || name.startsWith(p + '.'));
.noneMatch(p -> name.equals(p) || name.startsWith(p + '.'));
}
public final void update() {
......@@ -130,7 +128,7 @@ public class JPackage extends JNode implements Comparable<JPackage> {
public String makeLongString() {
return name;
}
public boolean isEnabled() {
return enabled;
}
......
package jadx.gui.ui;
import static javax.swing.KeyStroke.getKeyStroke;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.DisplayMode;
import java.awt.Font;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import javax.swing.*;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.event.ActionEvent;
......@@ -22,38 +27,6 @@ import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.ProgressMonitor;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.fife.ui.rsyntaxtextarea.Theme;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -83,6 +56,8 @@ import jadx.gui.utils.Link;
import jadx.gui.utils.NLS;
import jadx.gui.utils.Utils;
import static javax.swing.KeyStroke.getKeyStroke;
@SuppressWarnings("serial")
public class MainWindow extends JFrame {
private static final Logger LOG = LoggerFactory.getLogger(MainWindow.class);
......@@ -343,10 +318,10 @@ public class MainWindow extends JFrame {
Object obj = tree.getLastSelectedPathComponent();
if (obj instanceof JPackage) {
JPackagePopUp menu = new JPackagePopUp((JPackage) obj);
menu.show(e.getComponent(), e.getX(), e.getY());
menu.show(e.getComponent(), e.getX(), e.getY());
}
}
private void syncWithEditor() {
ContentPanel selectedContentPanel = tabbedPane.getSelectedCodePanel();
if (selectedContentPanel == null) {
......@@ -608,8 +583,7 @@ public class MainWindow extends JFrame {
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
treeRightClickAction(e);
}
else {
} else {
treeClickAction();
}
}
......@@ -780,7 +754,7 @@ public class MainWindow extends JFrame {
public void menuCanceled(MenuEvent e) {
}
}
private class JPackagePopUp extends JPopupMenu {
JMenuItem excludeItem = new JCheckBoxMenuItem("Exclude");
......@@ -791,8 +765,7 @@ public class MainWindow extends JFrame {
String fullName = pkg.getFullName();
if (excludeItem.isSelected()) {
wrapper.addExcludedPackage(fullName);
}
else {
} else {
wrapper.removeExcludedPackage(fullName);
}
reOpenFile();
......
package jadx.gui.ui.codearea;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.StyleConstants;
import javax.swing.text.Utilities;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
import org.fife.ui.rsyntaxtextarea.SyntaxScheme;
import org.fife.ui.rsyntaxtextarea.Token;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LineNumbers extends JPanel implements CaretListener {
private static final Logger LOG = LoggerFactory.getLogger(LineNumbers.class);
private static final long serialVersionUID = -4978268673635308190L;
private static final int NUM_HEIGHT = Integer.MAX_VALUE - 1000000;
private static final Map<?, ?> DESKTOP_HINTS = (Map<?, ?>) Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints");
private final CodeArea codeArea;
private boolean useSourceLines = true;
private int lastDigits;
private int lastLine;
private Map<String, FontMetrics> fonts;
private final transient Color numberColor;
private final transient Color currentColor;
private final transient Border border;
public LineNumbers(CodeArea component) {
this.codeArea = component;
setFont(component.getFont());
SyntaxScheme syntaxScheme = codeArea.getSyntaxScheme();
numberColor = syntaxScheme.getStyle(Token.LITERAL_NUMBER_DECIMAL_INT).foreground;
currentColor = syntaxScheme.getStyle(Token.LITERAL_STRING_DOUBLE_QUOTE).foreground;
border = new MatteBorder(0, 0, 0, 1, syntaxScheme.getStyle(Token.COMMENT_MULTILINE).foreground);
setBackground(codeArea.getBackground());
setForeground(numberColor);
setBorderGap(5);
setPreferredWidth();
component.addCaretListener(this);
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
useSourceLines = !useSourceLines;
repaint();
}
}
});
}
public void setBorderGap(int borderGap) {
Border inner = new EmptyBorder(0, borderGap, 0, borderGap);
setBorder(new CompoundBorder(border, inner));
lastDigits = 0;
}
private void setPreferredWidth() {
Element root = codeArea.getDocument().getDefaultRootElement();
int lines = root.getElementCount();
int digits = Math.max(String.valueOf(lines).length(), 3);
if (lastDigits != digits) {
lastDigits = digits;
FontMetrics fontMetrics = getFontMetrics(getFont());
int width = fontMetrics.charWidth('0') * digits;
Insets insets = getInsets();
int preferredWidth = insets.left + insets.right + width;
Dimension d = getPreferredSize();
if (d != null) {
d.setSize(preferredWidth, NUM_HEIGHT);
setPreferredSize(d);
setSize(d);
}
}
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
applyRenderHints(g);
Font font = codeArea.getFont();
font = font.deriveFont(font.getSize2D() - 1.0f);
g.setFont(font);
Dimension size = getSize();
g.setColor(codeArea.getBackground());
g.fillRect(0, 0, size.width, size.height);
FontMetrics fontMetrics = codeArea.getFontMetrics(font);
Insets insets = getInsets();
int availableWidth = size.width - insets.left - insets.right;
Rectangle clip = g.getClipBounds();
int rowStartOffset = codeArea.viewToModel(new Point(0, clip.y));
int endOffset = codeArea.viewToModel(new Point(0, clip.y + clip.height));
while (rowStartOffset <= endOffset) {
try {
String lineNumber = getTextLineNumber(rowStartOffset);
if (lineNumber != null) {
if (isCurrentLine(rowStartOffset)) {
g.setColor(currentColor);
} else {
g.setColor(numberColor);
}
int stringWidth = fontMetrics.stringWidth(lineNumber);
int x = availableWidth - stringWidth + insets.left;
int y = getOffsetY(rowStartOffset, fontMetrics);
g.drawString(lineNumber, x, y);
}
rowStartOffset = Utilities.getRowEnd(codeArea, rowStartOffset) + 1;
} catch (Exception e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Line numbers draw error", e);
}
break;
}
}
}
private void applyRenderHints(Graphics g) {
if (g instanceof Graphics2D) {
Graphics2D g2d = (Graphics2D) g;
if (DESKTOP_HINTS != null) {
g2d.setRenderingHints(DESKTOP_HINTS);
} else {
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
}
}
}
private boolean isCurrentLine(int rowStartOffset) {
int caretPosition = codeArea.getCaretPosition();
Element root = codeArea.getDocument().getDefaultRootElement();
return root.getElementIndex(rowStartOffset) == root.getElementIndex(caretPosition);
}
@Nullable
protected String getTextLineNumber(int rowStartOffset) {
Element root = codeArea.getDocument().getDefaultRootElement();
int index = root.getElementIndex(rowStartOffset);
Element line = root.getElement(index);
if (line.getStartOffset() != rowStartOffset) {
return null;
}
int lineNumber = index + 1;
if (useSourceLines) {
Integer sourceLine = codeArea.getSourceLine(lineNumber);
if (sourceLine == null) {
return null;
}
return String.valueOf(sourceLine);
}
return String.valueOf(lineNumber);
}
private int getOffsetY(int rowStartOffset, FontMetrics fontMetrics) throws BadLocationException {
Rectangle r = codeArea.modelToView(rowStartOffset);
if (r == null) {
throw new BadLocationException("Can't get Y offset", rowStartOffset);
}
int lineHeight = fontMetrics.getHeight();
int y = r.y + r.height;
int descent = 0;
if (r.height == lineHeight) {
descent = fontMetrics.getDescent();
} else {
if (fonts == null) {
fonts = new HashMap<>();
}
Element root = codeArea.getDocument().getDefaultRootElement();
int index = root.getElementIndex(rowStartOffset);
Element line = root.getElement(index);
for (int i = 0; i < line.getElementCount(); i++) {
Element child = line.getElement(i);
AttributeSet as = child.getAttributes();
String fontFamily = (String) as.getAttribute(StyleConstants.FontFamily);
Integer fontSize = (Integer) as.getAttribute(StyleConstants.FontSize);
String key = fontFamily + fontSize;
FontMetrics fm = fonts.computeIfAbsent(key, k -> {
Font font = new Font(fontFamily, Font.PLAIN, fontSize);
return codeArea.getFontMetrics(font);
});
descent = Math.max(descent, fm.getDescent());
}
}
return y - descent;
}
@Override
public void caretUpdate(CaretEvent e) {
int caretPosition = codeArea.getCaretPosition();
Element root = codeArea.getDocument().getDefaultRootElement();
int currentLine = root.getElementIndex(caretPosition);
if (lastLine != currentLine) {
repaint();
lastLine = currentLine;
}
}
public void setUseSourceLines(boolean useSourceLines) {
this.useSourceLines = useSourceLines;
}
}
package jadx.gui.ui.codearea;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.StyleConstants;
import javax.swing.text.Utilities;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
import org.fife.ui.rsyntaxtextarea.SyntaxScheme;
import org.fife.ui.rsyntaxtextarea.Token;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LineNumbers extends JPanel implements CaretListener {
private static final Logger LOG = LoggerFactory.getLogger(LineNumbers.class);
private static final long serialVersionUID = -4978268673635308190L;
private static final int NUM_HEIGHT = Integer.MAX_VALUE - 1000000;
private static final Map<?, ?> DESKTOP_HINTS = (Map<?, ?>) Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints");
private final CodeArea codeArea;
private boolean useSourceLines = true;
private int lastDigits;
private int lastLine;
private Map<String, FontMetrics> fonts;
private final transient Color numberColor;
private final transient Color currentColor;
private final transient Border border;
public LineNumbers(CodeArea component) {
this.codeArea = component;
setFont(component.getFont());
SyntaxScheme syntaxScheme = codeArea.getSyntaxScheme();
numberColor = syntaxScheme.getStyle(Token.LITERAL_NUMBER_DECIMAL_INT).foreground;
currentColor = syntaxScheme.getStyle(Token.LITERAL_STRING_DOUBLE_QUOTE).foreground;
border = new MatteBorder(0, 0, 0, 1, syntaxScheme.getStyle(Token.COMMENT_MULTILINE).foreground);
setBackground(codeArea.getBackground());
setForeground(numberColor);
setBorderGap(5);
setPreferredWidth();
component.addCaretListener(this);
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
useSourceLines = !useSourceLines;
repaint();
}
}
});
}
public void setBorderGap(int borderGap) {
Border inner = new EmptyBorder(0, borderGap, 0, borderGap);
setBorder(new CompoundBorder(border, inner));
lastDigits = 0;
}
private void setPreferredWidth() {
Element root = codeArea.getDocument().getDefaultRootElement();
int lines = root.getElementCount();
int digits = Math.max(String.valueOf(lines).length(), 3);
if (lastDigits != digits) {
lastDigits = digits;
FontMetrics fontMetrics = getFontMetrics(getFont());
int width = fontMetrics.charWidth('0') * digits;
Insets insets = getInsets();
int preferredWidth = insets.left + insets.right + width;
Dimension d = getPreferredSize();
if (d != null) {
d.setSize(preferredWidth, NUM_HEIGHT);
setPreferredSize(d);
setSize(d);
}
}
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
applyRenderHints(g);
Font font = codeArea.getFont();
font = font.deriveFont(font.getSize2D() - 1.0f);
g.setFont(font);
Dimension size = getSize();
g.setColor(codeArea.getBackground());
g.fillRect(0, 0, size.width, size.height);
FontMetrics fontMetrics = codeArea.getFontMetrics(font);
Insets insets = getInsets();
int availableWidth = size.width - insets.left - insets.right;
Rectangle clip = g.getClipBounds();
int rowStartOffset = codeArea.viewToModel(new Point(0, clip.y));
int endOffset = codeArea.viewToModel(new Point(0, clip.y + clip.height));
while (rowStartOffset <= endOffset) {
try {
String lineNumber = getTextLineNumber(rowStartOffset);
if (lineNumber != null) {
if (isCurrentLine(rowStartOffset)) {
g.setColor(currentColor);
} else {
g.setColor(numberColor);
}
int stringWidth = fontMetrics.stringWidth(lineNumber);
int x = availableWidth - stringWidth + insets.left;
int y = getOffsetY(rowStartOffset, fontMetrics);
g.drawString(lineNumber, x, y);
}
rowStartOffset = Utilities.getRowEnd(codeArea, rowStartOffset) + 1;
} catch (Exception e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Line numbers draw error", e);
}
break;
}
}
}
private void applyRenderHints(Graphics g) {
if (g instanceof Graphics2D) {
Graphics2D g2d = (Graphics2D) g;
if (DESKTOP_HINTS != null) {
g2d.setRenderingHints(DESKTOP_HINTS);
} else {
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
}
}
}
private boolean isCurrentLine(int rowStartOffset) {
int caretPosition = codeArea.getCaretPosition();
Element root = codeArea.getDocument().getDefaultRootElement();
return root.getElementIndex(rowStartOffset) == root.getElementIndex(caretPosition);
}
@Nullable
protected String getTextLineNumber(int rowStartOffset) {
Element root = codeArea.getDocument().getDefaultRootElement();
int index = root.getElementIndex(rowStartOffset);
Element line = root.getElement(index);
if (line.getStartOffset() != rowStartOffset) {
return null;
}
int lineNumber = index + 1;
if (useSourceLines) {
Integer sourceLine = codeArea.getSourceLine(lineNumber);
if (sourceLine == null) {
return null;
}
return String.valueOf(sourceLine);
}
return String.valueOf(lineNumber);
}
private int getOffsetY(int rowStartOffset, FontMetrics fontMetrics) throws BadLocationException {
Rectangle r = codeArea.modelToView(rowStartOffset);
if (r == null) {
throw new BadLocationException("Can't get Y offset", rowStartOffset);
}
int lineHeight = fontMetrics.getHeight();
int y = r.y + r.height;
int descent = 0;
if (r.height == lineHeight) {
descent = fontMetrics.getDescent();
} else {
if (fonts == null) {
fonts = new HashMap<>();
}
Element root = codeArea.getDocument().getDefaultRootElement();
int index = root.getElementIndex(rowStartOffset);
Element line = root.getElement(index);
for (int i = 0; i < line.getElementCount(); i++) {
Element child = line.getElement(i);
AttributeSet as = child.getAttributes();
String fontFamily = (String) as.getAttribute(StyleConstants.FontFamily);
Integer fontSize = (Integer) as.getAttribute(StyleConstants.FontSize);
String key = fontFamily + fontSize;
FontMetrics fm = fonts.computeIfAbsent(key, k -> {
Font font = new Font(fontFamily, Font.PLAIN, fontSize);
return codeArea.getFontMetrics(font);
});
descent = Math.max(descent, fm.getDescent());
}
}
return y - descent;
}
@Override
public void caretUpdate(CaretEvent e) {
int caretPosition = codeArea.getCaretPosition();
Element root = codeArea.getDocument().getDefaultRootElement();
int currentLine = root.getElementIndex(caretPosition);
if (lastLine != currentLine) {
repaint();
lastLine = currentLine;
}
}
public void setUseSourceLines(boolean useSourceLines) {
this.useSourceLines = useSourceLines;
}
}
package jadx.gui.utils;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.Vector;
import jadx.core.utils.exceptions.JadxRuntimeException;
public class NLS {
private static final Vector<LangLocale> i18nLocales = new Vector<>();
private static final Map<LangLocale, ResourceBundle> i18nMessagesMap = new HashMap<>();
private static final ResourceBundle fallbackMessagesMap;
private static final LangLocale localLocale;
// Use these two fields to avoid invoking Map.get() method twice.
private static ResourceBundle localizedMessagesMap;
private static LangLocale currentLocale;
static {
localLocale = new LangLocale(Locale.getDefault());
i18nLocales.add(new LangLocale("en", "US")); // As default language
i18nLocales.add(new LangLocale("zh", "CN"));
i18nLocales.add(new LangLocale("es", "ES"));
i18nLocales.forEach(NLS::load);
LangLocale defLang = i18nLocales.get(0);
fallbackMessagesMap = i18nMessagesMap.get(defLang);
localizedMessagesMap = i18nMessagesMap.get(defLang);
}
private NLS() {
}
private static void load(LangLocale locale) {
ResourceBundle bundle;
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
String resName = String.format("i18n/Messages_%s.properties", locale.get());
URL bundleUrl = classLoader.getResource(resName);
if (bundleUrl == null) {
throw new JadxRuntimeException("Locale resource not found: " + resName);
}
try (Reader reader = new InputStreamReader(bundleUrl.openStream(), StandardCharsets.UTF_8)) {
bundle = new PropertyResourceBundle(reader);
} catch (IOException e) {
throw new JadxRuntimeException("Failed to load " + resName, e);
}
i18nMessagesMap.put(locale, bundle);
}
public static String str(String key) {
try {
return localizedMessagesMap.getString(key);
} catch (MissingResourceException e) {
return fallbackMessagesMap.getString(key); // definitely exists
}
}
public static String str(String key, LangLocale locale) {
ResourceBundle bundle = i18nMessagesMap.get(locale);
if (bundle != null) {
try {
return bundle.getString(key);
} catch (MissingResourceException ignored) {
// use fallback string
}
}
return fallbackMessagesMap.getString(key); // definitely exists
}
public static void setLocale(LangLocale locale) {
if (i18nMessagesMap.containsKey(locale)) {
currentLocale = locale;
} else {
currentLocale = i18nLocales.get(0);
}
localizedMessagesMap = i18nMessagesMap.get(currentLocale);
}
public static Vector<LangLocale> getI18nLocales() {
return i18nLocales;
}
public static LangLocale currentLocale() {
return currentLocale;
}
public static LangLocale defaultLocale() {
if (i18nMessagesMap.containsKey(localLocale)) {
return localLocale;
}
// fallback to english if unsupported
return i18nLocales.get(0);
}
}
package jadx.gui.utils;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.Vector;
import jadx.core.utils.exceptions.JadxRuntimeException;
public class NLS {
private static final Vector<LangLocale> i18nLocales = new Vector<>();
private static final Map<LangLocale, ResourceBundle> i18nMessagesMap = new HashMap<>();
private static final ResourceBundle fallbackMessagesMap;
private static final LangLocale localLocale;
// Use these two fields to avoid invoking Map.get() method twice.
private static ResourceBundle localizedMessagesMap;
private static LangLocale currentLocale;
static {
localLocale = new LangLocale(Locale.getDefault());
i18nLocales.add(new LangLocale("en", "US")); // As default language
i18nLocales.add(new LangLocale("zh", "CN"));
i18nLocales.add(new LangLocale("es", "ES"));
i18nLocales.forEach(NLS::load);
LangLocale defLang = i18nLocales.get(0);
fallbackMessagesMap = i18nMessagesMap.get(defLang);
localizedMessagesMap = i18nMessagesMap.get(defLang);
}
private NLS() {
}
private static void load(LangLocale locale) {
ResourceBundle bundle;
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
String resName = String.format("i18n/Messages_%s.properties", locale.get());
URL bundleUrl = classLoader.getResource(resName);
if (bundleUrl == null) {
throw new JadxRuntimeException("Locale resource not found: " + resName);
}
try (Reader reader = new InputStreamReader(bundleUrl.openStream(), StandardCharsets.UTF_8)) {
bundle = new PropertyResourceBundle(reader);
} catch (IOException e) {
throw new JadxRuntimeException("Failed to load " + resName, e);
}
i18nMessagesMap.put(locale, bundle);
}
public static String str(String key) {
try {
return localizedMessagesMap.getString(key);
} catch (MissingResourceException e) {
return fallbackMessagesMap.getString(key); // definitely exists
}
}
public static String str(String key, LangLocale locale) {
ResourceBundle bundle = i18nMessagesMap.get(locale);
if (bundle != null) {
try {
return bundle.getString(key);
} catch (MissingResourceException ignored) {
// use fallback string
}
}
return fallbackMessagesMap.getString(key); // definitely exists
}
public static void setLocale(LangLocale locale) {
if (i18nMessagesMap.containsKey(locale)) {
currentLocale = locale;
} else {
currentLocale = i18nLocales.get(0);
}
localizedMessagesMap = i18nMessagesMap.get(currentLocale);
}
public static Vector<LangLocale> getI18nLocales() {
return i18nLocales;
}
public static LangLocale currentLocale() {
return currentLocale;
}
public static LangLocale defaultLocale() {
if (i18nMessagesMap.containsKey(localLocale)) {
return localLocale;
}
// fallback to english if unsupported
return i18nLocales.get(0);
}
}
project.ext {
mainSamplesClass = "jadx.samples.RunTests"
samplesJadxSrcDir = "${buildDir}/samples-jadx/src"
samplesJadxOutDir = "${buildDir}/samples-jadx/output"
mainSamplesClass = "jadx.samples.RunTests"
samplesJadxSrcDir = "${buildDir}/samples-jadx/src"
samplesJadxOutDir = "${buildDir}/samples-jadx/output"
}
dependencies {
compile(project(":jadx-core"))
compile(project(":jadx-cli"))
compile(project(":jadx-core"))
compile(project(":jadx-cli"))
}
compileJava {
options.compilerArgs << '-g:none'
options.compilerArgs << '-g:none'
}
task samplesRun(type: JavaExec, dependsOn: compileJava) {
classpath = sourceSets.main.output
main = mainSamplesClass
task samplesRun(type: JavaExec, dependsOn: compileJava) {
classpath = sourceSets.main.output
main = mainSamplesClass
}
task samplesJar(type: Jar, dependsOn: samplesRun) {
baseName = 'samples'
from sourceSets.main.output
baseName = 'samples'
from sourceSets.main.output
}
task samplesJadxCreate(type: JavaExec, dependsOn: samplesJar) {
classpath = sourceSets.main.output + configurations.compile
main = project(":jadx-cli").mainClassName
args = ['-d', samplesJadxSrcDir, samplesJar.archivePath]
task samplesJadxCreate(type: JavaExec, dependsOn: samplesJar) {
classpath = sourceSets.main.output + configurations.compile
main = project(":jadx-cli").mainClassName
args = ['-d', samplesJadxSrcDir, samplesJar.archivePath]
}
task samplesJadxCompile(type: JavaCompile, dependsOn: samplesJadxCreate) {
classpath = configurations.compile
destinationDir = file samplesJadxOutDir
source = samplesJadxSrcDir
options.encoding = "UTF-8"
task samplesJadxCompile(type: JavaCompile, dependsOn: samplesJadxCreate) {
classpath = configurations.compile
destinationDir = file samplesJadxOutDir
source = samplesJadxSrcDir
options.encoding = "UTF-8"
}
task samplesJadxRun(type: JavaExec, dependsOn: samplesJadxCompile) {
classpath = files samplesJadxOutDir
main = mainSamplesClass
task samplesJadxRun(type: JavaExec, dependsOn: samplesJadxCompile) {
classpath = files samplesJadxOutDir
main = mainSamplesClass
}
task samples(dependsOn: samplesJadxRun) {
}
task cleanGeneratedFiles(type: Delete) {
delete samplesJadxSrcDir
delete samplesJadxOutDir
delete samplesJadxSrcDir
delete samplesJadxOutDir
}
clean.dependsOn cleanGeneratedFiles
......@@ -5,14 +5,14 @@ public class TestInitializers extends AbstractTest {
private static String a;
private static int counter;
private A c_a;
public static class A {
public static String a;
static {
a = "a1";
}
public boolean z() {
return true;
}
......@@ -29,11 +29,11 @@ public class TestInitializers extends AbstractTest {
b = 1;
}
}
public B(int _b) {
b = _b;
}
public void setB(int _b) {
b = _b;
}
......@@ -41,11 +41,11 @@ public class TestInitializers extends AbstractTest {
public int getB() {
return b;
}
public int getBBB() {
return bbb;
}
{
bbb = 123;
}
......@@ -55,7 +55,7 @@ public class TestInitializers extends AbstractTest {
a = "a0";
counter = 0;
}
{
c_a = new A();
}
......@@ -90,15 +90,14 @@ public class TestInitializers extends AbstractTest {
assertTrue((new B()).getB() == -1);
assertTrue(counter == 1);
B b3 = new B(3);
assertTrue((b3.getB() == 3) && (b3.getBBB() == 123));
return true;
}
public static void main(String[] args) throws Exception {
new TestInitializers().testRun();
}
}
......@@ -5,11 +5,11 @@ public class TestInner3 extends AbstractTest {
private String i0;
public class A {
protected String a;
public A() {
a="";
a = "";
}
public String a() {
......@@ -39,7 +39,7 @@ public class TestInner3 extends AbstractTest {
public String i() {
String result = TestInner3.this.i0 + I0.this.i0 + I0.this.i1 + i0 + i1 + i2;
A a = new A() {
public String a() {
......@@ -82,5 +82,4 @@ public class TestInner3 extends AbstractTest {
public static void main(String[] args) throws Exception {
new TestInner2().testRun();
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册