Zero to hero in 2 hours with JavaFX

At work I’ve been working with Swing GUIs and I heard about JavaFX as a modern alternative. I decided to give it a try by working on a self contained project and was very pleased with the results I got in just two hours.

The task

I have been thinking about visualizing all my photos (travel and otherwise) as a timeline and on a map, and associate each photo with a caption. It would provide an alternative way of reliving the past. The first step would be to display a list of image files, show the image, and associated metadata such as date, location which is what this post is about. The next steps (out of the current scope) would be to provide a way to annotate or provide comments to photos, and visualize them on a map and timeline.

Step 1: Get the tools

I already have IntelliJ, and creating a new JavaFx project was quite straight forward, literally ​File > New > JavaFX project. It gave me a few sample files, I ran it and it gave me a “Hello world” window. Awesome!

I had a priori knowledge that there was a JavaFX GUI builder that was similar to NetBean’s Swing GUI builder, which I used briefly at work, so I searched for it. It is called Java Scene builder and I was about to hit my first road bump. Java’s website is notoriously archaic compared to other modern language website. Searching on their website for the tool sent me to the page below. However, none of the links in the search results brought me to the correct download page. This was frustrating.

where-to-download

I spent 5 minutes trying to figure out where the download link was. For example, one of the links brought me to the page shown below. It seems logical to click on the link that has the word Download Page. But there will be no download links there. Apparently the correct link to click on is “moved”. Go figure.

java

The saving grace was the smooth installation process. I started the Scene builder and was greeted with a rather modern looking UI, nice! There are even templates to help me get started. And detractors say Java’s boilerplate is a bad thing …

I chose a “complex application template” and using the given layout, I decided I would have a list on the left, the image viewer in the center, and metadata on the right.

scenebuilder

At this point I was experiencing a gulf of execution. So I read some guides and watched some videos to help me.

Step 2: Tweaking the sample with baby steps

From the video and guides I learned that I could use the @FXML annotation to magically link class variables to GUI components defined in XML and they would be bound properly at runtime. The only criteria is the fx:id in the GUI matches the variable name. There were a few gotchas here and there, but they were minor enough that a simple search would reveal the answer immediately. These were mainly due to the unfamiliarity with the toolbars available in the GUI.

I tried to take small steps. I first tried to add a list view, and try to populate it. The documentation page really helped a lot, with the examples given.

Next, I wrote a small class that would return me all the image file in a folder and got the list view to show all files in a directory. So far so good.

public class ImageProvider {
    private final Path p;

    public ImageProvider(Path p) {
        this.p = p;
    }

    public Stream<Path> getImages() throws IOException {
        Stream<Path> list = Files.list(p);
        return list.filter(x -> x.toString().endsWith("jpg") || x.toString().endsWith("jpeg"));
    }
}
public void getData() throws IOException {
    ImageProvider imageProvider = new ImageProvider(Paths.get(imagesDir));
    Stream<Path> images = imageProvider.getImages();
    listItems = FXCollections.observableList(images.map(Path::toString).collect(Collectors.toList()));
}

Next I wanted to attach a listener so that when I click on an item on the list, it would load the image and display it.

When setting this up I noticed a few differences from Swing. In Swing, I would have dealt with the List collection, but here it’s an ObservableList instead. Next, I would have attached an ActionListener on the ListView and register a callback but here I had to go an extra level down and work with selectedItemProperty, whatever that is:

listView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
    statusBarText.setText(String.format("Old value: %s, new value: %s", oldValue, newValue));
    try {
        loadImage(Paths.get(newValue));
    } catch (ImageProcessingException | IOException e) {
        e.printStackTrace();
    }
});

Getting the image to load was fairly straightforward too: Create an ImageView in the GUI and then call setImage in the callback. My first attempt resulted in images appearing too small.

small

I spent a while trying to figure out how best to maximize the real estate space there and went for a naive solution which just sets the width and height property to that of the scene, which is assume is the middle open space.

