JAW Speak

Jonathan Andrew Wolter

Archive for September, 2009

Can you spot Java Puzzler in this snippet?

without comments

Reading time: < 1 minute

I ran across this last week. It was marvelous when we saw what was happening, but entirely puzzling at first.

Boolean someFlag = complicatedLogicToFigureOutFlag();
Person person = new Person(someFlag);

Any signs for concern? How about if Person’s constructor is:

Person(boolean someFlag) {
    this.someFlag = someFlag;
}

Any warning signs?

Will it compile?

Read more for the full puzzler.

Read the rest of this entry »

Written by Jonathan

September 30th, 2009 at 2:51 pm

Posted in code, java, puzzle

How to do 3-way merges with Subversion and Kdiff3

with 4 comments

Reading time: 4 – 7 minutes

I do not endorse branch based development. I prefer trunk based development. Specifically I like what my colleague Paul calls Branch By Abstraction, coined by Stacy Curl, and recently mentioned by Martin Fowler (All one time ThoughtWorkers, and 2 currently).

If you’re stuck with merging though, 3-way merges make it much easier. Doing it with subversion is easy. Instructions are for Linux.

  1. apt-get or yum install kdiff3.
  2. Edit your /etc/subversion/config and fin the line with diff3-cmd, set it to: diff3-cmd=/usr/local/bin/svndiff.sh
  3. Next, create the file /usr/local/bin/svndiff.sh. See below for the script you’ll want to enter in it.

Now when you get a merge conflict you will choose M and merge will open in kdiff3. On the left is the base revision, in the middle is your working copy, and on the right the incoming change. This is a little more to look at, but it is invaluable when dealing with merges. I wouldn’t go back to 2 way diff ever again.

#!/bin/bash
 
# tim/paul: this is a copy of the file located at http://www.yolinux.com/TUTORIALS/src/svndiffwrapper.txt
#    modified to do a non-conflicting merge automatically. see #HERE#
 
# Return an errorcode of 0 on successful merge, 1 if unresolved conflicts
# remain in the result.  Any other errorcode will be treated as fatal.
# Author: Michael Bradley
 
#NOTE: all output must be redirected to stderr with "1&gt;&amp;2" as all stdout output is written to the output file
 
VDIFF3="kdiff3"
DIFF3="diff3"
DIFF="kdiff3"  
 
promptUser ()
{
    read answer
    case "${answer}" in
 
        "M"         )
        echo "" 1&gt;&amp;2
        echo "Attempting to merge ${baseFileName} with ${DIFF}" 1&gt;&amp;2
        $VDIFF3 $older $mine $theirs --L1 $labelOlder --L2 $labelMine --L3 $labelTheirs -o $output 1&gt;&amp;2
        bLoop=1
        if [ -f $output ]; then
            if [ -s $output ]; then
                #output succesfully written
                bLoop=0
            fi
        fi
        if [ $bLoop = 0 ]; then
            cat $output
            rm -f $output
            exit 0
        else
            echo "Merge failed, try again" 1&gt;&amp;2
        fi
 
        ;;
 
        "m"         )
        echo "" 1&gt;&amp;2
        echo "Attempting to auto-merge ${baseFileName}" 1&gt;&amp;2
        diff3 -L $labelMine -L $labelOlder -L $labelTheirs -Em $mine $older $theirs &gt; $output
        if [ $? = 1 ]; then
            #Can't auto merge
            rm -f $output
            $VDIFF3 $older $mine $theirs --L1 $labelOlder --L2 $labelMine --L3 $labelTheirs -o $output --auto 1&gt;&amp;2
            bLoop=1
            if [ -f $output ]; then
                if [ -s $output ]; then
                    #output succesfully written
                    bLoop=0
                fi
            fi
            if [ $bLoop = 0 ]; then
                cat $output
                rm -f $output
                exit 0
            else
                echo "Merge failed, try again" 1&gt;&amp;2
            fi
        else
            #We can automerge, and we already did it
            cat $output
            rm -f $output
            exit 0
        fi
        ;;
 
        "diff3" | "Diff3" | "DIFF3"  )
        echo "" 1&gt;&amp;2
        echo "Diffing..." 1&gt;&amp;2
        $VDIFF3 $older $mine $theirs --L1 $labelOlder --L2 $labelMine --L3 $labelTheirs 1&gt;&amp;2
        ;;
 
        "diff" | "Diff" | "DIFF"  )
        echo "" 1&gt;&amp;2
        echo "Diffing..." 1&gt;&amp;2
        $DIFF $mine $theirs -L $labelMine -L $labelTheirs 1&gt;&amp;2
        ;;
 
        "A" | "a"   )
        echo "" 1&gt;&amp;2
        echo "Accepting remote version of file..." 1&gt;&amp;2
        cat ${theirs}
        exit 0
        ;;
 
        "I" | "i"   )
        echo "" 1&gt;&amp;2
        echo "Keeping local modifications..." 1&gt;&amp;2
        cat ${mine}
        exit 0
        ;;
 
        "R" | "r"   )
        echo "" 1&gt;&amp;2
        echo "Reverting to base..." 1&gt;&amp;2
        cat ${older}
        exit 0
        ;;
 
        "D" | "d"   )
        echo "" 1&gt;&amp;2
        echo "Runnig diff3..." 1&gt;&amp;2
        diff3 -L $labelMine -L $labelOlder -L $labelTheirs -Em $mine $older $theirs
        #Exit with return vaule of the diff3 (to write out files if necessary)
        exit $?
        ;;
 
        "S" | "s"   )
        echo "" 1&gt;&amp;2
        echo "Saving for later..." 1&gt;&amp;2
        cat ${mine}
        #Exit with return vaule of 1 to force writting of files
        exit 1
        ;;
 
        "Fail" | "fail" | "FAIL"   )
        echo "" 1&gt;&amp;2
        echo "Failing..." 1&gt;&amp;2
        exit 2
        ;;
 
        "H" | "h"   )
        echo "" 1&gt;&amp;2
        echo "USAGE OPTIONS:" 1&gt;&amp;2
        echo "  [A]ccept    Accept $labelTheirs and throw out local modifications" 1&gt;&amp;2
        echo "  [D]efault   Use diff3 to merge files (same behavior as vanilla SVN)" 1&gt;&amp;2
        echo "  [Fail]      Kills the command (not suggested)" 1&gt;&amp;2
        echo "  [H]elp      Print this message" 1&gt;&amp;2
        echo "  [I]gnore    Keep your locally modified version as is" 1&gt;&amp;2
        echo "  [M]erge     Manually merge using ${VDIFF3}" 1&gt;&amp;2
        echo "  [m]erge     Same as "M" but attempts to automerge if possible" 1&gt;&amp;2
        echo "  [R]evert    Revert to base version (${labelOlder})" 1&gt;&amp;2
        echo "  [S]ave      Same as 'I' but writes out rold, rnew, and rmine files to deal with later" 1&gt;&amp;2
        echo "  [diff]      Type 'diff' to diff versions $labelMine and $labelTheirsthe before making a descision" 1&gt;&amp;2
        echo "  [diff3]     Type 'diff3' to diff all three versions before making a descision" 1&gt;&amp;2
        echo "" 1&gt;&amp;2
        ;;
 
        *   )
        echo "'${answer}' is not an option, try again." 1&gt;&amp;2
        ;;
    esac
}
 
if [ -z $2 ]
then
    echo ERROR: This script expects to be called by subversion
    exit 1
fi
 
if [ $2 = "-m" ]
then
    #Setup vars
    labelMine=${4}
    labelOlder=${6}
    labelTheirs=${8}
    mine=${9}
    older=${10}
    theirs=${11}
    output=${9}.svnDiff3TempOutput
    baseFileName=`echo $mine | sed -e "s/.tmp$//"`
 
