diff --git a/.idea/misc.xml b/.idea/misc.xml index eb311f1..ecc80d4 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..e96534f --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/kotlin-reflect.jar b/lib/kotlin-reflect.jar index 6d7087d..9da99a2 100644 Binary files a/lib/kotlin-reflect.jar and b/lib/kotlin-reflect.jar differ diff --git a/lib/kotlin-stdlib-jre7-sources.jar b/lib/kotlin-stdlib-jre7-sources.jar index 769bb69..acd520f 100644 Binary files a/lib/kotlin-stdlib-jre7-sources.jar and b/lib/kotlin-stdlib-jre7-sources.jar differ diff --git a/lib/kotlin-stdlib-jre7.jar b/lib/kotlin-stdlib-jre7.jar index 819e578..656f193 100644 Binary files a/lib/kotlin-stdlib-jre7.jar and b/lib/kotlin-stdlib-jre7.jar differ diff --git a/lib/kotlin-stdlib-jre8-sources.jar b/lib/kotlin-stdlib-jre8-sources.jar index cfc1c61..d6ed88c 100644 Binary files a/lib/kotlin-stdlib-jre8-sources.jar and b/lib/kotlin-stdlib-jre8-sources.jar differ diff --git a/lib/kotlin-stdlib-jre8.jar b/lib/kotlin-stdlib-jre8.jar index 606f9c7..b3e193f 100644 Binary files a/lib/kotlin-stdlib-jre8.jar and b/lib/kotlin-stdlib-jre8.jar differ diff --git a/lib/kotlin-stdlib-sources.jar b/lib/kotlin-stdlib-sources.jar index 3197dda..d77c60a 100644 Binary files a/lib/kotlin-stdlib-sources.jar and b/lib/kotlin-stdlib-sources.jar differ diff --git a/lib/kotlin-stdlib.jar b/lib/kotlin-stdlib.jar index 30e90c5..9f30725 100644 Binary files a/lib/kotlin-stdlib.jar and b/lib/kotlin-stdlib.jar differ diff --git a/lib/kotlin-test.jar b/lib/kotlin-test.jar index cfc87d6..98caff7 100644 Binary files a/lib/kotlin-test.jar and b/lib/kotlin-test.jar differ diff --git a/src/ClassySharkBytecodeViewer.kt b/src/ClassySharkBytecodeViewer.kt index 8cced6d..40b84fe 100644 --- a/src/ClassySharkBytecodeViewer.kt +++ b/src/ClassySharkBytecodeViewer.kt @@ -19,19 +19,24 @@ import java.awt.BorderLayout import java.awt.Color import java.awt.Dimension import java.awt.Font +import java.beans.PropertyChangeEvent +import java.beans.PropertyChangeListener import java.io.* +import java.util.prefs.Preferences import javax.swing.* import javax.swing.filechooser.FileNameExtensionFilter -class ClassySharkBytecodeViewer @Throws(Exception::class) -constructor() : JFrame() { - - internal var javaArea: JTextPane - internal var asmArea: JTextPane - internal var ASM: String = "" - internal val panelTitle = "ClassyShark Byte Code Viewer" - internal val RESULT_AREAS_BACKGROUND = Color(46, 48, 50) - internal val INPUT_AREA_BACKGROUND = Color(88, 110, 117) +class ClassySharkBytecodeViewer: JFrame(), PropertyChangeListener { + + private var loadedFile: File + private var loadedFileWatcherThread: FileWatcherThread + private val searchText: JTextField + private var javaArea: JTextPane + private var asmArea: JTextPane + private var ASM: String = "" + private val panelTitle = "ClassyShark Byte Code Viewer" + private val RESULT_AREAS_BACKGROUND = Color(46, 48, 50) + private val INPUT_AREA_BACKGROUND = Color(88, 110, 117) object IntroTextHolder { @JvmStatic val INTRO_TEXT = "\n\n\n\n\n\n\n\n\n\n" + @@ -39,7 +44,7 @@ constructor() : JFrame() { "\n\n\n\n\n ClassyShark ByteCode Viewer ver." + Version.MAJOR + "." + Version.MINOR } - + init { title = panelTitle defaultCloseOperation = JFrame.EXIT_ON_CLOSE @@ -47,10 +52,11 @@ constructor() : JFrame() { val mainPanel = JPanel() mainPanel.layout = BoxLayout(mainPanel, BoxLayout.Y_AXIS) + val tabbedPane = JTabbedPane() val openButton = JButton(createImageIcon("ic_open.png", "Search")) openButton.addActionListener { openButtonPressed() } - val searchText = JTextField() + searchText = JTextField() searchText.font = Font("Menlo", Font.PLAIN, 18) searchText.background = INPUT_AREA_BACKGROUND searchText.foreground = Color.CYAN @@ -74,7 +80,9 @@ constructor() : JFrame() { javaArea.transferHandler = FileTransferHandler(this) javaArea.preferredSize = Dimension(830, 250) val javaScrollPane = JScrollPane(javaArea) - resultPanel.add(javaScrollPane) + + tabbedPane.addTab("Java", null, javaScrollPane, + "Java sources") asmArea = SyntaxPane() asmArea.font = Font("Menlo", Font.PLAIN, 18) @@ -83,8 +91,9 @@ constructor() : JFrame() { asmArea.foreground = Color.CYAN asmArea.text = SharkBG.SHARKEY val asmScrollPane = JScrollPane(asmArea) - resultPanel.add(asmScrollPane) - + tabbedPane.addTab("Bytecode", null, asmScrollPane, + "Java Bytecode") + resultPanel.add(tabbedPane) mainPanel.add(resultPanel) val asmSearch = IncrementalSearch(asmArea) @@ -97,10 +106,45 @@ constructor() : JFrame() { contentPane = mainPanel pack() setLocationRelativeTo(null) + + loadedFile = File("") + loadedFileWatcherThread = FileWatcherThread("", "", this) + } + + override fun propertyChange(evt: PropertyChangeEvent) { + if (evt.propertyName == "command") { + // Received new command (outside EDT) + SwingUtilities.invokeLater { + // Updating GUI inside EDT + onFileRecompiled() + } + } + } + + fun onFileLoaded(file: File) { + try { + this.loadedFile = file + title = panelTitle + " - " + file.name + + fillJavaArea(file) + fillAsmArea(file) + + watchLoadedFileChanges(file) + } catch (e: FileNotFoundException) { + e.printStackTrace() + } catch (e: IOException) { + e.printStackTrace() + } } + private val LAST_USED_FOLDER: String = "ClassySharkBytecodeViewer" + private fun openButtonPressed() { - val fc = JFileChooser("ClassyShark Bytecode Viewer") + + val prefs = Preferences.userRoot().node("ClassySharkBytecodeViewer") + + val fc = JFileChooser(prefs.get(LAST_USED_FOLDER, + File(".").absolutePath)) fc.fileSelectionMode = JFileChooser.FILES_AND_DIRECTORIES val filter = FileNameExtensionFilter("classes", "class") @@ -109,15 +153,18 @@ constructor() : JFrame() { val retValue = fc.showOpenDialog(JPanel()) if (retValue == JFileChooser.APPROVE_OPTION) { - onFileDragged(fc.selectedFile) + onFileLoaded(fc.selectedFile) + + prefs.put(LAST_USED_FOLDER, fc.selectedFile.parent) } } - fun onFileDragged(file: File) { + private fun onFileRecompiled() { try { - title = panelTitle + " - " + file.name - fillJavaArea(file) - fillAsmArea(file) + title = panelTitle + " - " + loadedFile.name + searchText.text = "" + fillJavaArea(loadedFile) + fillAsmArea(loadedFile) } catch (e: FileNotFoundException) { e.printStackTrace() } catch (e: IOException) { @@ -125,6 +172,20 @@ constructor() : JFrame() { } } + private fun watchLoadedFileChanges(file: File) { + + if(loadedFileWatcherThread.isAlive) { + loadedFileWatcherThread.interrupt() + } + + loadedFileWatcherThread = FileWatcherThread(file.parent, file.name, this) + loadedFileWatcherThread.start() + + // the line below must be there otherwise threading + file system events + // don't work possibly after the thread had started + loadedFileWatcherThread.addPropertyChangeListener(this) + } + private fun fillAsmArea(file: File) { javaArea.caretPosition = 0 val inputStream = FileInputStream(file) @@ -158,11 +219,11 @@ constructor() : JFrame() { private fun createImageIcon(path: String, description: String): ImageIcon? { val imgURL = javaClass.getResource(path) - if (imgURL != null) { - return ImageIcon(imgURL, description) + return if (imgURL != null) { + ImageIcon(imgURL, description) } else { System.err.println("Couldn't find file: " + path) - return null + null } } @@ -173,7 +234,7 @@ constructor() : JFrame() { val bytecodeViewer = ClassySharkBytecodeViewer() if (args.size == 1) { - bytecodeViewer.onFileDragged(File(args[0])) + bytecodeViewer.onFileLoaded(File(args[0])) } else if (args.size > 1) { System.out.println("Usage: java -jar ClassySharkBytecodeViewer.jar ") } diff --git a/src/FileTransferHandler.kt b/src/FileTransferHandler.kt index 3c8102a..de52a69 100644 --- a/src/FileTransferHandler.kt +++ b/src/FileTransferHandler.kt @@ -32,7 +32,7 @@ class FileTransferHandler(private val classySharkBytecodeViewer: ClassySharkByte return false } - data.map { it as File }.forEach { classySharkBytecodeViewer.onFileDragged(it) } + data.map { it as File }.forEach { classySharkBytecodeViewer.onFileLoaded(it) } return true diff --git a/src/FileWatcherThread.kt b/src/FileWatcherThread.kt new file mode 100644 index 0000000..7e7519d --- /dev/null +++ b/src/FileWatcherThread.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2017 Google Inc. + * + * 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. + */ + +import java.beans.PropertyChangeListener +import java.beans.PropertyChangeSupport +import java.nio.file.* +import java.nio.file.StandardWatchEventKinds.* + +// todo make package com.borisfarber.csviewer +class FileWatcherThread @Throws(Exception::class) +constructor(directoryName: String, private val fileName: String, + listener:PropertyChangeListener) : Thread() { + + private val watcher: WatchService = FileSystems.getDefault().newWatchService() + private val pcs: PropertyChangeSupport + + private var command: String? = null + + init { + val dir = Paths.get(directoryName) + dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY) + println("Watch Service registered for dir: " + dir.fileName) + this.pcs = PropertyChangeSupport(listener) + } + + override fun run() { + while (true) { + val key: WatchKey + try { + key = watcher.take() + } catch (ex: InterruptedException) { + return + } + + for (event in key.pollEvents()) { + val kind = event.kind() + + val ev = event as WatchEvent + val fileName = ev.context() + + val commandText = kind.name() + ": " + fileName + println(commandText) + + if (fileName.endsWith(this.fileName)) { + setCommand(commandText) + } + } + + val valid = key.reset() + if (!valid) { + break + } + } + } + + private fun setCommand(command: String) { + val old = this.command + this.command = command + pcs.firePropertyChange("command", old, command) + } + + fun addPropertyChangeListener(listener: PropertyChangeListener) { + pcs.addPropertyChangeListener(listener) + } + + companion object { + @Throws(Exception::class) + @JvmStatic + fun main(args: Array) { + val directoryName = "/Users/" + + val dwd = FileWatcherThread(directoryName, + "User.class", PropertyChangeListener { }) + dwd.run() + } + } +} \ No newline at end of file diff --git a/src/Version.kt b/src/Version.kt index 7ade339..ac1c4dd 100644 --- a/src/Version.kt +++ b/src/Version.kt @@ -2,6 +2,6 @@ * This class holds the current ClassySharkBytecodeViewer version */ object Version { - val MAJOR = 5 + val MAJOR = 6 val MINOR = 0 } \ No newline at end of file