Created
July 2, 2014 08:10
-
-
Save ytoshima/da6f586cb3234fd694c0 to your computer and use it in GitHub Desktop.
findjar-ui
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
JAVA_OPTS=-Xss2m | |
JAVA_HOME=$(/usr/libexec/java_home) | |
#SCALA_HOME=~/local/scala-2.10.2 | |
SCALA_HOME=~/local/scala-2.9.2 | |
export JAVA_OPT JAVA_HOME SCALA_HOME | |
jdkhome=$JAVA_HOME | |
$SCALA_HOME/bin/scalac -toolcp $jdkhome/jre/lib/jfxrt.jar *.scala | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<?import java.lang.*?> | |
<?import java.util.*?> | |
<?import javafx.scene.control.*?> | |
<?import javafx.scene.layout.*?> | |
<?import javafx.scene.paint.*?> | |
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0000999999975" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="UIController"> | |
<children> | |
<MenuBar prefWidth="600.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> | |
<menus> | |
<Menu mnemonicParsing="false" text="File"> | |
<items> | |
<MenuItem mnemonicParsing="false" text="閉じる" /> | |
</items> | |
</Menu> | |
<Menu mnemonicParsing="false" text="Edit"> | |
<items> | |
<MenuItem mnemonicParsing="false" text="削除" /> | |
</items> | |
</Menu> | |
<Menu mnemonicParsing="false" text="Help"> | |
<items> | |
<MenuItem mnemonicParsing="false" text="バージョン情報" /> | |
</items> | |
</Menu> | |
</menus> | |
</MenuBar> | |
<BorderPane prefHeight="363.0" prefWidth="600.0" AnchorPane.bottomAnchor="12.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="25.0"> | |
<bottom> | |
<Label fx:id="status" prefWidth="600.0" text="Status" /> | |
</bottom> | |
<center> | |
<ListView fx:id="listView" prefHeight="200.0" prefWidth="200.0" /> | |
</center> | |
<top> | |
<GridPane prefHeight="48.0" prefWidth="600.0"> | |
<children> | |
<Label fx:id="patternLabel" alignment="CENTER_RIGHT" contentDisplay="RIGHT" text="Pattern" GridPane.columnIndex="0" GridPane.halignment="RIGHT" GridPane.rowIndex="0" /> | |
<Label text="Path" GridPane.columnIndex="0" GridPane.halignment="RIGHT" GridPane.rowIndex="1" /> | |
<TextField fx:id="patternField" onKeyReleased="#keyReleasedOnPatternField" prefWidth="200.0" promptText="Enter search pattern" GridPane.columnIndex="1" GridPane.rowIndex="0" /> | |
<TextField id="" fx:id="pathField" editable="false" prefWidth="200.0" promptText="Add dir(s) to search" GridPane.columnIndex="1" GridPane.rowIndex="1" /> | |
<Button mnemonicParsing="false" onAction="#doSearchAction" text="Search" GridPane.columnIndex="2" GridPane.rowIndex="0" /> | |
<Button mnemonicParsing="false" onAction="#addDir" text="Add" GridPane.columnIndex="2" GridPane.rowIndex="1" /> | |
<CheckBox fx:id="incrementalCheck" mnemonicParsing="false" selected="true" text="Incremental" GridPane.columnIndex="3" GridPane.rowIndex="0" /> | |
</children> | |
<columnConstraints> | |
<ColumnConstraints hgrow="SOMETIMES" maxWidth="198.0" minWidth="10.0" prefWidth="62.0" /> | |
<ColumnConstraints hgrow="SOMETIMES" maxWidth="404.0" minWidth="10.0" prefWidth="361.0" /> | |
<ColumnConstraints hgrow="SOMETIMES" maxWidth="200.0" minWidth="10.0" prefWidth="73.0" /> | |
<ColumnConstraints hgrow="SOMETIMES" maxWidth="200.0" minWidth="10.0" prefWidth="104.0" /> | |
</columnConstraints> | |
<rowConstraints> | |
<RowConstraints maxHeight="25.0" minHeight="10.0" prefHeight="25.0" vgrow="SOMETIMES" /> | |
<RowConstraints maxHeight="24.0" minHeight="10.0" prefHeight="23.0" vgrow="SOMETIMES" /> | |
</rowConstraints> | |
</GridPane> | |
</top> | |
</BorderPane> | |
</children> | |
</AnchorPane> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io._ | |
import collection.JavaConversions._ | |
import java.util.jar.JarFile | |
import scala.concurrent._ | |
import ExecutionContext.Implicits.global | |
import scala.util.{Success, Failure} | |
import scala.concurrent.duration._ | |
case class MatchedEnt(path: String, names: List[String]) | |
class FindJar(path: String) { | |
val concList = true | |
import scala.util.matching.Regex | |
if (!(new File(path)).exists()) { | |
throw new IllegalArgumentException( | |
"E: path %s does not exist".format(path)) | |
} | |
val filesMapFuture: Map[String,Future[List[String]]] = getFilesMapFuture | |
def getFilesMapFuture: Map[String,Future[List[String]]] = { | |
getJarPaths.map(p => (p, future { | |
val jf = new JarFile(p) | |
val res = jf.entries.toList.map(_.getName) | |
try { | |
jf.close | |
} catch { | |
case t: java.io.IOException => | |
println("D: failed to close %s: %s".format(p, t)) | |
} | |
res | |
})).toMap | |
} | |
def jarEntNamesFuture(jarPath: String): List[String] = { | |
filesMapFuture.get(jarPath) match { | |
case None => { | |
println( | |
"E: file list is not available for %s".format(jarPath)) | |
List[String]() | |
} | |
case Some(fu) => { | |
Await.ready(fu, Duration.Inf) | |
fu.value match { | |
case Some(Success(l)) => l | |
case Some(Failure(t)) => { | |
println( | |
"E: failed to retrieve result for %s, %s".format(jarPath, t)) | |
List[String]() | |
} | |
case None => { | |
println( | |
"E: Future.value None %s".format(jarPath)) | |
List[String]() | |
} | |
} | |
} | |
} | |
} | |
def getJarPaths: List[String] = { | |
def findJar(path: String): List[String] = { | |
new File(path) match { | |
case d if d.isDirectory => { | |
val jarFiles = d.listFiles(new FileFilter { | |
override def accept(f: File): Boolean = { | |
f.isFile && f.getPath.endsWith(".jar") | |
} | |
}).map(_.getPath) | |
val subDirs = d.listFiles(new FileFilter { | |
override def accept(f: File): Boolean = f.isDirectory | |
}).map(_.getPath) | |
Array.concat(jarFiles, subDirs.flatMap(findJar(_))).toList | |
} | |
} | |
} | |
findJar(path) | |
} | |
def jarEntNames(jarPath: String): List[String] = { | |
(new JarFile(jarPath)).entries.toList.map(_.getName) | |
} | |
def candInJar(jarPath: String, re: Regex): List[String] = { | |
jarEntNames(jarPath).filter(re.findFirstIn(_)!=None) | |
} | |
def candInJarFuture(jarPath: String, re: Regex): List[String] = { | |
jarEntNamesFuture(jarPath).filter(re.findFirstIn(_)!=None) | |
} | |
def findMatchingFiles(patternStr: String): List[MatchedEnt] = { | |
val pattern = patternStr.r | |
getJarPaths.map(p => | |
MatchedEnt(p, candInJarFuture(p, pattern))).filter(_.names.size > 0) | |
} | |
def findMatchingFilesNaive(patternStr: String): List[MatchedEnt] = { | |
val pattern = patternStr.r | |
getJarPaths.map(p => | |
MatchedEnt(p, candInJar(p, pattern))).filter(_.names.size > 0) | |
} | |
} | |
object FindJar extends App { | |
def usage() { | |
val m = "usage: scala FindJar <path> <pattern>" | |
println(m) | |
} | |
if (args.size < 2) { | |
usage(); | |
} else { | |
val fj = new FindJar(args(0)) | |
val ents = fj.findMatchingFiles(args(1)) | |
for (e <- ents) { | |
println(e.path) | |
for (n <- e.names) { | |
println(" %s".format(n)) | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import javafx.application.Application | |
import javafx.application.Platform | |
import javafx.event.ActionEvent | |
import javafx.scene.input.KeyEvent | |
import javafx.scene.input.KeyCode | |
import javafx.fxml.FXML | |
import javafx.fxml.FXMLLoader | |
import javafx.scene.Scene | |
import javafx.scene.control.CheckBox | |
import javafx.scene.control.Label | |
import javafx.scene.control.TextField | |
import javafx.scene.control.ListView | |
import javafx.scene.layout.AnchorPane | |
import javafx.stage.Stage | |
import javafx.stage.DirectoryChooser | |
import java.io.File | |
import java.util.Timer | |
import java.util.TimerTask | |
import collection.JavaConversions._ | |
class UIController { | |
@FXML private var patternLabel: Label = _ | |
@FXML private var patternField: TextField = _ | |
@FXML private var pathField: TextField = _ | |
@FXML private var listView: ListView[String] = _ | |
@FXML private var incrementalCheck: CheckBox = _ | |
@FXML private var status: Label = _ | |
private val dirs = collection.mutable.ListBuffer.empty[String] | |
private val fjs = collection.mutable.ListBuffer.empty[FindJar] | |
private lazy val dirChooser = new DirectoryChooser | |
private val timer = new Timer(true /*daemon*/) | |
private var propLoadTryCount = 0 | |
private def controls = List(patternLabel, patternField, pathField, listView, incrementalCheck, status) | |
def controlsAreInitialized = controls.forall(_ != null) | |
def controlsStr = controls.mkString(",") | |
// println("UIController.<init>: controlsAreInitialized: " + controlsAreInitialized) | |
private val loadPropTask = new TimerTask { | |
def run { | |
if (controlsAreInitialized) { | |
loadPropFile | |
cancel | |
} else { | |
println("D: controls are not initialized yet: " + controlsStr) | |
if (propLoadTryCount > 10) { | |
println("W: giving up prop loading") | |
cancel | |
} | |
} | |
propLoadTryCount = propLoadTryCount + 1 | |
} | |
} | |
timer.schedule(loadPropTask, 500, 1000) | |
@FXML | |
def addDir(event: ActionEvent) { | |
dirChooser.showDialog(patternField.getScene.getWindow) match { | |
case null => | |
case d => addPathStr(d.getAbsolutePath) | |
} | |
} | |
def addPathStr(path: String) { | |
require{ val d = new File(path); d.exists && d.isDirectory } | |
dirs += path | |
fjs += new FindJar(path) | |
val pfref = pathField | |
// this method might be called on a Timer thread. | |
Platform.runLater(new Runnable { | |
def run() { | |
pfref.setText(dirs.mkString(System.getProperty("path.separator"))) | |
} | |
}) | |
updatePropFile | |
} | |
/** | |
* dump app properties to ~/.findjar.properties or _findjar.properties | |
*/ | |
protected def updatePropFile { | |
val propPath = System.getProperty("user.home") + System.getProperty("file.separator") + FindJarApp.propFilename | |
val props = new java.util.Properties | |
dirs.zipWithIndex.foreach{case (p, idx) => props.put("search.path." + idx, p)} | |
val propf = new java.io.File(propPath) | |
if (!propf.exists) propf.createNewFile | |
val os = new java.io.FileOutputStream(propf) | |
props.store(os, "FindJar property file") | |
os.close | |
} | |
protected def loadPropFile { | |
val propPath = System.getProperty("user.home") + System.getProperty("file.separator") + FindJarApp.propFilename | |
val propf = new java.io.File(propPath) | |
if (propf.exists) { | |
val is = new java.io.FileInputStream(propf) | |
val props = new java.util.Properties | |
props.load(is) | |
val searchPathKeys = props.propertyNames.toList.filter{ | |
case s: String => s.startsWith("search.path.") | |
}.asInstanceOf[List[String]].foreach((key:String) => addPathStr(props.get(key).asInstanceOf[String])) | |
println("D: loaded prop file") | |
} | |
} | |
@FXML | |
def doSearchAction(event: ActionEvent) { | |
if (patternField.getLength > 0) { | |
doSearch | |
} else { | |
status.setText("I: pattern is empty") | |
} | |
} | |
private def doSearch { | |
if (pathField.getLength > 0) { | |
val ptn = patternField.getText | |
// List[MatchedEnt] | |
val res = fjs.flatMap(_.findMatchingFiles(ptn)) | |
// List[String] | |
val ls = res.flatMap{me => | |
me.names.foldLeft(List[String]())((l,e) => (e + " -- " + me.path) :: l) | |
} | |
if (ls.size == 0) status.setText("I: No match found.") | |
else if (ls.size > 0) status.setText("I: Found %d match(es)".format(ls.size)) | |
val ol = listView.getItems | |
ol.clear | |
ol.setAll(ls:_*) | |
} else { | |
status.setText("E: dir(s) not set") | |
} | |
} | |
@FXML def keyReleasedOnPatternField(ke: KeyEvent) { | |
if (incrementalCheck.isSelected && patternField.getLength > 2) { | |
doSearch | |
} else { | |
status.setText("I: pattern too short. need at least three chars") | |
} | |
if (ke.isControlDown) handleControlOp(ke) | |
} | |
private def handleControlOp(ke: KeyEvent) { | |
require(ke.isControlDown) | |
val tf = patternField | |
ke.getCode match { | |
case KeyCode.A => tf.positionCaret(0) | |
case KeyCode.E => tf.positionCaret(tf.getLength) | |
case KeyCode.K => { | |
val pos = tf.getCaretPosition | |
tf.setText(tf.getText.substring(0, tf.getCaretPosition)) | |
tf.positionCaret(pos) | |
} | |
case KeyCode.F => tf.positionCaret(Math.min(tf.getCaretPosition + 1, tf.getLength)) | |
case KeyCode.B => tf.positionCaret(Math.max(tf.getCaretPosition - 1, 0)) | |
case _ => | |
} | |
} | |
} | |
class FindJarApp extends Application { | |
override def start(stage: Stage) { | |
try { | |
stage.setTitle("FindJar") | |
val root = FXMLLoader.load(getClass().getResource("findjar.fxml")). | |
asInstanceOf[AnchorPane] | |
val scene = new Scene(root) | |
stage.setScene(scene) | |
stage.show() | |
} catch { | |
case t: Throwable => t.printStackTrace | |
} | |
} | |
} | |
object FindJarApp extends App { | |
val propFilename = (if (System.getProperty("file.separator") == """\""") "_" else ".") + "findjar.properties" | |
// launch | |
Application.launch(classOf[FindJarApp], args:_*) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
JAVA_OPTS=-Xss2m | |
JAVA_HOME=$(/usr/libexec/java_home) | |
#SCALA_HOME=~/local/scala-2.10.2 | |
SCALA_HOME=~/local/scala-2.9.2 | |
export JAVA_OPT JAVA_HOME SCALA_HOME | |
jdkhome=$JAVA_HOME | |
$SCALA_HOME/bin/scala -toolcp $jdkhome/jre/lib/jfxrt.jar FindJarApp | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment