com.yworks.yguard.LogParserView Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of yguard Show documentation
Show all versions of yguard Show documentation
The open-source Java obfuscation tool working with Ant and Gradle by yWorks - the diagramming experts
package com.yworks.yguard;
import com.yworks.yguard.YGuardLogParser.ClassStruct;
import com.yworks.yguard.YGuardLogParser.Mapped;
import com.yworks.yguard.YGuardLogParser.MethodStruct;
import com.yworks.yguard.YGuardLogParser.PackageStruct;
import com.yworks.yguard.obf.Version;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Enumeration;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.filechooser.FileFilter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
/**
* Displays a browsable yGuard mapping file and provides controls for
* de-obfuscating obfuscated stacktraces.
* @author Thomas Behr
*/
class LogParserView {
LogParserView() {
}
void show( final File initialPath ) {
final JTree tree = new JTree(new DefaultTreeModel(new DefaultMutableTreeNode()));
tree.setCellRenderer(new TreeCellRenderer() {
DefaultTreeCellRenderer dtcr = new DefaultTreeCellRenderer();
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
JComponent c = (JComponent) dtcr.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
DefaultMutableTreeNode dmtr = (DefaultMutableTreeNode) value;
if (dmtr.getUserObject() != null) {
dtcr.setIcon(((Mapped)dmtr.getUserObject()).getIcon());
}
return c;
}
});
tree.setRootVisible(false);
tree.setShowsRootHandles(true);
final JPanel textPanel = new JPanel(new BorderLayout());
final JTextArea textArea = new JTextArea();
textArea.setMinimumSize(new Dimension(600, 200));
final JScrollPane textScrollPane = new JScrollPane(textArea);
textScrollPane.getViewport().setPreferredSize(new Dimension(400, 200));
textPanel.add(textScrollPane, BorderLayout.CENTER);
final JButton button = new JButton("Deobfuscate!");
button.setMnemonic('D');
textPanel.add(button, BorderLayout.SOUTH);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
deobfuscate(getParser(tree), textArea);
}
});
final JPanel top = new JPanel(new BorderLayout());
top.add(new JScrollPane(tree), BorderLayout.CENTER);
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0,0));
buttonPanel.add(new JButton(new AbstractAction("Sort by Mapping") {
public void actionPerformed(ActionEvent e) {
final DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
sort(model, new MappedNameComparator());
}
}));
buttonPanel.add(new JButton(new AbstractAction("Sort by Names") {
public void actionPerformed(ActionEvent e) {
final DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
sort(model, new NameComparator());
}
}));
top.add(buttonPanel, BorderLayout.NORTH);
final JFrame frame = new JFrame(newTitle(initialPath.getAbsolutePath()));
final JMenu recent = new JMenu("Open Recent");
final JFileChooser jfc = new JFileChooser();
jfc.addChoosableFileFilter(new FileFilterImpl(".gz", "Compressed XML (*.gz)"));
jfc.addChoosableFileFilter(new FileFilterImpl(".xml", "XML (*.xml)"));
jfc.setAcceptAllFileFilterUsed(true);
jfc.setFileFilter(jfc.getAcceptAllFileFilter());
final File parent = initialPath.getParentFile();
if (parent != null) {
jfc.setCurrentDirectory(parent);
}
final UiContext ctx = new UiContext(frame, tree, textArea, recent, jfc);
try {
setParser(tree, newParser(initialPath));
addRecent(ctx, initialPath);
} catch (Exception ex) {
setParser(tree, new YGuardLogParser());
frame.setTitle("Element Mapping - yGuard " + Version.getVersion());
final String msg = toErrorMessage(initialPath, ex);
frame.addComponentListener(new ComponentAdapter() {
public void componentShown( final ComponentEvent e ) {
frame.removeComponentListener(this);
EventQueue.invokeLater(new Runnable() {
public void run() {
showErrorMessage(msg, tree);
}
});
}
});
}
final JMenu file = new JMenu("File");
file.add(new AbstractOpenAction(ctx, "Open") {
public void actionPerformed( final ActionEvent e ) {
final JFileChooser jfc = context.fileChooser;
if (jfc.showOpenDialog(top) == JFileChooser.APPROVE_OPTION) {
open(jfc.getSelectedFile());
}
}
@Override
void onOpened( final UiContext context, final File path ) {
LogParserView.addRecent(context, path);
super.onOpened(context, path);
}
});
file.add(recent);
file.addSeparator();
file.add(new AbstractAction("Quit") {
public void actionPerformed( final ActionEvent e ) {
System.exit(0);
}
});
final JMenu help = new JMenu("?");
help.add(new AbstractAction("About") {
public void actionPerformed( final ActionEvent e ) {
final JLabel jl = new JLabel("Element Mapping - yGuard " + Version.getVersion());
JOptionPane.showMessageDialog(top, jl, "About", JOptionPane.PLAIN_MESSAGE);
}
});
final JMenuBar jmb = new JMenuBar();
jmb.add(file);
jmb.add(help);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setJMenuBar(jmb);
frame.setContentPane(new JSplitPane(JSplitPane.VERTICAL_SPLIT, top, textPanel));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
static void addRecent( final UiContext context, final File path ) {
final JMenu menu = context.recentMenu;
final int n = menu.getItemCount();
if (n > 9) {
menu.remove(n - 1);
}
final RecentAction ra = new RecentAction(context, path);
final JMenuItem jc = menu.add(ra);
ra.setItem(jc);
if (n > 0) {
menu.remove(menu.getItemCount() - 1);
menu.add(jc, 0);
}
}
static void showErrorMessage( final String text, final JComponent parent ) {
final JTextArea jta = new JTextArea(text);
jta.setEditable(false);
final JScrollPane jsp = new JScrollPane(jta);
jsp.setPreferredSize(new Dimension(400, 600));
JOptionPane.showMessageDialog(parent, jsp, "Error", JOptionPane.ERROR_MESSAGE);
}
static String toErrorMessage( final File file, final Exception ex ) {
final StringWriter sw = new StringWriter();
sw.write("Could not read ");
sw.write(file.getAbsolutePath());
sw.write(":\n");
ex.printStackTrace(new PrintWriter(sw));
return sw.toString();
}
static YGuardLogParser getParser( final JTree tree ) {
return (YGuardLogParser) tree.getClientProperty("PARSER");
}
static void setParser( final JTree tree, final YGuardLogParser parser ) {
tree.setModel(parser.getTreeModel());
tree.putClientProperty("PARSER", parser);
}
static YGuardLogParser newParser( final File file ) throws Exception {
final YGuardLogParser parser = new YGuardLogParser();
parser.parse(file);
return parser;
}
static String newTitle( final String path ) {
final String t = path == null ? "" : path.trim();
if (t.length() > 31) {
return newTitleSuffix(t);
} else {
return t;
}
}
static String newTitleSuffix( final String path ) {
int idx = path.lastIndexOf(File.separatorChar);
if (idx > -1) {
idx = path.lastIndexOf(File.separatorChar, idx - 1);
}
if (idx > -1) {
return "..." + path.substring(idx);
} else {
return path;
}
}
static void deobfuscate(
final YGuardLogParser parser, final JTextArea textArea
) {
String[] lines = textArea.getText().split("\n");
lines = parser.translate(lines);
final StringBuffer sb = new StringBuffer();
for (int i = 0; i < lines.length; ++i) {
sb.append(lines[i]).append("\n");
}
textArea.setText(sb.toString());
textArea.setCaretPosition(0);
}
static void sort( final DefaultTreeModel model, final Comparator c ) {
sortRecursively((DefaultMutableTreeNode) model.getRoot(), c);
model.nodeStructureChanged((DefaultMutableTreeNode) model.getRoot());
}
private static void sortRecursively(DefaultMutableTreeNode parent, Comparator c) {
if (parent.getChildCount() > 0) {
if (parent.getChildCount() > 1) {
sort(parent, c);
}
for (Enumeration enu = parent.children(); enu.hasMoreElements();) {
DefaultMutableTreeNode tn = (DefaultMutableTreeNode) enu.nextElement();
sortRecursively(tn, c);
}
}
}
private static void sort(DefaultMutableTreeNode parent, Comparator c) {
DefaultMutableTreeNode[] children = new DefaultMutableTreeNode[parent.getChildCount()];
for (int i = 0; i < children.length; ++i) {
children[i] = ((DefaultMutableTreeNode) parent.getChildAt(i));
}
parent.removeAllChildren();
Arrays.sort(children, c);
for (int i = 0; i < children.length; ++i) {
parent.add(children[i]);
}
}
static final class MappedNameComparator implements Comparator {
public int compare(Object o1, Object o2) {
Mapped m1 = (Mapped) ((DefaultMutableTreeNode)o1).getUserObject();
Mapped m2 = (Mapped) ((DefaultMutableTreeNode)o2).getUserObject();
if (m1.getClass() != m2.getClass()) {
if (m1.getClass() == PackageStruct.class) {
return -1;
} else if (m2.getClass() == PackageStruct.class) {
return 1;
}
if (m1.getClass() == ClassStruct.class) {
return -1;
} else if (m2.getClass() == ClassStruct.class) {
return 1;
}
if (m1.getClass() == MethodStruct.class) {
return -1;
} else if (m2.getClass() == MethodStruct.class) {
return 1;
}
}
return m1.getMappedName().compareTo(m2.getMappedName());
}
}
static final class NameComparator implements Comparator {
public int compare(Object o1, Object o2) {
Mapped m1 = (Mapped) ((DefaultMutableTreeNode)o1).getUserObject();
Mapped m2 = (Mapped) ((DefaultMutableTreeNode)o2).getUserObject();
if (m1.getClass() != m2.getClass()) {
if (m1.getClass() == PackageStruct.class) {
return -1;
} else if (m2.getClass() == PackageStruct.class) {
return 1;
}
if (m1.getClass() == ClassStruct.class) {
return -1;
} else if (m2.getClass() == ClassStruct.class) {
return 1;
}
if (m1.getClass() == MethodStruct.class) {
return -1;
} else if (m2.getClass() == MethodStruct.class) {
return 1;
}
}
return m1.getName().compareTo(m2.getName());
}
}
private static class FileFilterImpl extends FileFilter {
private final String suffix;
private final String description;
FileFilterImpl( final String suffix, final String description ) {
this.suffix = suffix == null ? "" : suffix.toLowerCase();
this.description = description;
}
public boolean accept( final File f ) {
return f.isDirectory() || f.getName().toLowerCase().endsWith(suffix);
}
public String getDescription() {
return description;
}
}
private static final class UiContext {
final JFrame frame;
final JTree mappingTree;
final JTextArea textArea;
final JMenu recentMenu;
final JFileChooser fileChooser;
UiContext(
final JFrame frame,
final JTree mappingTree,
final JTextArea textArea,
final JMenu recentMenu,
final JFileChooser fileChooser
) {
this.frame = frame;
this.mappingTree = mappingTree;
this.textArea = textArea;
this.recentMenu = recentMenu;
this.fileChooser = fileChooser;
}
}
private abstract static class AbstractOpenAction extends AbstractAction {
final UiContext context;
AbstractOpenAction( final UiContext context, final String name ) {
super(name);
this.context = context;
}
void open( final File path ) {
final JTree tree = context.mappingTree;
try {
setParser(tree, newParser(path));
onOpened(context, path);
} catch (Exception ex) {
showErrorMessage(toErrorMessage(path, ex), tree);
}
}
void onOpened( final UiContext context, final File path ) {
context.textArea.setText("");
context.frame.setTitle(newTitle(path.getAbsolutePath()));
}
}
private static final class RecentAction extends AbstractOpenAction {
final File path;
JMenuItem item;
RecentAction( final UiContext context, final File path ) {
super(context, LogParserView.newTitleSuffix(path.getAbsolutePath()));
this.path = path;
}
@Override
public void actionPerformed( final ActionEvent e ) {
open(path);
}
@Override
void onOpened( final UiContext context, final File path ) {
updateRecent(context, path);
updateFileChooser(context, path);
super.onOpened(context, path);
}
private void updateRecent( final UiContext context, final File path ) {
final JMenu menu = context.recentMenu;
final JMenuItem ami = getItem();
if (ami != null) {
for (int i = 0, n = menu.getItemCount(); i < n; ++i) {
final JMenuItem mi = menu.getItem(i);
if (mi == ami) {
menu.remove(i);
menu.add(ami, 0);
break;
}
}
}
}
private void updateFileChooser( final UiContext context, final File path ) {
final File parent = path.getParentFile();
if (parent != null) {
context.fileChooser.setCurrentDirectory(parent);
}
}
JMenuItem getItem() {
return item;
}
void setItem( final JMenuItem jc ) {
this.item = jc;
}
}
}