public void loadImage(Path p) throws IOException, ImageProcessingException {
    BufferedInputStream is = new BufferedInputStream(new FileInputStream(p.toFile()));
    Image img = new Image(is);
    imageView.setImage(img);
    imageView.setCache(true);
    imageView.setPreserveRatio(true);
    imageView.fitWidthProperty().bind(imageView.getScene().widthProperty());
    imageView.fitHeightProperty().bind(imageView.getScene().heightProperty());
...

At this point, I’m quite satisfied with what I have so far. Next step was the get the metadata out. Looking at the Image API, it didn’t seem like there was anything useful to extract EXIF data. So I did a quick search and got pointed to https://github.com/drewnoakes/metadata-extractor.

Step 3: Pulling in a dependency

At work I use SBT to pull in dependency. I was also familiar with package managers like Golang’s go get, or NodeJS’s npm. But I had no idea how to pull in dependencies for Java. I didn’t want to use SBT because I felt that it was mainly for Scala.

With zero idea on how create a build file, I started clicking around IntelliJ hoping to stumble on the correct command. I know there were Maven, Ant and Gradle so I was looking out for one of those.

IntelliJ makes it easy to create a build file, I right click on my project, and chose add framework support and I clicked on Maven. It generated a pom.xml file and it all looks promising. Searching on http://search.maven.org/ gave me the snippet I needed to paste into the file. At this point I hit another slight bump, apparently the pom file messed up my intelliJ configuration and now it thinks I’m trying to compile my program with an older Java compiler. It took me a while to hunt down the relevant configuration and make it work for Java 1.8 again.

When Ctrl + F9 no longer complains. I decided to try the library. The getting started page provided useful snippets which I first tried printing on the console. They looked promising so now it’s time to use the right panel to display a table of metadata name and values.

The documentation page again provided useful snippets. The only problem was I needed to call a method instead of accessing a field, which was the only example given. So I had to figure out how to convert a normal String value into an ObservableValue type, which turns out to be trivial.

TableColumn<Tag, String> tag_name = new TableColumn<>("Tag name");
tag_name.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getTagName()));
TableColumn<Tag, String> tag_value = new TableColumn<>("Tag Value");
tag_value.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getDescription()));
metadataTableView.getColumns().setAll(tag_name, tag_value);

The final result is the following:

final.PNG

Closing remarks

For someone who has never touched JavaFX in his life, I think this is pretty good progress for 2 hours. In the past, I would have built a web GUI for visualization, and then write a backend server that hosts the front end and expose REST endpoints for logic but that route takes a much longer time. Unless I need my app to be accessible online, the architectural overhead is simply too high.

Even though I don’t fully understand all the concepts of JavaFX (such as all the Observables flying around) I was able to explore the API and achieve what I wanted which is not always a given.

Apart from the annoying Java website, the development process was enjoyable and the documentation and examples out there are really a great place for a beginner to get productive. The next time someone asks me for advice on how to start learning programming I might actually suggest they do a GUI project with JavaFX!

I have been pwned

Security breach is a matter of when, not if.

This post is a recount of my virgin encounter of my password being compromised.

The myriad of the data breaches in the last decade saw the birth of https://haveibeenpwned.com/. Some websites have taken a step further to warn its users of potential password breaches. The community have also developed many tools to help users check if their password has been compromised.

I was fairly surprized to receive a notification as I tried to log onto github that my password has been pwned since I consider myself fairly security-savvy and rather paranoid. That being said, that password is more than a decade old and I haven’t changed it since.

So I followed my protocol. First step is to stop the bleeding. I opened my password manager to find out which other sides were using that same password and went to change passwords for those services.

Step two was to figure out the cause of the leak to evaluate the extent of the damage. Did I click on a phishing website? Is my computer compromised by malware? The website provides a useful utility where you can enter your email and/or password and it will tell you where or how many count of data breach there was. For me, it turns out to be the dropbox databreach in 2012. [Yes, now you know I reused password for dropbox and github but that was before I went all in with a password manager].

It was slightly uneasy to type my password into a text field on a random website that claims to be on my side but since I was going to discard that password, it was low risk anyway. I was also tempted to test other passwords to determine if they have been compromised, but the paranoid side of me didn’t want to. Today’s browsers and web technologies are so feature rich that I don’t fully trust a piece of software unless I can audit the code. One can never be too careful because the bad guys can always masquerade as the good guys.

Now that I have changed my password, life continues.

 

So, about this viral maths birthday problem …

Here’s a warm up joke:

Three logicians walk into a bar. The bar tender asks “Do all of you want a drink?”

The first logician says, “I don’t know.”

The second logician says, “I don’t know.”

The third logician says, “Yes!”

The following question went viral on Facebook. It had a sensationalising title: “Primary 5 [11 year old] mathematics question”, which later turned out to be incorrect as it was actually a Secondary 3 [15 year old] maths olympiad question.

I didn’t give much though to this question when I first saw it because I have seen such questions before so it wasn’t something new. But someone shared this with me in a private chat and so I thought might as well write a short blog post about how I solved it.

math1e

Continue reading So, about this viral maths birthday problem …

Manacher Algorithm

This post tries to explain the idea behind Manacher’s algorithm. It is written mainly for my own future reference so it might lack some clarity. I might improve upon it in the future when I have the time.

