Java tutorial
/** * JReversePro - Java Decompiler / Disassembler. * Copyright (C) 2008 Karthik Kumar. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***/ package org.jreversepro.gui; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Properties; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.WindowConstants; import org.apache.commons.io.IOUtils; import org.jreversepro.JReverseProContext; import org.jreversepro.JReverseProContext.OutputType; import org.jreversepro.jls.JLSConstants; import org.jreversepro.jvm.JVMConstants; import org.jreversepro.jvm.TypeInferrer; import org.jreversepro.parser.ClassParserException; import org.jreversepro.reflect.ClassInfo; import org.jreversepro.reflect.Field; import org.jreversepro.reflect.Import; import org.jreversepro.reflect.Method; /** * Entry point for swing-based GUI * * @author Karthik Kumar */ @SuppressWarnings("serial") public class GUIMain extends JFrame implements ActionListener, GuiConstants { /** * Application Context shared between command line and GUI. */ private final JReverseProContext context; /** * Represents the MenuBar. */ private final MainMenu mMbrGen; /** * Panel containing the Reverse engineered code. */ private final ClassEditPanel pnlEditor; /** * Panel containing the status bar. */ private final StatusPanel mPnlStatusBar; /** * Path to class currently reverse engineered. */ private final String mCurrentClass = null; /** * JClassInfo class currently reverse engineered. */ private ClassInfo mClassInfo; /** * Name of the property file. */ private final String mPropertyFile; /** * Default Directory of a file open/save dialog. */ private String mCurDir; private static final String CURRENT_DIRECTORY = "."; /** * No-argument constructor. */ public GUIMain(JReverseProContext _context) { super(TITLE); mPropertyFile = System.getProperty("user.home") + System.getProperty("file.separator") + PROP_FILE; pnlEditor = new ClassEditPanel(); mPnlStatusBar = new StatusPanel(); pnlEditor.setPreferredSize(new Dimension(500, 200)); mPnlStatusBar.setPreferredSize(new Dimension(500, 20)); getContentPane().setLayout(new BorderLayout()); getContentPane().add(pnlEditor, BorderLayout.CENTER); getContentPane().add(mPnlStatusBar, BorderLayout.SOUTH); mMbrGen = new MainMenu(this); setJMenuBar(mMbrGen); mCurDir = CURRENT_DIRECTORY; mMbrGen.onViewCPool.setEnabled(false); context = _context; initAppState(); addListeners(); } /** * Method containing handlers for events generated by MenuItems. * * @param aEvent * Event generated by GUI. */ public void actionPerformed(ActionEvent aEvent) { if (aEvent.getSource() == mMbrGen.onFileOpen) { openFile(); } else if (aEvent.getSource() == mMbrGen.onFileSave) { saveFile(); } else if (aEvent.getSource() == mMbrGen.onFileExit) { appClose(); } else if (aEvent.getSource() == mMbrGen.onViewCPool) { viewPool(); } else if (aEvent.getSource() == mMbrGen.onOptFont) { showFontDialog(); } else if (aEvent.getSource() == mMbrGen.onHelpAbout) { showAbout(); } else if (aEvent.getSource() == mMbrGen.onEditCut) { cutText(); } else if (aEvent.getSource() == mMbrGen.onEditCopy) { copyText(); } } /** * Method to open a file. */ public synchronized void openFile() { CustomFileChooser chooser = new CustomFileChooser(mCurDir, "Class Files", ".class", "Open File"); if (chooser.showChooser(this, "Decompile File") == JFileChooser.APPROVE_OPTION) { File f = chooser.getSelectedFile(); mCurDir = f.getAbsolutePath(); try { reverseEngineer(f); formatTitle(f.getAbsolutePath()); } catch (Exception _ex) { (new DlgError(this, f.toString(), _ex)).setVisible(true); } } } /** * Method to reverse engineer a file. * * @param aFile * Class file to be reverse engineered. * @throws ClassParserException * Thrown if class file not in proper format. * @throws IOException * Thrown if error occured in reading class file. * @throws RevEngineException * Thrown if error occured in reverse engineering file. */ private synchronized void reverseEngineer(File aFile) throws ClassParserException, IOException { mClassInfo = context.loadResource(aFile.getAbsolutePath()); // Extract code. // TODO: Select Disassembler / Decompiler based on a variable. String code = context.print(OutputType.DECOMPILER, mClassInfo); // Output the code pnlEditor.writeCode(code); mMbrGen.onViewCPool.setEnabled(true); createTree(mClassInfo, mCurrentClass); } /** * Method invoked while saving to a file. */ public void saveFile() { CustomFileChooser chooser = new CustomFileChooser(mCurDir, "Java Source Files", ".java", "Save File"); if (chooser.showChooser(this, "Save File") == JFileChooser.APPROVE_OPTION) { try { pnlEditor.writeToFile(this, chooser.getSelectedFile()); } catch (Exception ex) { // TODO: Do something about this exception } } } /** * Method invoked while closing a file. */ public void appClose() { if (ConfirmCloseDialog.confirmExit(this)) { saveProperties(); } } /** * Method invoked while text is cut. */ public void cutText() { // System.out.println("Text Cut"); } /** * Method invoked while text is copied. */ public void copyText() { // System.out.println("Text Copied"); } /** * Method invoked while ConstantPool is viewed. */ public void viewPool() { DlgConstantPool dlgPool = new DlgConstantPool(this, mCurrentClass, mClassInfo.getConstantPool()); dlgPool.setVisible(true); dlgPool = null; } /** * Method invoked while System Font is being viewed. */ public void showFontDialog() { DlgFont dlg = new DlgFont(this, "System Fonts"); if (dlg.showFontDialog() == DlgFont.SELECTED) { pnlEditor.setEditorFont(dlg.getChosenFont()); } } /** * Method invoked to show the About dialog box. */ public void showAbout() { new DlgAbout(this, "About"); } /** * Method invoked to initialize the GUI parameters. */ private void initAppState() { setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); FileInputStream fis = null; try { Properties pp = new Properties(); fis = new FileInputStream(mPropertyFile); pp.load(fis); int x = Integer.parseInt(pp.getProperty(XPOS)); int y = Integer.parseInt(pp.getProperty(YPOS)); int width = Integer.parseInt(pp.getProperty(XSIZE)); int height = Integer.parseInt(pp.getProperty(YSIZE)); mMbrGen.setFlag(pp.getProperty(DECOMPILE_FLAG)); pnlEditor.setEditorFont(new Font(pp.getProperty(FONT), Font.PLAIN, DlgFont.OPTIMUM_SIZE)); setLocation(x, y); setSize(width, height); } catch (FileNotFoundException fnfe) { setLocation(0, 0); setSize(800, 550); pnlEditor.setEditorFont(new Font(ClassEditPanel.DEFAULT_FONT, Font.PLAIN, DlgFont.OPTIMUM_SIZE)); System.err.println("Failed to load property file"); } catch (IOException ioe) { System.err.println("Exception while closing a property file "); } finally { IOUtils.closeQuietly(fis); } } /** * Method to add property listeners to MenuItems and the MainFrame. */ private void addListeners() { mMbrGen.onFileOpen.addActionListener(this); mMbrGen.onFileSave.addActionListener(this); mMbrGen.onFileExit.addActionListener(this); mMbrGen.onEditCut.addActionListener(this); mMbrGen.onEditCopy.addActionListener(this); mMbrGen.onViewCPool.addActionListener(this); mMbrGen.onOptFont.addActionListener(this); mMbrGen.onHelpAbout.addActionListener(this); addWindowListener(new WindowAdapter() { /** * WindowClosing event handler. * * @param aEvent * Event generated. */ @Override public void windowClosing(WindowEvent aEvent) { appClose(); super.windowClosing(aEvent); } }); } /** * Formats the title from the string Rhs and sets the title of the Frame. * * @param aFileName * Full Path name to the class being reverse engineered. * */ private void formatTitle(String aFileName) { int dotIndex = aFileName.indexOf("."); if (dotIndex != -1) { String className = aFileName.substring(0, dotIndex); setTitle(TITLE + " - " + className); } } /** * Save the state of the GUI as a properties file. * */ private void saveProperties() { FileOutputStream fos = null; try { Properties pp = new Properties(); pp.setProperty(DECOMPILE_FLAG, Boolean.valueOf(mMbrGen.onDecompiler.isSelected()).toString()); pp.setProperty(XPOS, Integer.valueOf(getLocation().x).toString()); pp.setProperty(YPOS, Integer.valueOf(getLocation().y).toString()); pp.setProperty(XSIZE, Integer.valueOf(getSize().width).toString()); pp.setProperty(YSIZE, Integer.valueOf(getSize().height).toString()); pp.setProperty(FONT, pnlEditor.getEditorFont().getFamily()); fos = new FileOutputStream(mPropertyFile); pp.store(fos, PROP_HEADING); } catch (Exception _ex) { System.err.println("Failed to write Properties" + mPropertyFile); System.err.println(_ex); } finally { IOUtils.closeQuietly(fos); } } /** * Creates the JTree of the LHS Panel ssociated with the class. * * @param aClassInfo * Information about the class. * @param aCurrentClass * Name of the current class being reverse engineered. * */ private void createTree(ClassInfo aClassInfo, String aCurrentClass) { Import mImports = aClassInfo.getConstantPool().getImportedClasses(); int dotIndex = aCurrentClass.indexOf("."); if (dotIndex != -1) { aCurrentClass = aCurrentClass.substring(0, dotIndex); } List<Field> listFields = aClassInfo.getFields(); List<Method> listMethods = aClassInfo.getMethods(); List<String> result = new ArrayList<String>(); for (Field field : listFields) { StringBuilder sb = new StringBuilder(""); String datatype = Import.getClassName(TypeInferrer.getJLSType(field.getDatatype(), false)); sb.append(field.getQualifier()); sb.append(" " + datatype); sb.append(" " + field.getName()); result.add(sb.toString()); } for (Method method : listMethods) { StringBuilder sb = new StringBuilder(""); String returnType = Import.getClassName(TypeInferrer.getJLSType(method.getReturnType(), false)); String name = method.getName(); if (name.compareTo(JVMConstants.CLINIT) == 0) { sb.append(JLSConstants.STATIC); } else if (name.compareTo(JVMConstants.INIT) == 0) { sb.append(method.getQualifier()); sb.append(aCurrentClass); sb.append(writeArgs(method.getArgList(), mImports)); } else { sb.append(method.getQualifier()); sb.append(returnType); sb.append(" " + name); sb.append(writeArgs(method.getArgList(), mImports)); } result.add(sb.toString()); } pnlEditor.createModel(this, aCurrentClass, result); } /** * Returns the argument in Java language Class Format. * * @param aArgs * List of arguments of class names in JVM class format. * @param aImports * List of imported classes. * @return A Concatenated list of classes separated by a comma. */ private StringBuilder writeArgs(List<String> aArgs, Import aImports) { StringBuilder result = new StringBuilder("("); boolean first = true; for (String str : aArgs) { if (first) { first = false; } else { result.append(" ,"); } String argType = Import.getClassName(TypeInferrer.getJLSType(str, false)); result.append(argType); } result.append(")"); return result; } }