Tuesday, February 19, 2013

Rebasing in SVN

Suppose you are creating a branch from a trunk and you need to merge the commits from the trunk to the branch. Unlike Git, SVN does not really have a concept of rebasing. To do that in SVN, it requires quite a bit of work. To simplify the tasks of rebasing in SVN, I created small tools, one in Go and the other one in Python.
svnrebase.go
package main

import (
    "bufio"
    "bytes"
    "errors"
    "fmt"
    "io"
    "os"
    "os/exec"
    "strings"
)

const BaseRevFile = "base_rev.txt"

func getLatestBaseSVNRev(svnBaseURL string) (string, error) {
    out, e1 := exec.Command("svn", "info", svnBaseURL).Output()
    if e1 != nil {
        return "", errors.New("Unable to execute svn info " +
            svnBaseURL)
    }
    r := bufio.NewReader(bytes.NewReader(out))
    for {
        l, _, e2 := r.ReadLine()
        if e2 == io.EOF {
            break
        }
        s := string(l)
        if strings.Contains(s, "Revision:") {
            svnRev := strings.TrimSpace(strings.Split(s, ":")[1])
            return svnRev, nil
        }
    }
    return "", errors.New("Unable to get the SVN revision number")
}

func getOldBaseSVNRev() (string, error) {
    f, e := os.Open(BaseRevFile)
    if e != nil {
        return "", errors.New(BaseRevFile + " does not exist. " +
            "You need to create this file initially!")
    }
    defer f.Close()
    r := bufio.NewReader(f)
    l, _, _ := r.ReadLine()
    return strings.TrimSpace(string(l)), nil
}

func updateBaseSVNFile(newBaseRev string) error {
    f, e := os.OpenFile(BaseRevFile, os.O_WRONLY, 0666)
    if e != nil {
        return e
    }
    defer f.Close()
    f.Write([]byte(newBaseRev))
    return nil
}

func SVNRebase(svnBaseURL string, dryRun bool) error {
    oldBaseRev, e1 := getOldBaseSVNRev()
    if e1 != nil {
        return e1
    }
    newBaseRev, e2 := getLatestBaseSVNRev(svnBaseURL)
    if e2 != nil {
        return e2
    }
    if oldBaseRev == newBaseRev {
        fmt.Println("Your repo has already had the latest revision")
        return nil
    }
    cmd := "svn"
    args := []string{"merge"}
    if dryRun {
        args = append(args, "--dry-run")
    }
    args = append(args, svnBaseURL + "@" + oldBaseRev)
    args = append(args, svnBaseURL + "@" + newBaseRev)
    fmt.Println("Command:", cmd + " " + strings.Join(args, " "))
    c := exec.Command(cmd, args...)
    c.Stdout = os.Stdout
    c.Stdin = os.Stdout
    c.Stderr = os.Stderr
    e3 := c.Run()
    if e3 != nil {
        return e3
    }
    if !dryRun {
        updateBaseSVNFile(newBaseRev)
    }
    return nil
}

func printUsage() {
    fmt.Println("Usage:", os.Args[0], "<svn_base_url> [--dry-run]")
}

func validateArgs() {
    if len(os.Args) == 1 || len(os.Args) > 3 {
        printUsage()
        os.Exit(1)
    }
    if len(os.Args) == 3 {
        if os.Args[2] != "--dry-run" {
            fmt.Println("Error: Invalid option:", os.Args[2])
            os.Exit(1)
        }
    }
}

func main() {
    validateArgs()
    baseSVNURL := os.Args[1]
    dryRun := false
    if len(os.Args) == 3 {
        dryRun = true
    }
    if e := SVNRebase(baseSVNURL, dryRun); e != nil {
        fmt.Println("Error:", e)
    }
}
Usage: ./svnrebase  [--dry-run]
svnrebase.py
#!/usr/bin/env python
import sys, subprocess, os

BASE_REV_FILE = 'base_rev.txt'

def get_latest_base_svn_rev(svn_base_url):
    p = subprocess.Popen(['svn', 'info', svn_base_url], stdout=subprocess.PIPE)
    for line in p.communicate()[0].split(os.linesep):
        if line.startswith('Revision:'):
            return line.split(":")[1].strip()
    return None

def get_old_base_svn_rev():
    if not os.path.exists(BASE_REV_FILE):
        raise Exception(BASE_REV_FILE + ' does not exist. ' + 
                        'You need to create this file initially!')
    f = open(BASE_REV_FILE, 'r')
    return f.read().strip() 

def update_base_svn_file(new_base_rev):
     f = open(BASE_REV_FILE, 'w')
     f.write(new_base_rev)
     f.close()

def svn_rebase(svn_base_url, dry_run):
    old_base_rev = get_old_base_svn_rev()
    new_base_rev = get_latest_base_svn_rev(svn_base_url)
    if old_base_rev == new_base_rev:
        print 'Your repo has already had the latest revision'
        return
    cmd = ['svn', 'merge']
    if dry_run:
        cmd.append('--dry-run')
    cmd.append(svn_base_url + '@' + old_base_rev)
    cmd.append(svn_base_url + '@' + new_base_rev)
    print 'Command:', ' '.join(cmd)
    subprocess.call(cmd)
    if not dry_run:
       update_base_svn_file(new_base_rev)

def print_usage():
    print 'Usage:', sys.argv[0], '<svn_base_url> [--dry-run]'

def validate_args():
    if len(sys.argv) == 1 or len(sys.argv) > 3:
        print_usage()
        sys.exit(1)
    if len(sys.argv) == 3:
        if sys.argv[2] != '--dry-run':
            print 'Error: Invalid option:', sys.argv[2]
            sys.exit(1)

if __name__ == '__main__':
    validate_args()
    base_svn_url = sys.argv[1]
    dry_run = True if len(sys.argv) == 3 else False
    try:
        svn_rebase(base_svn_url, dry_run)
    except Exception, e:
        print 'Error:', e
Usage: ./svnrebase.py  [--dry-run]
Initially, you need to create base_rev.txt that specifies which revision your branch was created. Subsequently, the base_rev.txt will be automatically updated by the tool.

Sunday, December 30, 2012

How to Setup JavaFX Project with Gradle

apply plugin: "java"
apply plugin: "eclipse"

version = "0.1.0"

sourceCompatibility = 1.7
targetCompatibility = 1.7

defaultTasks = ["clean", "jar"]

def javafxLib = "jfxrt.jar"

def getJavaFXPath(def javafxLib) {
    def javaHome = System.env["JAVA_HOME"]
    if (javaHome == null) {
        throw new RuntimeException("JAVA_HOME environment variable must be set")
    }
    def javafxrt = "jre" + File.separator + "lib" + File.separator + javafxLib
    return new File(javaHome, javafxrt).absolutePath
}

dependencies {
    compile files(getJavaFXPath(javafxLib))
}

jar {
    // When creating a fat JAR, make sure we exclude javafx runtime
    // from the fat JAR
    dependsOn configurations.runtime
    from {
        configurations.runtime.findAll { !it.name.contains(javafxLib) }.collect { 
            it.isDirectory() ? it : zipTree(it)
        }
    }
    from sourceSets.main.allJava

    // There's no need to explicitly specify the -classpath or -cp
    // since the classpath information is already stored in the MANIFEST.MF
    manifest {
        attributes "Main-Class": "javafxapp.JavaFXApp"
        attributes "Class-Path": getJavaFXPath(javafxLib)
    }
}
Now we can easily execute the generated JAR by calling
java -jar <jar_file.jar>

Saturday, December 29, 2012

How to Create Fully-Occupied-Space Controls in JavaFX

A simple example to create fully-occupied-space buttons in JavaFX.
package javafxapp;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBuilder;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.AnchorPaneBuilder;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.layout.VBoxBuilder;
import javafx.stage.Stage;

public class JavaFXApp extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("JavaFX App");
        
        BorderPane root = new BorderPane();
        
        root.setLeft(createNode("Left"));
        root.setRight(createNode("Right"));
        root.setCenter(createNode("Center"));
        root.setTop(createNode("Top"));
        root.setBottom(createNode("Bottom"));
        
        Scene scene = new Scene(root);
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private Node createNode(String text) {
        VBox vbox = VBoxBuilder.create()
            .spacing(5)
            .padding(new Insets(10, 10, 10, 10))
            .build();

        Button btn = ButtonBuilder.create()
            .text(text)
            .build();
        
        AnchorPane anchoredPane = AnchorPaneBuilder.create()
            .children(btn)
            .style("-fx-border-style: solid;"
                + "-fx-border-width: 1;"
                + "-fx-border-color: black")
                .build();
        
        AnchorPane.setBottomAnchor(btn, 5.0);
        AnchorPane.setRightAnchor(btn, 5.0);
        AnchorPane.setTopAnchor(btn, 5.0);
        AnchorPane.setLeftAnchor(btn, 5.0);
        
        VBox.setVgrow(anchoredPane, Priority.ALWAYS);
        vbox.getChildren().addAll(anchoredPane);
        
        return vbox;
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}

Thursday, December 27, 2012

Getting Started with Gradle Multiproject

Project structure:
.
├── api
│   └── src
│       └── main
│           └── java
│               └── api
│                   └── API.java
├── build.gradle
├── cli
│   └── src
│       └── main
│           └── java
│               └── cli
│                   └── CLI.java
├── core
│   └── src
│       └── main
│           └── java
│               └── core
│                   └── Core.java
├── gui
│   └── src
│       └── main
│           └── java
│               └── gui
│                   └── GUI.java
└── settings.gradle
In this project, core depends on api, cli depends on core, and gui depends on core.

settings.gradle

include ":api", ":core", ":gui", ":cli"
build.gradle
def distDir = "dist"

defaultTasks = ["clean", "jar", "dist"]

subprojects {
    apply plugin: "java"
    apply plugin: "eclipse"
    sourceCompatibility = 1.7
    targetCompatibility = 1.7
    version = "0.1.0"
}

project(":core") {
    dependencies {
        compile project(":api")
    }
}

project(":gui") {
    dependencies {
        compile project(":core")
    }
}

project(":cli") {
    dependencies {
        compile project(":core")
    }
}

task clean << {
    delete distDir
}

task dist << {
    def d = mkdir(distDir)
    subprojects.each { subproject ->
        subproject.tasks.jar.each { archiveTask ->
            copy {
                from archiveTask.archivePath
                into d
            }
        }
    }
}
The build.gradle shown above has a dist task that will pull all the generated jars into dist directory.

How to Include Source Files into JAR in Gradle

Project structure:
├── build.gradle
└── src
    └── main
        └── java
            └── test
                └── Test.java
build.gradle
apply plugin: "java"

defaultTasks = ["clean", "jar"]

jar {
    from sourceSets.main.allJava
}

Monday, December 17, 2012

How to Solve Cutting Rod Problem

This solution uses memoization.
#!/usr/bin/env python

def find_max(list_tuples):
    if len(list_tuples) == 0:
        return None
    maximum = list_tuples[0]
    for i in xrange(1, len(list_tuples)):
        if maximum[0] < list_tuples[i][0]:
            maximum = list_tuples[i]
    return maximum

memo = {}
def cutting_rod(prices, ncuts):
    if ncuts == 0:
        return (0, [])
    if ncuts in memo:
        return memo[ncuts]
    tmp = []
    for i in xrange(0, ncuts):
        r = cutting_rod(prices, ncuts-i-1)
        new_list = list(r[1])
        new_list.append(i)
        value = prices[i] + r[0]
        tmp.append((value, new_list))
    result = find_max(tmp)
    memo[ncuts] = result
    return result