#HERE#
    diff3 -L $labelMine -L $labelOlder -L $labelTheirs -Em $mine $older $theirs &gt; $output
    if [ $? = 1 ]; then
        #Can't auto merge
        #Prompt user for direction
        while [ 1 ]
        do
            echo "" 1&gt;&amp;2
            echo "${baseFileName} requires merging." 1&gt;&amp;2
            echo "" 1&gt;&amp;2
            echo "What would you like to do?" 1&gt;&amp;2
            echo "[M]erge [A]ccept [I]gnore [R]evert [D]efault [H]elp" 1&gt;&amp;2
            promptUser
        done
    else
        #We can automerge, and we already did it
        cat $output
        rm -f $output
        exit 0
    fi
else
    L="-L"         #Argument option for left label
    R="-L"         #Argument option for right label
    label1=$3       #Left label
    label2=$5       #Right label
    file1=$6        #Left file
    file2=$7        #Right file
 
    $DIFF $file1 $file2 $L "$label1" $L "$label2" &amp;
    #$DIFF $file1 $file2 &amp;
    #wait for the command to finish
    wait
fi
exit 0

Note: I also posted this to a gist on github: svndiff.sh.

Written by Jonathan

September 17th, 2009 at 8:49 pm

Ruby Script to Organize Mp3’s based on ID3 Genre Tag

without comments

Reading time: 2 – 4 minutes

I had one gigantic directory of all my tagged and organized mp3 files. Problem is it was too big to use. This bloated my library and I have since not been able to fit my music on my laptop. I needed to manipulate mp3 files by genre and extract them out of this single directory to create smaller libraries. I spent all of about two minutes looking for a program to do this before deciding to write a script. Truthfully, it was worse: once upon a time I over-enthusiastically downloaded StepMania and 493 DDR games/songs. And then, I added all the songs into my music library. It’s a great party game, but not the kind of music I want to listen to.

Many implementations exist for reading ID3 tags. I first tried ruby-mp3info, however it didn’t read my custom genre (‘DDR’) so then I moved to id3lib-ruby which uses the c++ id3lib library.

This worked like a charm. I ran the script over all my directories and built up a list of the directories.

#!/usr/bin/env ruby
# find_music.sh
require "rubygems"
require 'id3lib'
require 'find'
require 'set'
 
ddr_files = []
ddr_dirs = Set.new
 
search_dir = '~/media/music/music_categorized'
 
Find.find(search_dir) do |file|
  next if file !~ /.*mp3$/
  mp3 = ID3Lib::Tag.new(file)
  next if mp3.genre != 'DDR'
  ddr_dirs << File.dirname(file)
  ddr_files << file   puts "%s, %s --> AT: %s" % [mp3.genre, mp3.album, file]
end
 
File.open('result-ddr-files.txt', 'w') do |f|
  f.write(ddr_files.join("\n"))
end
 
File.open('result-ddr-dirs.txt', 'w') do |f|
  ddr_dirs.each { |d| f.write("%s\n" % d)}
end

Next I reviewed the two output files, then ran the file result-ddr-dirs.txt in as an argument into this next script. That removed almost a gig of music from my library.

#!/usr/bin/env ruby
 
if (ARGV.length != 1)
  puts "Usage: #{__FILE__} input_file"
  exit(1)
end
 
destination="/home/jwolter/media/music/music_ddr_questionable_value/"
 
File.foreach(ARGV[0]) do |line|
  next if line.strip == ""
  cmd =  "mv \"#{line.strip}\" \"#{destination}\""
  #puts cmd
  `#{cmd}`
end

Bonus: In the process searching for this, I ran into the ID3 Tags RubyQuiz.

One of the nicest benefits of being a software engineer is I avoid doing boring manual tasks on my computer. Writing a script is more fun, and faster. I’ve got many scripts to automate file manipulation, online banking, and more. What bit of your automation scripts do you think is the most helpful?

Written by Jonathan

September 5th, 2009 at 7:06 pm

Posted in automation, music