Skip to content

Instantly share code, notes, and snippets.

@ytoshima
Created July 2, 2014 08:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ytoshima/da6f586cb3234fd694c0 to your computer and use it in GitHub Desktop.
Save ytoshima/da6f586cb3234fd694c0 to your computer and use it in GitHub Desktop.
findjar-ui
#!/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
<?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>
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))
}
}
}
}
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:_*)
}
#!/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