if __name__ == '__main__':
    p = [2, 5, 6, 7]
    r = cutting_rod(p, len(p))
    print "Prices:", p
    print "Optimal value:", r[0]
    print "Indices:", r[1]
    print "Cuts:", [p[x] for x in r[1]]
Output:
Prices: [2, 5, 6, 7]
Optimal value: 10
Indices: [1, 1]
Cuts: [5, 5]

Thursday, December 13, 2012

How to Solve Producer Consumer Problem in Java

Change the preJava5 flag to true if you are running Java 4 and below. Change preJava5 flag to true if you are running Java 5 and above.
import java.util.LinkedList;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

public class ProducerConsumer {
    public static final int MAX_SIZE = 5;
    public static final int SLEEP_TIME = 5 * 100;
    
    public static class PreJava5 {
        public static class Consumer {
            private Random random = new Random();
            
            public void consume(LinkedList<Integer> queue) {
                while (true) {
                    try {
                        synchronized (queue) {
                            if (queue.isEmpty()) {
                                System.out.println("Queue is empty");
                                queue.wait();
                            } else {
                                System.out.println("Consuming something, size="
                                    + queue.size());
                                queue.removeFirst();
                                queue.notifyAll();
                            }
                        }
                        // to simulate performing some busy tasks
                        Thread.sleep(random.nextInt(SLEEP_TIME));
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }
        
        public static class Producer {
            private Random random = new Random();
            
            public void produce(LinkedList<Integer> queue) {
                while (true) {
                    try {
                        synchronized (queue) {
                            if (queue.size() == MAX_SIZE) {
                                System.out.println("Queue is full");
                                queue.wait();
                            } else {
                                System.out.println("Producing something, size="
                                    + queue.size());
                                queue.addLast(0);
                                queue.notifyAll();
                            }
                        }
                        // to simulate performing some busy tasks
                        Thread.sleep(random.nextInt(SLEEP_TIME));
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }
    }
    
    public static class Java5AndAbove {
        public static class Consumer {
            private Random random = new Random();
            
            public void consume(BlockingQueue<Integer> queue) {
                while (true) {
                    try {
                        System.out.println("Consuming something, size="
                            + queue.size());
                        // will block if the queue is empty
                        queue.take();
                        // to simulate performing some busy tasks
                        Thread.sleep(random.nextInt(SLEEP_TIME));
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }
        
        public static class Producer {
            private Random random = new Random();
            
            public void produce(BlockingQueue<Integer> queue) {
                while (true) {
                    try {
                        System.out.println("Producing something, size="
                            + queue.size());
                        // will block if the queue is full
                        queue.put(0);
                        // to simulate performing some busy tasks
                        Thread.sleep(random.nextInt(SLEEP_TIME));
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }
    }
    
    public static void main(String[] args) {
        boolean preJava5 = false;
        
        if (preJava5) {
            final LinkedList<Integer> queue = new LinkedList<>();
            final PreJava5.Producer producer = new PreJava5.Producer();
            final PreJava5.Consumer consumer = new PreJava5.Consumer();
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    producer.produce(queue);
                }
            });
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    consumer.consume(queue);
                }
            });
            t1.start();
            t2.start();
        } else {
            final BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(MAX_SIZE);
            final Java5AndAbove.Producer producer = new Java5AndAbove.Producer();
            final Java5AndAbove.Consumer consumer = new Java5AndAbove.Consumer();
            
            ExecutorService es1 = Executors.newSingleThreadExecutor();
            ExecutorService es2 = Executors.newSingleThreadExecutor();
            
            es1.execute(new Runnable() {
                @Override
                public void run() {
                    producer.produce(queue);
                }
            });
            
            es2.execute(new Runnable() {
                @Override
                public void run() {
                    consumer.consume(queue);
                }
            });
        }
    }
}