Introduction

Manacher’s algorithm is a O(N) solution for finding the longest palindromic substring of a given string.

A palindrome is a sequence of symbols or elements that reads the same forward or reversed.

It is a dynamic programming problem. We let dp[i] be the longest palindrome length for the palindrome that is centered at character i. We use a bottom up approach and fill the table dp. After that we find the maximum value in dp and retrieve the substring.

The transformation (preprocessing)

A clever trick to avoid splitting cases for odd and even as well as boundary checks is to transform the given string into one with delimiters added in between each character.

abacabb \mapsto \#a\#b\#a\#c\#a\#b\#b\#

preprocess

 

This will help us easily identify our palindrome because in our transformed string, the longest palindrome must start and end on a delimiter, and this maps nicely back to the index of the original string, providing an easy way to extract the substring. For example, to extract the palindrome bb, we note that it is a palindrome about index 12, and the left and right limits are 10 and 14 respectively. Dividing it by 2 gives us the indices of the original string, 5 and 7. And we can extract the substring by using s.substring(5,7).

The crux

The crux of the algorithm is noting that after calculating some dp[k] , we can save calculations of dp[n] for k < n \le k + dp[k] .

To see why. Let us denote the indices of left limit of the palindrome as L, right limit as R, and the center as C. Let T be the transformed string. Then let C = T[k], L = T[k-m], R = T[k+m] where m is the range on either side of the palindrome centered at C.

Suppose we are now calculating dp[i] where i is between C and R. There are two cases for this palindrome that is centered at i. Either it extends past R or it doesn’t. Suppose it doesn’t extend past R. Then by looking at the reflected point about C, that is dp[2C – i], we know that the palindrome centered at that point must not extend past L either. Therefore, dp[i] = dp[2C-i]. In the other case, the palindrome extends beyond R so we have to manually calculate the length of this palindrome centered at i. However, we can speed up this calculation by noting that since this extends past R, it’s length must be at least R-i, so we can start comparing characters after that distance.

This translate into code by setting dp[i] = min(dp[2C-i], R-i) and then updating dp[i] by trying to extend the palindrome.

Setting up a Unix environment in Windows

TL;DR :

  • Get consolez
  • get cygwin, and apt-cyg
  • Map windows’ version of software so they can be run in the shell. e.g. vim

This post will talk about how I set up my Windows development console and how it integrates nicely with a Unix workflow.

My laptop’s hardware incompatibility (overheating GPU and a non-working fingerprint reader) when running Linux is the main motivation for this setup. I find that the Unix terminal is more conducive for programming than the Windows’ terminal for some reasons. So because I am stuck with using Windows for now, I really like being able to emulate a Unix environment as much as possible.

Most common way to do so is to install a Unix environment such as cygwin or mingw, or if you install git, you’ll have git bash. However, these are quite spartan and feels more like a clutch rather than a proper tool. I also wanted to be able to use tabs and have my home directory similar to that of Windows. In short, I wanted those terminals to be a drop-in replacement for cmd.

Then I found console. Firstly, it has tabs, which makes things awesome (aside: It really amazes me that tabbed windows explorer isn’t a thing even up till Windows 8, when tabbed browsers was already a thing as early as Windows XP). Secondly, it allows you to launch different types of shell, both cmd and those terminals mentioned above. That is great because now I can bind a shortcut key like ctrl + alt + t to launch console, and go into a Unix environment. Later on I found a fork of console called consolez, which has many awesome features like keybinding for hiding/revealing the console, running cmd with elevated privileges, windows snap. The last one means I can break out of the 80 by 25 window, which makes vim look awesome.

However, such emulators fall short of the Unix terminal because not all POSIX softwares are available by default, eg ssh. Furthermore, installing a software on such terminal is not as simple as apt-get install <package> in Linux. Luckily, for cygwin, there is apt-cyg, which allows you to install software by typing apt-cyg install <package>.

Still, that is not a silver bullet, some software deals with the windows libraries and the above method just doesn’t work. In that case, a simple solution will be to install the windows binary version, and then copy the executable file into the /usr/bin directory for the terminal.

For example, I wanted to run mysql console from cygwin. But it was not installed by default. Using apt-cyg install mysql gave me some error when running it which I suspect is because the system calls don’t match up exactly between the two environment. However, because I already have mysql installed via wamp, I simply copied the binary from wamp/bin/mysql/mysql<version>/bin to cygwin/bin and viola, I was able to run mysql inside cygwin successfully.

That concludes this post. This has made my life working in the command line so much more pleasurable in Windows.