<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7729215530786039380</id><updated>2024-09-19T10:27:35.755-07:00</updated><category term="Android"/><category term="java"/><title type='text'>Everything I See</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>15</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7729215530786039380.post-3878301148645811779</id><published>2011-03-08T13:29:00.000-08:00</published><updated>2011-03-08T13:50:14.011-08:00</updated><title type='text'>Removing and Subsetting Repositories in git versus svn</title><content type='html'>If ever you decide to take a bit repository apart and make smaller ones, do it in git.  In subversion, you first make a dump of the whole thing, then you create a new repository and use svndumpfilter to filter out what you want to add and add it with svnadmin:&lt;br /&gt;&lt;pre class=&quot;source-code&quot;&gt;&lt;code&gt;svnadmin dump svnroot/ &gt; svn.dump&lt;br /&gt;svnadmin create svnroot-fixed&lt;br /&gt;cd svnroot-fixed&lt;br /&gt;svndumpfilter include what-to-include &lt; ../svn.dump | svnadmin load ./ &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This works alright, but it&#39;s pretty slow and for any kind of complex subsetting/removal, you&#39;re going to have to do multiple passes.  In the end, since I was transitioning things to git, I gave up after a little while.&lt;br /&gt;&lt;br /&gt;Git, although it doesn&#39;t make the process entirely trivial, is a bit better.  The workflow is about the same, but it&#39;s faster and more flexible.  You clone the directory and then use &#39;git filter-branch&#39; to do what you want to do.  For example to remove a directory (or specific files):&lt;br /&gt;&lt;pre class=&quot;source-code&quot;&gt;&lt;code&gt;git filter-branch --index-filter &quot;git rm -rf --cached --ignore-unmatch $files&quot; HEAD&lt;/code&gt;&lt;/pre&gt;Note that this won&#39;t yet delete the files from disk, since git keeps around a cache of the files.  To totally delete them forever, David Underhill made a nice little &lt;a href=&quot;http://dound.com/2009/04/git-forever-remove-files-or-folders-from-history/&quot;&gt;script&lt;/a&gt;.&lt;div&gt;&lt;br /&gt;To make a copy of a repository only containing a directory:&lt;pre class=&quot;source-code&quot;&gt;&lt;code&gt;git clone $PARENT $NEW_REPO&lt;br /&gt;cd $NEW_REPO&lt;br /&gt;git filter-branch --subdirectory-filter $CHILD -- --all&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;And in case it wasn&#39;t obvious from the variable names, I made a quick script to promote all the subdirectories of a repository to their own repository:&lt;br /&gt;&lt;pre class=&quot;source-code&quot;&gt;&lt;code&gt;#!/bin/bash&lt;br /&gt;WRKDIR=`pwd`&lt;br /&gt;PARENT=$1&lt;br /&gt;echo $PARENT&lt;br /&gt;&lt;br /&gt;cd $PARENT&lt;br /&gt;CHILDREN=`git ls-tree --name-only HEAD`&lt;br /&gt;&lt;br /&gt;for CHILD in $CHILDREN; do&lt;br /&gt;cd $WRKDIR&lt;br /&gt;NEW_REPO=&quot;$PARENT-$CHILD&quot;&lt;br /&gt;git clone $PARENT $NEW_REPO&lt;br /&gt;cd $NEW_REPO&lt;br /&gt;git filter-branch --subdirectory-filter $CHILD -- --all&lt;br /&gt;done&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;That&#39;s all for now.&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/3878301148645811779/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfolson.blogspot.com/2011/03/removing-and-subsetting-repositories-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/3878301148645811779'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/3878301148645811779'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/2011/03/removing-and-subsetting-repositories-in.html' title='Removing and Subsetting Repositories in git versus svn'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7729215530786039380.post-3950702266253195748</id><published>2011-03-08T13:17:00.001-08:00</published><updated>2011-03-08T13:28:54.434-08:00</updated><title type='text'>Gotcha git</title><content type='html'>So hopefully mentioning them here will help me remember not to do a couple of things.  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;First&lt;a href=&quot;http://hans.fugal.net/blog/2008/11/10/git-push-is-worse-than-worthless/&quot;&gt; DO NOT, EVER, push to a non-bare repository&lt;/a&gt;, unless you will  immediately &#39;git reset HEAD&#39;.  When you push to a non-bare repository, git for some reason inserts the pushed files &quot;before&quot; the existing working directory.  This means that current working directory, without your pushed changes, is seen as having changes after your commit.  So if you commit the working directory you pushed to, &lt;b&gt;you effectively revert everything you just pushed!!!&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Second, be &lt;a href=&quot;http://book.git-scm.com/5_submodules.html&quot;&gt;very, very careful with submodules&lt;/a&gt;.  Every commit to a submodule has to be committed both the the submodule AND to the parent module!  If you to do both, everyone dies.  Well, maybe not, but you all other clones of the parent directory will be broken until you go pack and do both commits.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Even worse are the terrible terrible consequences of &#39;git add submodule/&#39; with the trailing slash included.  Rather than add the changes in the submodule to the list of changes to commit, this will &lt;b&gt;remove the submodule from the repository and re-add all of the individual files contained in the submodule&lt;/b&gt;.  I&#39;m not quite sure why this was determined to be the desired behavior, but whatever.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Anyhow, I thought I&#39;d also throw up a couple of &lt;a href=&quot;http://www.sourcemage.org/Git_Guide&quot;&gt;handy&lt;/a&gt; &lt;a href=&quot;https://git.wiki.kernel.org/index.php/GitFaq&quot;&gt;references&lt;/a&gt; for generic git stuff.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/3950702266253195748/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfolson.blogspot.com/2011/03/gotcha-git.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/3950702266253195748'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/3950702266253195748'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/2011/03/gotcha-git.html' title='Gotcha git'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7729215530786039380.post-7632402643980151792</id><published>2011-03-08T12:19:00.001-08:00</published><updated>2011-03-08T12:47:26.886-08:00</updated><title type='text'>Managing user files with git</title><content type='html'>So now that I&#39;m moving some of my projects over to git, I decided I should better automate syncing of user files across machines.  Specifically, I want (some of) my $HOME/.* files and (some of) my $HOME/bin/* files version controlled and sym-linked to where they normally are.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So I made a repository with a &lt;i&gt;conf&lt;/i&gt; directory and &lt;i&gt;bin&lt;/i&gt; directory and symlinked the files to their respective $HOME and $HOME/bin locations.  But then I wondered if I could do something a bit better.  Specifically, I was thinking about what would happen when I added a new file to the repository.  Inevitably, things would be fine, but on at least one computer, I&#39;d forget to symlink the new files and I&#39;d be confused for a bit.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In order to prevent what is for all practical purposes, a non-problem, I spent &lt;too_much_time&gt; mucking around with git-hooks and git&#39;s config options, I came up with a solution.   Beginning with a simpler solution that merely asks which files to replace and symlink when its run, I came up with a more elegant (stupid) solution:&lt;/too_much_time&gt;&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: monospace; font-size: 13px; white-space: pre; &quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: monospace; font-size: 13px; white-space: pre; &quot;&gt;#! /bin/bash&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;pre class=&quot;source-code&quot;&gt;&lt;code&gt;# Move all existing files for which a versioned file exists to a local branch&lt;br /&gt;# and symlink to the versioned location. But make sure to stash the current&lt;br /&gt;# branch, commit the local branch after you&#39;re done and then move back to the&lt;br /&gt;# original branch and pop the stash back to where it was&lt;br /&gt;#&lt;br /&gt;function move_to_branch () {&lt;br /&gt;VERSIONED_DIR=$1&lt;br /&gt;TARGET_DIR=$2&lt;br /&gt;cd $VERSIONED_DIR&lt;br /&gt;FILES=`git ls-tree --name-only HEAD`&lt;br /&gt;for FILE in $FILES; do&lt;br /&gt;# if isn&#39;t already a symlink that points to the the versioned copy&lt;br /&gt; if [ ! -h $TARGET_DIR/$FILE ] ||&lt;br /&gt;   [ ! `readlink -f $VERSIONED_DIR/$FILE` ==&lt;br /&gt;     `readlink -f $TARGET_DIR/$FILE` ]; then&lt;br /&gt;#if it exists, remove the versioned copy in the local branch and replace with local file&lt;br /&gt; if [ -e $TARGET_DIR/$FILE ]; then&lt;br /&gt; rm -rf $VERSIONED_DIR/$FILE&lt;br /&gt; mv $TARGET_DIR/$FILE $VERSIONED_DIR/$FILE&lt;br /&gt; fi&lt;br /&gt; ln --force --symbolic $VERSIONED_DIR/$FILE $TARGET_DIR/$FILE&lt;br /&gt; fi&lt;br /&gt;done&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;cd `dirname $0`/../&lt;br /&gt;#echo $0&lt;br /&gt;ROOTDIR=`pwd`&lt;br /&gt;&lt;br /&gt;if [ -z `git branch | grep local` ]; then git branch local; fi&lt;br /&gt;&lt;br /&gt;CURRENT_BRANCH=`$ROOTDIR/scripts/get_current_branch.sh`&lt;br /&gt;git stash&lt;br /&gt;git checkout local&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;move_to_branch $ROOTDIR/conf $HOME&lt;br /&gt;if [ ! -e &quot;$HOME/bin&quot; ]; then mkdir $HOME/bin; fi&lt;br /&gt;move_to_branch $ROOTDIR/bin $HOME/bin&lt;br /&gt;&lt;br /&gt;git commit -a -m &quot;Backing up local copy of config files&quot;&lt;br /&gt;git checkout $CURRENT_BRANCH&lt;br /&gt;git stash pop&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;This script goes through the file that are version controlled in the conf or bin directories, and for each one, replaces the current file with a symlink to the version-controlled file.  However, it also places a copy of the original file (if there was one) in a branch called local.  Now of course, to avoid horribleness, it also records the current branch before stashing it, creates the local branch if it doesn&#39;t exist, and the returns to the original branch and pops the stash.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Why?  It&#39;s a perfectly reasonable question, but what this allows you to do, is have no user interaction, which allows you to call it from post-merge:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;pre class=&quot;source-code&quot;&gt;&lt;code&gt;$ cat scripts/post-action&lt;br /&gt;#!/bin/sh&lt;br /&gt;./scripts/link_files_branch.sh&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now, every time I pull in a new version, this script will automatically symlink any new files and commit a copy of the original (non-versioned) file in a &lt;i&gt;local&lt;/i&gt; branch.   This way, I can keep an up-to-date version of everything in &lt;i&gt;master&lt;/i&gt; and any local changes in &lt;i&gt;local&lt;/i&gt;.  &quot;You fool!&quot; you might say, wondering what will happen when I eventually push/pull from the &lt;i&gt;local&lt;/i&gt; branch.  Well, yeah, that&#39;ll be annoying, but I can minimize that by setting default branches to push/pull to/from:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;pre class=&quot;source-code&quot;&gt;&lt;code&gt;$ cat scripts/setup_repository.sh&lt;br /&gt;#!/bin/bash&lt;br /&gt;&lt;br /&gt;cd `dirname $0`&lt;br /&gt;&lt;br /&gt;git config remote.origin.pull master&lt;br /&gt;git config remote.origin.push master&lt;br /&gt;&lt;br /&gt;#echo $0&lt;br /&gt;SCRIPT_DIR=`pwd`&lt;br /&gt;ROOT_DIR=$SCRIPT_DIR/../&lt;br /&gt;cp $SCRIPT_DIR/post-action $ROOT_DIR.git/hooks/post-merge&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Well, now that I&#39;ve got that 5-minute possible future confusion averted I can go back to work.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/7632402643980151792/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfolson.blogspot.com/2011/03/managing-user-files-with-git.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/7632402643980151792'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/7632402643980151792'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/2011/03/managing-user-files-with-git.html' title='Managing user files with git'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7729215530786039380.post-7085417989000237578</id><published>2011-03-08T11:26:00.000-08:00</published><updated>2011-03-08T12:19:02.547-08:00</updated><title type='text'>Playing with Git</title><content type='html'>So I finally got fed up with subversion for managing my personal research projects and documents, etc. and have been playing around with git. So far, I really enjoy it for a few reasons:&lt;div&gt;&lt;ul&gt;&lt;li&gt;I commit more&lt;/li&gt;&lt;li&gt;I branch more&lt;/li&gt;&lt;li&gt;I divide projects logically&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Committing changes really is easier, so you do it more&lt;/b&gt;.  With every commit pushing changes out to a remote repository, and possibly forcing you to update first, there&#39;s just enough overhead that I ended up not doing it as much as I should.  With git, everything&#39;s local, so it&#39;s faster and easier.  That said, it means that I&#39;m really only committing for myself.  In order to commit changes to other people or bring in changes from other people I have to either pull or push (to a bare repository!).  However, for files that are mainly personal, I tend to work for a longer period on a single machine and then occasionally shifting to a different machine either to run large jobs or work not at my desk.  So for me, a major point of (much) version control is just versioning stuff for personal consumption, not for coordinating with others.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Branching/Merging is easier, so you do it more&lt;/b&gt;.  In git, there&#39;s no real distinction between pulling from a remote repository (e.g. for collaboration) and pulling in from another branch. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Using examples from the excellent &lt;a href=&quot;http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html&quot;&gt;gittutorial&lt;/a&gt;, pulling from a remote repository, ala &lt;i&gt;svn update&lt;/i&gt;, is:&lt;/div&gt;&lt;div&gt;&lt;pre class=&quot;source-code&quot;&gt;&lt;code&gt;git pull /home/bob/myrepo&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Or for the cautious who want to put the update from Bob&#39;s &lt;i&gt;master&lt;/i&gt; into a new &lt;i&gt;bob-incoming &lt;/i&gt;branch:&lt;/div&gt;&lt;div&gt;&lt;pre class=&quot;source-code&quot;&gt;&lt;code&gt;git fetch /home/bob/myrepo master:bob-incoming&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In contrast, merging the &lt;i&gt;experimental&lt;/i&gt; branch into the current one:&lt;/div&gt;&lt;div&gt;&lt;pre class=&quot;source-code&quot;&gt;&lt;code&gt;git pull . experimental&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;This means that branching and merging uses utilities and skills that you already need to know if you&#39;re using git at all!  Since you&#39;re already familiar with the tools, branching and merging in the traditional sense become much less intimidating.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;My repositories now make sense&lt;/b&gt;.  Because I&#39;m lazy, I&#39;ll tend to set things up in the way that seems easiest, not in the way that makes sense.  Since svn makes it easy to checkout just part of a repository, I had basically&lt;i&gt; everything I&#39;ve done in the past 5 years &lt;/i&gt;in one repository.  This meant coursework, papers (that I wrote), papers (that I read), code, scripts, etc. all went to one svn repository.  This was not a good decision, and was ultimately part of why I left subversion.  I had munged up something in part of the repository and didn&#39;t really know how to fixing other than exporting everything to a new repository.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In contrast, git does not allow you to checkout part of a repository, so you are required to split it up into the subcomponents that make sense.  But if that was the end of the story, I&#39;d be complaining about git instead of lauding it because it&#39;d be a pain to have to manually checkout each repository for large projects.  However, as pointed out in the &lt;a href=&quot;http://book.git-scm.com/5_submodules.html&quot;&gt;git community book&lt;/a&gt;,  submodules make this unnecessary. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For some modules, a, b, c, d that you have:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;pre class=&quot;source-code&quot;&gt;&lt;code&gt;$ mkdir ~/git&lt;br /&gt;$ cd ~/git&lt;br /&gt;$ for i in a b c d&lt;br /&gt;do&lt;br /&gt;  mkdir $i&lt;br /&gt;  cd $i&lt;br /&gt;  git init&lt;br /&gt;  echo &quot;module $i&quot; &amp;gt; $i.txt&lt;br /&gt;  git add $i.txt&lt;br /&gt;  git commit -m &quot;Initial commit, submodule $i&quot;&lt;br /&gt;  cd ..&lt;br /&gt;done&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You just add them as submodules to the logical container:&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;source-code&quot;&gt;&lt;code&gt;$ mkdir super&lt;br /&gt;$ cd super&lt;br /&gt;$ git init&lt;br /&gt;$ for i in a b c d&lt;br /&gt;do&lt;br /&gt;  git submodule add ~/git/$i $i&lt;br /&gt;done&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;div&gt;Unfortunately, cloning, updating and committing of submodules gets a bit more complicated.  Rather than duplicate information that&#39;s better described (and maintained) elsewhere, I&#39;ll just say to look at the &lt;a href=&quot;http://book.git-scm.com/5_submodules.html&quot;&gt;git community book&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Anyway, I&#39;m pretty pleased with git so far, at least for personal stuff.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/7085417989000237578/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfolson.blogspot.com/2011/03/playing-with-git.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/7085417989000237578'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/7085417989000237578'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/2011/03/playing-with-git.html' title='Playing with Git'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7729215530786039380.post-199935134017856083</id><published>2011-03-06T22:39:00.001-08:00</published><updated>2011-03-06T23:10:19.157-08:00</updated><title type='text'>Automated Generation of rJava Interfaces with Annotations</title><content type='html'>So I&#39;ve recently decided to create an R interface to one of my Java projects to streamline analysis and ultimately, to make it more usable for other people.  As pretty much the only option for running Java code in R, the rJava package makes things pretty simple.  You just initialize the jvm with your required jar files and then use .jnew and .jcall to construct objects and run methods.  I found the rJava &lt;a href=&quot;http://cran.r-project.org/web/packages/helloJavaWorld/vignettes/helloJavaWorld.pdf&quot;&gt;vignettes&lt;/a&gt; quite helpful and the &lt;a href=&quot;http://www.rforge.net/doc/packages/rJava/00Index.html&quot;&gt;package documentation&lt;/a&gt; has more detail when you need it.&lt;br /&gt;&lt;br /&gt;However, the API for my Java code is in a bit of flux right now and for at least the near future., so I didn&#39;t want to put in too much effort that would be lost as soon as the Java code changes.  But I also wanted to start to get a sense as to how things would work going between R and Java because that could influence some of my design decisions (e.g. type inference, casting, etc.).  In particular, I&#39;ve been having a hard time deciding how much I should use generics.  They&#39;re handy for the &quot;type-checking&quot; but a pain in the butt in terms of verbosity.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Anyway, rJava seemed nice and straightforward, but I know my java code would change, so I got a crazy idea in my head.  What if I could use Java&#39;s user-defined &lt;a href=&quot;http://download.oracle.com/javase/1.5.0/docs/guide/apt/GettingStarted.html&quot;&gt;annotations&lt;/a&gt; to markup the constructors and methods I wanted exposed in R and then use the &lt;i&gt;apt &lt;/i&gt;annotation processing tool to convert those annotations into R code?  Well, it turns out, you can, and it only distracted me from my research for about an afternoon.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The actual definition of your annotation is quite simple:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;pre style=&quot;font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%&quot;&gt;&lt;code&gt;import java.lang.annotation.ElementType;&lt;br /&gt;import java.lang.annotation.Retention;&lt;br /&gt;import java.lang.annotation.RetentionPolicy;&lt;br /&gt;import java.lang.annotation.Target;&lt;br /&gt;&lt;br /&gt;@Retention(RetentionPolicy.SOURCE)&lt;br /&gt;@Target( { ElementType.METHOD, ElementType.CONSTRUCTOR })&lt;br /&gt;public @interface AnnotateR {&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And using your new annotation is just as easy:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;pre style=&quot;font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%&quot;&gt;&lt;code&gt;public class HelloJavaWorld {&lt;br /&gt;String message = &quot;Hello Java World!&quot;;&lt;br /&gt;&lt;br /&gt;public HelloJavaWorld() {&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Create a simple hello world message from Java using rJava&lt;br /&gt; *&lt;br /&gt; * @param message&lt;br /&gt; *            the message to be sent&lt;br /&gt; */&lt;br /&gt;@AnnotateR&lt;br /&gt;public HelloJavaWorld(String message) {&lt;br /&gt;    if (message != null) {&lt;br /&gt;        this.message = message;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@AnnotateR&lt;br /&gt;public String sayHello() {&lt;br /&gt;    String result = new String(&quot;Hello Java World!&quot;);&lt;br /&gt;    return result;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@AnnotateR&lt;br /&gt;public void sayVoid() {}&lt;br /&gt;&lt;br /&gt;@AnnotateR&lt;br /&gt;public int[] sayArray() {&lt;br /&gt;    return new int[] { 1, 2, 3 };&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now in order to do anything useful with those nice new annotations, you have to define a Factory class:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;pre style=&quot;font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%&quot;&gt;&lt;code&gt;public class AnnotateRJavaFactory implements AnnotationProcessorFactory {&lt;br /&gt; // Process only the R annotations&lt;br /&gt; private static final Collection&amp;lt;String&amp;gt; supportedAnnotations = unmodifiableCollection(Arrays&lt;br /&gt;         .asList(&quot;net.sourceforge.jannotater.AnnotateR&quot;));&lt;br /&gt;&lt;br /&gt; // No supported options&lt;br /&gt; private static final Collection&amp;lt;String&amp;gt; supportedOptions = emptySet();&lt;br /&gt;&lt;br /&gt; public Collection&amp;lt;String&amp;gt; supportedAnnotationTypes() {&lt;br /&gt;     return supportedAnnotations;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public Collection&amp;lt;String&amp;gt; supportedOptions() {&lt;br /&gt;     return supportedOptions;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public AnnotationProcessor getProcessorFor(Set&amp;lt;AnnotationTypeDeclaration&amp;gt; atds, AnnotationProcessorEnvironment env) {&lt;br /&gt;     return new AnnotateRProcessor(atds, env);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This basically just generates objects of the class that does the work:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;pre style=&quot;font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%&quot;&gt;&lt;code&gt;&lt;br /&gt;public class AnnotateRProcessor extends SimpleDeclarationVisitor implements AnnotationProcessor {&lt;br /&gt;  private final AnnotationProcessorEnvironment mEnv;&lt;br /&gt;  private final Set&amp;lt;AnnotationTypeDeclaration&amp;gt; mAnnotationDeclarations;&lt;br /&gt;      .......&lt;br /&gt;      .....&lt;br /&gt;&lt;br /&gt;  AnnotateRProcessor(Set&amp;lt;AnnotationTypeDeclaration&amp;gt; annotationDeclarations, AnnotationProcessorEnvironment env) {&lt;br /&gt;      this.mAnnotationDeclarations = annotationDeclarations;&lt;br /&gt;      this.mEnv = env;&lt;br /&gt;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void process() {&lt;br /&gt;      for (AnnotationTypeDeclaration annotationType : this.mAnnotationDeclarations) {&lt;br /&gt;          for (Declaration typeDecl : mEnv.getDeclarationsAnnotatedWith(annotationType)) {&lt;br /&gt;              typeDecl.accept(getDeclarationScanner(this, NO_OP));&lt;br /&gt;          }&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Override&lt;br /&gt;  public void visitMethodDeclaration(MethodDeclaration d) {&lt;br /&gt;         //Process a method&lt;br /&gt;      }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Then it&#39;s just a matter of running it all (using the -factory option forces apt to use your AnnotationProcessor):&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;pre style=&quot;font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%&quot;&gt;&lt;code&gt;apt -cp ./jannotater.jar:$JAVA_HOME/lib/tools.jar -factory net.sourceforge.jannotater.AnnotateRJavaFactory -nocompile -s ${packagename}_tmp $srcfiles&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In my case, it also took some reading up on the &lt;a href=&quot;http://cran.r-project.org/doc/manuals/R-exts.html#Package-structure&quot;&gt;structure of R packages&lt;/a&gt; and then I decided I might as well insert the javadoc comments into the R interface and use &lt;a href=&quot;http://inlinedocs.r-forge.r-project.org/&quot;&gt;inlinedocs&lt;/a&gt; to generate the R documentation that are used to generate the help and manuals.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Anyway, I got it all working and threw it up onto &lt;a href=&quot;https://sourceforge.net/projects/jannotater/&quot;&gt;sourceforge&lt;/a&gt;.  For not I just focused on the basic functionality of exposing constructors and methods, but it wouldn&#39;t be hard to extend it to expose attributes, too.  Also worth mentioning is that I&#39;m doing absolutely nothing to prevent name collisions, so watch out for those.  Despite the limitations, though, this makes it absolutely easy-peasy to expose Java functionality to R.  Hopefully this is also of value to people who are not me.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/199935134017856083/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfolson.blogspot.com/2011/03/automated-generation-of-rjava.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/199935134017856083'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/199935134017856083'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/2011/03/automated-generation-of-rjava.html' title='Automated Generation of rJava Interfaces with Annotations'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7729215530786039380.post-2929935996642682308</id><published>2011-03-04T14:06:00.000-08:00</published><updated>2011-03-04T15:21:44.775-08:00</updated><title type='text'>Towards a Wrapping JComboBox</title><content type='html'>&lt;div&gt;Okay, so I&#39;m going to go ahead and publicly document a terrible decision I made a while ago. I decided that in order to make the user interface I wanted, I needed a WrappingComboBox. I wanted a JComboBox-like interface that would show a collection of options of widely varying lengths but that would not need to expand to occupy the space of the largest item but that could takeup multiple lines of text. I basically wanted this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;                         &lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRkYeCBmSKpPpfE7WRXwW_Acb2kfgfLCf-Qqn8uARF6s7re6ta5RCjR8mhaua70QFu73LIQzdcZsvSNrmpPTDtpKOs0HQTjYP1IshRpuX4RwyM0QomCeVcLKs3v_H4RZnKzsCrAzpSGg8/s1600/Screenshot-open.png&quot;&gt;&lt;img style=&quot;cursor:pointer; cursor:hand;width: 200px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRkYeCBmSKpPpfE7WRXwW_Acb2kfgfLCf-Qqn8uARF6s7re6ta5RCjR8mhaua70QFu73LIQzdcZsvSNrmpPTDtpKOs0HQTjYP1IshRpuX4RwyM0QomCeVcLKs3v_H4RZnKzsCrAzpSGg8/s320/Screenshot-open.png&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5580359378407328498&quot; /&gt;&lt;/a&gt;                     &lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzbfm-BqGyDxnsNAExwGR8XUcE4CWGukW_lDQ0vgNcAImb37Sie4ssozBf0Juo7rV3Z71UnU3XE8-bHtVOdE37KS8-zWlUQ38xlbplMsC70XPUbJiQM3SaMI6ENeqM5mN-cnCFDLyTrOk/s1600/Screenshot-closed.png&quot;&gt;&lt;img style=&quot;cursor:pointer; cursor:hand;width: 200px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzbfm-BqGyDxnsNAExwGR8XUcE4CWGukW_lDQ0vgNcAImb37Sie4ssozBf0Juo7rV3Z71UnU3XE8-bHtVOdE37KS8-zWlUQ38xlbplMsC70XPUbJiQM3SaMI6ENeqM5mN-cnCFDLyTrOk/s320/Screenshot-closed.png&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5580359374281543330&quot; /&gt;&lt;/a&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I wanted to choose from a set of options, but not require the entire selected option be visible when selected.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This seemingly simple task led me on an increasingly frustrating and desparate struggle to create something acceptable, ultimately including digging into the OpenJDK source code to see what&#39;s going on.  The end product does what I want, mostly, but it also prompted the following comment on the Java forum &lt;a href=&quot;http://forums.oracle.com/forums/thread.jspa?threadID=1771411&amp;amp;tstart=315&quot;&gt;here&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&quot;I&#39;ve seen guys abuse Swing Components before, making them do things they shouldn&#39;t do... but I think you got em all beat!&quot;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Okay, so here it is:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style=&quot;font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%&quot;&gt;&lt;code&gt;&lt;br /&gt;import java.awt.Component;&lt;br /&gt;import java.awt.Container;&lt;br /&gt;import java.awt.Dimension;&lt;br /&gt;import java.awt.Insets;&lt;br /&gt;import java.awt.LayoutManager;&lt;br /&gt;import java.awt.Rectangle;&lt;br /&gt;import java.awt.event.ActionEvent;&lt;br /&gt;import java.awt.event.ActionListener;&lt;br /&gt;import java.awt.event.ComponentEvent;&lt;br /&gt;import java.awt.event.ComponentListener;&lt;br /&gt;import java.awt.event.InputMethodEvent;&lt;br /&gt;import java.awt.event.InputMethodListener;&lt;br /&gt;import java.util.Vector;&lt;br /&gt;&lt;br /&gt;import javax.swing.BoxLayout;&lt;br /&gt;import javax.swing.ComboBoxEditor;&lt;br /&gt;import javax.swing.JComboBox;&lt;br /&gt;import javax.swing.JComponent;&lt;br /&gt;import javax.swing.JFrame;&lt;br /&gt;import javax.swing.JLabel;&lt;br /&gt;import javax.swing.JList;&lt;br /&gt;import javax.swing.JScrollPane;&lt;br /&gt;import javax.swing.JTextArea;&lt;br /&gt;import javax.swing.JViewport;&lt;br /&gt;import javax.swing.ListCellRenderer;&lt;br /&gt;import javax.swing.ScrollPaneConstants;&lt;br /&gt;import javax.swing.SwingUtilities;&lt;br /&gt;import javax.swing.event.EventListenerList;&lt;br /&gt;import javax.swing.plaf.basic.BasicComboBoxUI;&lt;br /&gt;import javax.swing.plaf.basic.BasicComboPopup;&lt;br /&gt;import javax.swing.plaf.basic.ComboPopup;&lt;br /&gt;import javax.swing.text.View;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* @author jfolson&lt;br /&gt;*&lt;br /&gt;*/&lt;br /&gt;public class WrappingComboBox extends JComboBox {&lt;br /&gt;&lt;br /&gt;   WrappingComboBox() {&lt;br /&gt;       super();&lt;br /&gt;       this.setUI(new WrappingComboBoxUI());&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   WrappingComboBox(Vector&amp;lt;?&amp;gt; items) {&lt;br /&gt;       super(items);&lt;br /&gt;       this.setUI(new WrappingComboBoxUI());&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public static class WrappingComboBoxUI extends BasicComboBoxUI {&lt;br /&gt;       WrappingHelper wrappingHelper;&lt;br /&gt;&lt;br /&gt;       public WrappingComboBoxUI() {&lt;br /&gt;           super();&lt;br /&gt;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       public WrappingHelper getWrappingHelper() {&lt;br /&gt;           if (wrappingHelper == null)&lt;br /&gt;               wrappingHelper = new WrappingHelper();&lt;br /&gt;           return wrappingHelper;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       /* Since my Editor, Renderer and Popup are not UIResources, I have to overload these methods&lt;br /&gt;        * or else installUI would call BasicComboBoxUI.createXXX anyway and replace mine&lt;br /&gt;        * */&lt;br /&gt;       @Override&lt;br /&gt;       protected ComboBoxEditor createEditor() {&lt;br /&gt;           return new WrappingComboBoxEditor();&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       /* See note for createEditor()&lt;br /&gt;        * */&lt;br /&gt;       @Override&lt;br /&gt;       protected ListCellRenderer createRenderer() {&lt;br /&gt;           return new WrappingList.WrappingListCellRenderer();&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       /* See note for createEditor()&lt;br /&gt;        * */&lt;br /&gt;       @Override&lt;br /&gt;       protected ComboPopup createPopup() {&lt;br /&gt;           return new BasicComboPopup(comboBox) {&lt;br /&gt;               @Override&lt;br /&gt;               protected JList createList() {&lt;br /&gt;                   // use the WrappingList for the popup to make it update its&lt;br /&gt;                   // sizes&lt;br /&gt;                   return new WrappingList(comboBox.getModel());&lt;br /&gt;                   // BasicComboPopup overrides default, apparently fixes some&lt;br /&gt;                   // bug but I can&#39;t use it because BasicGraphicsUtils&lt;br /&gt;                   // functions are private&lt;br /&gt;                   /*return new JList(comboBox.getModel()) {&lt;br /&gt;                       @Override&lt;br /&gt;                       public void processMouseEvent(MouseEvent e) {&lt;br /&gt;                           if (BasicGraphicsUtils.isMenuShortcutKeyDown(e)) {&lt;br /&gt;                               // Fix for 4234053. Filter out the Control Key&lt;br /&gt;                               // from the list.&lt;br /&gt;                               // ie., don&#39;t allow CTRL key deselection.&lt;br /&gt;                               Toolkit toolkit = Toolkit.getDefaultToolkit();&lt;br /&gt;                               e = new MouseEvent((Component) e.getSource(), e&lt;br /&gt;                                       .getID(), e.getWhen(), e.getModifiers()&lt;br /&gt;                                       ^ toolkit.getMenuShortcutKeyMask(), e&lt;br /&gt;                                       .getX(), e.getY(), e.getXOnScreen(), e&lt;br /&gt;                                       .getYOnScreen(), e.getClickCount(), e&lt;br /&gt;                                       .isPopupTrigger(), MouseEvent.NOBUTTON);&lt;br /&gt;                           }&lt;br /&gt;                           super.processMouseEvent(e);&lt;br /&gt;                       }&lt;br /&gt;                   };*/&lt;br /&gt;               }&lt;br /&gt;           };&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       /*Have to overrige getMinimumSize, rectangleForCurrentValue and layoutContainer in the Helper class&lt;br /&gt;        * to make a smaller (reasonable) sized popup button, otherwise, for multi-line combobox you get&lt;br /&gt;        *  ridiculously large buttons.&lt;br /&gt;        * */&lt;br /&gt;       @Override&lt;br /&gt;       public Dimension getMinimumSize(JComponent c) {&lt;br /&gt;           if (!isMinimumSizeDirty) {&lt;br /&gt;               return new Dimension(cachedMinimumSize);&lt;br /&gt;           }&lt;br /&gt;           Dimension size = getDisplaySize();&lt;br /&gt;           Insets insets = getInsets();&lt;br /&gt;           // calculate the width and height of the button&lt;br /&gt;           int buttonHeight = size.height;&lt;br /&gt;           if (buttonHeight &amp;gt; arrowButton.getPreferredSize().width)&lt;br /&gt;               buttonHeight = arrowButton.getPreferredSize().width;&lt;br /&gt;           int buttonWidth = arrowButton.getPreferredSize().width;&lt;br /&gt;           // adjust the size based on the button width&lt;br /&gt;           size.height += insets.top + insets.bottom;&lt;br /&gt;           size.width += insets.left + insets.right + buttonWidth;&lt;br /&gt;&lt;br /&gt;           cachedMinimumSize.setSize(size.width, size.height);&lt;br /&gt;           isMinimumSizeDirty = false;&lt;br /&gt;&lt;br /&gt;           return new Dimension(size);&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       /**&lt;br /&gt;        * Returns the area that is reserved for drawing the currently selected&lt;br /&gt;        * item.&lt;br /&gt;        */&lt;br /&gt;       @Override&lt;br /&gt;       protected Rectangle rectangleForCurrentValue() {&lt;br /&gt;           int width = comboBox.getWidth();&lt;br /&gt;           int height = comboBox.getHeight();&lt;br /&gt;           Insets insets = getInsets();&lt;br /&gt;           int buttonSize = height - (insets.top + insets.bottom);&lt;br /&gt;           if (arrowButton != null) {&lt;br /&gt;               buttonSize = arrowButton.getWidth();&lt;br /&gt;           }&lt;br /&gt;           if (true) {// BasicGraphicsUtils.isLeftToRight(comboBox)) { // this&lt;br /&gt;               // method is not visible&lt;br /&gt;               return new Rectangle(insets.left, insets.top, width&lt;br /&gt;                       - (insets.left + insets.right + buttonSize), height&lt;br /&gt;                       - (insets.top + insets.bottom));&lt;br /&gt;           } else { // if I could tell, put the box on the other side for right&lt;br /&gt;               // to left checkboxes&lt;br /&gt;               return new Rectangle(insets.left + buttonSize, insets.top,&lt;br /&gt;                       width - (insets.left + insets.right + buttonSize),&lt;br /&gt;                       height - (insets.top + insets.bottom));&lt;br /&gt;           }&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       @Override&lt;br /&gt;       protected LayoutManager createLayoutManager() {&lt;br /&gt;           return getWrappingHelper();&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       private class WrappingHelper implements LayoutManager {&lt;br /&gt;&lt;br /&gt;           //&lt;br /&gt;           // LayoutManager&lt;br /&gt;           //&lt;br /&gt;           /* Need to override layoutContainer to put in a smaller popup button&lt;br /&gt;            *     */&lt;br /&gt;           public void addLayoutComponent(String name, Component comp) {} // TODO&lt;br /&gt;&lt;br /&gt;           public void removeLayoutComponent(Component comp) {} // TODO&lt;br /&gt;&lt;br /&gt;           public Dimension preferredLayoutSize(Container parent) {&lt;br /&gt;               return parent.getPreferredSize();&lt;br /&gt;           }&lt;br /&gt;&lt;br /&gt;           public Dimension minimumLayoutSize(Container parent) {&lt;br /&gt;               return parent.getMinimumSize();&lt;br /&gt;           }&lt;br /&gt;&lt;br /&gt;           public void layoutContainer(Container parent) {&lt;br /&gt;               JComboBox cb = (JComboBox) parent;&lt;br /&gt;               int width = cb.getWidth();&lt;br /&gt;               int height = cb.getHeight();&lt;br /&gt;&lt;br /&gt;               Insets insets = getInsets();&lt;br /&gt;               int buttonHeight = height - (insets.top + insets.bottom);&lt;br /&gt;&lt;br /&gt;               int buttonWidth = buttonHeight;&lt;br /&gt;               if (arrowButton != null) {&lt;br /&gt;                   if (buttonHeight &amp;gt; arrowButton.getPreferredSize().width)&lt;br /&gt;                       buttonHeight = arrowButton.getPreferredSize().width;&lt;br /&gt;                   Insets arrowInsets = arrowButton.getInsets();&lt;br /&gt;                   buttonWidth = arrowButton.getPreferredSize().width&lt;br /&gt;                           + arrowInsets.left + arrowInsets.right;&lt;br /&gt;               }&lt;br /&gt;               Rectangle cvb;&lt;br /&gt;&lt;br /&gt;               if (arrowButton != null) {&lt;br /&gt;                   if (true) {// BasicGraphicsUtils.isLeftToRight(cb)) { //&lt;br /&gt;                       // this method is not visible&lt;br /&gt;                       arrowButton.setBounds(width&lt;br /&gt;                               - (insets.right + buttonWidth), insets.top,&lt;br /&gt;                               buttonWidth, buttonHeight);&lt;br /&gt;                   } else { // if I could tell, put the box on the other side&lt;br /&gt;                       // for right to left checkboxes&lt;br /&gt;                       arrowButton.setBounds(insets.left, insets.top,&lt;br /&gt;                               buttonWidth, buttonHeight);&lt;br /&gt;                   }&lt;br /&gt;               }&lt;br /&gt;               if (editor != null) {&lt;br /&gt;                   cvb = rectangleForCurrentValue();&lt;br /&gt;                   editor.setBounds(cvb);&lt;br /&gt;               }&lt;br /&gt;           }&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public static class WrappingComboBoxEditor extends JScrollPane implements&lt;br /&gt;           ComboBoxEditor, ComponentListener {&lt;br /&gt;       EventListenerList listenerList = new EventListenerList();&lt;br /&gt;       LineAwareTextArea text;&lt;br /&gt;&lt;br /&gt;       public WrappingComboBoxEditor() {&lt;br /&gt;           super();&lt;br /&gt;           this.text = new LineAwareTextArea();&lt;br /&gt;           this.setViewportView(text);&lt;br /&gt;&lt;br /&gt;           setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);&lt;br /&gt;           setPreferredSize(new Dimension(100, 30));&lt;br /&gt;&lt;br /&gt;           this.setItem(&quot;&quot;);&lt;br /&gt;           text.setLineWrap(true);&lt;br /&gt;           text.setWrapStyleWord(false);&lt;br /&gt;           text.setEditable(true);&lt;br /&gt;&lt;br /&gt;           text.addInputMethodListener(new InputMethodListener() {&lt;br /&gt;               @Override&lt;br /&gt;               public void caretPositionChanged(InputMethodEvent arg0) {}&lt;br /&gt;&lt;br /&gt;               @Override&lt;br /&gt;               public void inputMethodTextChanged(InputMethodEvent arg0) {&lt;br /&gt;                   Object[] listeners = WrappingComboBoxEditor.this.listenerList&lt;br /&gt;                           .getListenerList();&lt;br /&gt;                   // Process the listeners last to first, notifying&lt;br /&gt;                   // those that are interested in this event&lt;br /&gt;                   ActionEvent actionEvent = null;&lt;br /&gt;                   for (int i = listeners.length - 2; i &amp;gt;= 0; i -= 2) {&lt;br /&gt;                       if (listeners[i] == ActionListener.class) {&lt;br /&gt;                           if (actionEvent == null)&lt;br /&gt;                               actionEvent = new ActionEvent(this,&lt;br /&gt;                                       ActionEvent.ACTION_PERFORMED, null);&lt;br /&gt;                           ((ActionListener) listeners[i + 1])&lt;br /&gt;                                   .actionPerformed(actionEvent);&lt;br /&gt;                       }&lt;br /&gt;                   }&lt;br /&gt;&lt;br /&gt;               }&lt;br /&gt;&lt;br /&gt;           });&lt;br /&gt;           text.addComponentListener(this);&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       @Override&lt;br /&gt;       public void addActionListener(ActionListener l) {&lt;br /&gt;           listenerList.add(ActionListener.class, l);&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       @Override&lt;br /&gt;       public Component getEditorComponent() {&lt;br /&gt;           return this;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       @Override&lt;br /&gt;       public Object getItem() {&lt;br /&gt;           return text.getText();&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       @Override&lt;br /&gt;       public void removeActionListener(ActionListener l) {&lt;br /&gt;           listenerList.remove(ActionListener.class, l);&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       @Override&lt;br /&gt;       public void setItem(Object anObject) {&lt;br /&gt;           if (anObject == null)&lt;br /&gt;               text.setText(&quot;&quot;);&lt;br /&gt;           else&lt;br /&gt;               text.setText(String.valueOf(anObject));&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       @Override&lt;br /&gt;       public void selectAll() {&lt;br /&gt;           text.selectAll();&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       public void componentHidden(ComponentEvent arg0) {}// TODO&lt;br /&gt;&lt;br /&gt;       public void componentMoved(ComponentEvent arg0) {}// TODO&lt;br /&gt;&lt;br /&gt;       @Override&lt;br /&gt;       public void componentResized(ComponentEvent arg0) {&lt;br /&gt;           JViewport view = this.getViewport();&lt;br /&gt;           Dimension viewDim = view.getExtentSize();&lt;br /&gt;           Insets insets = view.getInsets();&lt;br /&gt;           Dimension wantDim = text.getPreferredScrollableViewportSize();&lt;br /&gt;           // update preferred size of combobox&lt;br /&gt;           if (true) {// wantDim.height &amp;gt; viewDim.height) { // there may be&lt;br /&gt;               // times where&lt;br /&gt;               // you don&#39;t want it to get smaller, would need to think some&lt;br /&gt;               // more about this&lt;br /&gt;               viewDim.height = wantDim.height + insets.top + insets.bottom;&lt;br /&gt;               view.setPreferredSize(viewDim);&lt;br /&gt;               view.revalidate();&lt;br /&gt;               ((JComponent) this.getParent()).revalidate();&lt;br /&gt;               SwingUtilities.windowForComponent(this).pack();&lt;br /&gt;           }&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       @Override&lt;br /&gt;       public void componentShown(ComponentEvent arg0) {}&lt;br /&gt;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    * Only really need to expose rowHeight but might as well determine the&lt;br /&gt;    * correct size it wants its viewport&lt;br /&gt;    *&lt;br /&gt;    * @author jfolson&lt;br /&gt;    *&lt;br /&gt;    */&lt;br /&gt;   public static class LineAwareTextArea extends JTextArea {&lt;br /&gt;       private int maxVisibleRows = 3; // arbitrary default value, should&lt;br /&gt;                                       // probably be passed in instead&lt;br /&gt;&lt;br /&gt;       public LineAwareTextArea() {&lt;br /&gt;           super();&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       public LineAwareTextArea(String text) {&lt;br /&gt;           this();&lt;br /&gt;           setText(text);&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       @Override&lt;br /&gt;       public Dimension getPreferredScrollableViewportSize() {&lt;br /&gt;           Dimension d = this.getPreferredSize();&lt;br /&gt;           float hspan = getWidth();&lt;br /&gt;           View view = (getUI()).getRootView(this);&lt;br /&gt;           view.setSize(hspan, Float.MAX_VALUE);&lt;br /&gt;           // float vspan = view.getPreferredSpan(View.Y_AXIS);&lt;br /&gt;&lt;br /&gt;           Insets insets = getInsets();&lt;br /&gt;           // get the desired number of rows&lt;br /&gt;           int rows = (d.height / this.getRowHeight());&lt;br /&gt;           if (rows &amp;gt; maxVisibleRows)&lt;br /&gt;               rows = maxVisibleRows;&lt;br /&gt;&lt;br /&gt;           d.height = rows * getRowHeight() + insets.top + insets.bottom;&lt;br /&gt;           /*System.out.println(&quot;prefer view height: &quot; + d.height + &quot; (&quot; + rows&lt;br /&gt;                   + &quot; line(s))&quot;);*/&lt;br /&gt;           return d;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       public void setMaxVisibleRows(int rows) {&lt;br /&gt;           this.maxVisibleRows = rows;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       public int getMaxVisibleRows() {&lt;br /&gt;           return this.maxVisibleRows;&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;pre style=&quot;font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%&quot;&gt;&lt;code&gt;&lt;br /&gt;import java.awt.Component;&lt;br /&gt;import java.awt.Dimension;&lt;br /&gt;import java.awt.Insets;&lt;br /&gt;&lt;br /&gt;import javax.swing.JFrame;&lt;br /&gt;import javax.swing.JList;&lt;br /&gt;import javax.swing.JScrollPane;&lt;br /&gt;import javax.swing.JTextPane;&lt;br /&gt;import javax.swing.ListCellRenderer;&lt;br /&gt;import javax.swing.ListModel;&lt;br /&gt;import javax.swing.border.EmptyBorder;&lt;br /&gt;import javax.swing.plaf.basic.BasicListUI;&lt;br /&gt;import javax.swing.text.View;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * List that wraps its text! Whenever the list&#39;s layout (e.g. size) changes the&lt;br /&gt; * list&#39;s UI is instructed to update its layout too.&lt;br /&gt; */&lt;br /&gt;public class WrappingList extends JList {&lt;br /&gt;&lt;br /&gt;    public WrappingList() {&lt;br /&gt;        setUI(new WrappingListUI());&lt;br /&gt;        setCellRenderer(new WrappingListCellRenderer());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public WrappingList(ListModel model) {&lt;br /&gt;        super(model);&lt;br /&gt;        setUI(new WrappingListUI());&lt;br /&gt;        setCellRenderer(new WrappingListCellRenderer());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public void doLayout() {&lt;br /&gt;        ((WrappingListUI) getUI()).updateLayoutState();&lt;br /&gt;        super.doLayout();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public boolean getScrollableTracksViewportWidth() {&lt;br /&gt;        return true;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * ListUI implementation that exposes the method for updating its layout&lt;br /&gt;     */&lt;br /&gt;    private static class WrappingListUI extends BasicListUI {&lt;br /&gt;&lt;br /&gt;        @Override&lt;br /&gt;        public void updateLayoutState() {&lt;br /&gt;            super.updateLayoutState();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * List cell renderer that uses the list&#39;s width to alter its preferred size&lt;br /&gt;     * TODO - override bound properties in the same way as&lt;br /&gt;     * DefaultListCellRenderer&lt;br /&gt;     */&lt;br /&gt;    public static class WrappingListCellRenderer extends JTextPane implements&lt;br /&gt;            ListCellRenderer {&lt;br /&gt;&lt;br /&gt;        public WrappingListCellRenderer() {&lt;br /&gt;            setBorder(new EmptyBorder(1, 1, 1, 1));&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public Component getListCellRendererComponent(JList list, Object value,&lt;br /&gt;                int index, boolean selected, boolean hasFocus) {&lt;br /&gt;&lt;br /&gt;            setBackground(selected ? list.getSelectionBackground() : list&lt;br /&gt;                    .getBackground());&lt;br /&gt;            setForeground(selected ? list.getSelectionForeground() : list&lt;br /&gt;                    .getForeground());&lt;br /&gt;            // TODO - border, font etc.&lt;br /&gt;&lt;br /&gt;            setText(String.valueOf(value));&lt;br /&gt;&lt;br /&gt;            float hspan = list.getWidth();&lt;br /&gt;            View view = (getUI()).getRootView(this);&lt;br /&gt;            view.setSize(hspan, Float.MAX_VALUE);&lt;br /&gt;            float vspan = view.getPreferredSpan(View.Y_AXIS);&lt;br /&gt;&lt;br /&gt;            Insets insets = getInsets();&lt;br /&gt;            setPreferredSize(new Dimension(insets.left + insets.right&lt;br /&gt;                    + (int) hspan, insets.top + insets.bottom + (int) vspan));&lt;br /&gt;&lt;br /&gt;            return this;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args) {&lt;br /&gt;        new Test();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static class Test extends JFrame {&lt;br /&gt;&lt;br /&gt;        public Test() {&lt;br /&gt;            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);&lt;br /&gt;            init();&lt;br /&gt;            pack();&lt;br /&gt;            show();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private void init() {&lt;br /&gt;            JList list = new WrappingList();&lt;br /&gt;            list&lt;br /&gt;                    .setListData(new Object[] {&lt;br /&gt;                            &amp;quot;Item One&amp;quot;,&lt;br /&gt;                            &amp;quot;Item Two content content content content Item Two content content content content Item Two content content content content Item Two content content content content&amp;quot; });&lt;br /&gt;&lt;br /&gt;            setContentPane(new JScrollPane(list,&lt;br /&gt;                    JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,&lt;br /&gt;                    JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;And a main to run:&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;pre style=&quot;font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%&quot;&gt;&lt;code&gt;&lt;br /&gt;public class ComboTest extends JFrame {&lt;br /&gt;&lt;br /&gt;       public ComboTest() {&lt;br /&gt;           setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);&lt;br /&gt;           init();&lt;br /&gt;           pack();&lt;br /&gt;           show();&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       private void init() {&lt;br /&gt;&lt;br /&gt;           ScrollablePanel mainPanel = new ScrollablePanel();&lt;br /&gt;           mainPanel.setScrollableWidth(ScrollableSizeHint.FIT);&lt;br /&gt;           mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));&lt;br /&gt;           ScrollablePanel fieldPanel = new ScrollablePanel();&lt;br /&gt;           fieldPanel.setLayout(new BoxLayout(fieldPanel, BoxLayout.X_AXIS));&lt;br /&gt;           fieldPanel.setScrollableWidth(ScrollableSizeHint.FIT);&lt;br /&gt;&lt;br /&gt;           Vector&amp;lt;String&amp;gt; items = new Vector&amp;lt;String&amp;gt;();&lt;br /&gt;           items.add(&quot;Item One&quot;);&lt;br /&gt;&lt;br /&gt;           JComboBox list = new WrappingComboBox(items);&lt;br /&gt;           list.setEditable(true);&lt;br /&gt;&lt;br /&gt;           JLabel fieldLabel = new JLabel(&quot;Label: &quot;);&lt;br /&gt;           fieldLabel.setAlignmentY(TOP_ALIGNMENT);&lt;br /&gt;           list.setAlignmentY(TOP_ALIGNMENT);&lt;br /&gt;           fieldPanel.add(fieldLabel);&lt;br /&gt;           fieldPanel.add(list);&lt;br /&gt;           mainPanel.add(fieldPanel);&lt;br /&gt;&lt;br /&gt;           fieldPanel = new ScrollablePanel();&lt;br /&gt;           fieldPanel.setLayout(new BoxLayout(fieldPanel, BoxLayout.X_AXIS));&lt;br /&gt;           fieldPanel.setScrollableWidth(ScrollableSizeHint.FIT);&lt;br /&gt;&lt;br /&gt;           items&lt;br /&gt;                   .add(&quot;Item Two content content content content Item Two content content content content Item Two content content content content Item Two content content content content&quot;);&lt;br /&gt;&lt;br /&gt;           list = new WrappingComboBox(items);&lt;br /&gt;           list.setEditable(true);&lt;br /&gt;&lt;br /&gt;           fieldLabel = new JLabel(&quot;Label: &quot;);&lt;br /&gt;           fieldLabel.setAlignmentY(TOP_ALIGNMENT);&lt;br /&gt;           list.setAlignmentY(TOP_ALIGNMENT);&lt;br /&gt;           fieldPanel.add(fieldLabel);&lt;br /&gt;           fieldPanel.add(list);&lt;br /&gt;           mainPanel.add(fieldPanel);&lt;br /&gt;           JScrollPane mainScrolls = new JScrollPane(mainPanel,&lt;br /&gt;                   JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,&lt;br /&gt;                   JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);&lt;br /&gt;           mainScrolls.setPreferredSize(new Dimension(200, 200));&lt;br /&gt;           setContentPane(mainScrolls);&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public static void main(String[] args) {&lt;br /&gt;       new ComboTest();&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;So now it doesn&#39;t look nearly so bad, but Swing does a lot of delegating to helper classes, and I had to track down those helper classes, none of which make much of an appearance in the API or javadoc and make sure that my versions were being used.  In particular, I had to override createEditor(), createRenderer() and createPopup() or else the super classes would just replace my classes with their own.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There was also a bit of math in determining sizes that I had to consult the OpenJDK to determine the right things to query.  I changed it up a bit in order to reduce the size of the popup button for taller WrappingComboBoxes.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The ScrollablePanel class referenced comes from &lt;a href=&quot;http://tips4java.wordpress.com/2009/12/20/scrollable-panel/&quot;&gt;here&lt;/a&gt; but I don&#39;t think it&#39;s actually necessary for simple use cases but it&#39;s definitely worth knowing about.  Feel free to ask questions.  Hopefully this is helpful to at least one person somewhere sometime.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/2929935996642682308/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfolson.blogspot.com/2011/03/towards-wrapping-jcombobox.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/2929935996642682308'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/2929935996642682308'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/2011/03/towards-wrapping-jcombobox.html' title='Towards a Wrapping JComboBox'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRkYeCBmSKpPpfE7WRXwW_Acb2kfgfLCf-Qqn8uARF6s7re6ta5RCjR8mhaua70QFu73LIQzdcZsvSNrmpPTDtpKOs0HQTjYP1IshRpuX4RwyM0QomCeVcLKs3v_H4RZnKzsCrAzpSGg8/s72-c/Screenshot-open.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7729215530786039380.post-8225208559117119003</id><published>2009-10-26T15:36:00.000-07:00</published><updated>2009-10-26T15:47:57.485-07:00</updated><title type='text'>Going Parallel in R</title><content type='html'>I recently had the need to get some parallelism going in R.  I was doing some largeish-scale monte carlo and markov chain monte carlo simulations of latent space network models for NIPS.  Anyway, my needs were quite simple, basically, I just need lots of repetitions and I wanted to easily spread the repetitions across a few processors/cores, all on a single machine.&lt;br /&gt;&lt;br /&gt;I found a number of options and started out using the &lt;a href=&quot;http://www.rforge.net/doc/packages/multicore/multicore.html&quot;&gt;multicore&lt;/a&gt; package.  This worked wonderfully and easily when I tested it out on my desktop and ported my code over to use it.  Of course, I neglected to notice the not-so-small print on the manual:&lt;br /&gt;&lt;blockquote&gt;SystemRequirements: POSIX-compliant OS (&lt;span style=&quot;font-weight: bold;&quot;&gt;essentially anything but Windows&lt;/span&gt;)&lt;/blockquote&gt;Now, I had never really encountered system-specific packages for R before, so this kind of caught me by surprise when I scp&#39;ed things over to our big ole&#39; windows machine to run and found that I couldn&#39;t install multicore.  Well, eventually wised up and replaced mutlicore with &lt;a href=&quot;http://cran.r-project.org/web/packages/snowfall/index.html&quot;&gt;snowfall&lt;/a&gt; when on a windows system.  Beginning to think I should have just used &lt;a href=&quot;http://cran.r-project.org/web/packages/foreach/index.html&quot;&gt;foreach&lt;/a&gt;, but that&#39;s another story.</content><link rel='replies' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/8225208559117119003/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfolson.blogspot.com/2009/10/going-parallel-in-r.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/8225208559117119003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/8225208559117119003'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/2009/10/going-parallel-in-r.html' title='Going Parallel in R'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7729215530786039380.post-4067451109716909964</id><published>2009-10-26T09:52:00.000-07:00</published><updated>2009-10-26T15:35:41.423-07:00</updated><title type='text'>Editing Mendeley Citation keys</title><content type='html'>&lt;a href=&quot;http://www.mendeley.com/&quot;&gt;Mendeley&lt;/a&gt; is a nifty new tool for organizing and sharing academic references.  I&#39;ve been test driving it a bit, mainly using it to import files that I then transfer over to &lt;a href=&quot;http://jabref.sourceforge.net/&quot;&gt;JabRef&lt;/a&gt;.  This was working out alright, since Mendeley allows you to export to bibtex, which is the backend for JabRef and what I ultimately end up using to write my papers.  I&#39;ve been looking for a way to streamline this process, since I like Mendeley&#39;s interface a lot better and it has better search features, e.g. full-text search of all attached pdfs.&lt;br /&gt;&lt;br /&gt;Unfortunately, Mendeley doesn&#39;t use bibtex for its backend, but it does use SQLite and will even let you export your database as a SQLite zip file.  I can&#39;t seem to figure out where Mendeley stores my user files so far, so exporting seems the way to go.&lt;br /&gt;&lt;br /&gt;Using the &lt;a href=&quot;http://sqlitebrowser.sourceforge.net/&quot;&gt;SQLite Database Browser&lt;/a&gt;, I managed to load up my Mendeley database.  Things seem pretty obvious, with most of the things you&#39;d want to futz with in the &quot;Documents&quot; table.  For now I just removed all the citationKey values and generated my own the way that I wanted them, but it&#39;s nice to know that it&#39;s not too miserable to get at the raw data from Mendeley.&lt;br /&gt;&lt;br /&gt;Note: You can also restore an edited zip of sqlite data, but you have to have it all flat, exactly the way that Mendeley exported it.</content><link rel='replies' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/4067451109716909964/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfolson.blogspot.com/2009/10/editing-mendeley-citation-keys.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/4067451109716909964'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/4067451109716909964'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/2009/10/editing-mendeley-citation-keys.html' title='Editing Mendeley Citation keys'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7729215530786039380.post-6848101799022802804</id><published>2009-03-24T13:02:00.000-07:00</published><updated>2009-03-24T13:19:26.764-07:00</updated><title type='text'>Analysing Spatiall Embedded Networks</title><content type='html'>I realized this would probably be a decent place to describe some of the work I&#39;ve been doing and hopefully get some feedback.  I&#39;ve been working at the intersection of spatial information and network information, trying to develop ways of integrating the two types of information.  This is not the &quot;&lt;a href=&quot;http://en.wikipedia.org/wiki/Spatial_network&quot;&gt;spatial network&lt;/a&gt;s&quot; that are used to analyze space syntax and transportation networks.  Instead, I&#39;m interested in looking at arbitrary networks of things where some of the things are labeled with spatial location information.&lt;br /&gt;&lt;br /&gt;So far I&#39;ve analyzed drug seizure networks, shipping networks, organizational network of terrorist groups, epidemiological networks and simulated social networks.  Most of this analysis has been to test new methodologies I&#39;ve developed.  There are two main techniques I&#39;ve been working on, manipulating network scale and aggregation and visualizing spatial dependencies in network topology.&lt;br /&gt;&lt;br /&gt;There is a fundamental discord between network and space.  Space is continuous; relationships in networks are defined as between discrete entities.  This means that some level of aggregation of space (implicit or explicit) us required in order to do a meaningful analysis of the network.  Different levels of aggregation can lead do quite different networks.  I&#39;ve been working on methods of capturing the tradeoffs of aggregation versus precision.&lt;br /&gt;&lt;br /&gt;Second, visualization of social networks has been important historically in providing the intuition for many commonly used network statistics.  My hope is that the visualization of spatially embedded network data will be similarly useful.   However, simple visualizations of spatially embedded networks quickly become noisy and difficult to interpret.  For this reason, I&#39;m developing techniques for visualizing higher-level network topological properties and their interaction with spatial location.   I am also working to develop a statistical measure of the spatial dependencies in structural properties of a spatially embedded network.&lt;br /&gt;&lt;br /&gt;All of these techniques and more have been implemented in the Geospatial Network Visualizer in the &lt;a href=&quot;http://www.casos.cs.cmu.edu/projects/ora/&quot;&gt;ORA dynamic network analysis tool&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/6848101799022802804/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfolson.blogspot.com/2009/03/analysing-spatiall-embedded-networks.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/6848101799022802804'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/6848101799022802804'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/2009/03/analysing-spatiall-embedded-networks.html' title='Analysing Spatiall Embedded Networks'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7729215530786039380.post-1814206649794138673</id><published>2009-03-01T16:09:00.000-08:00</published><updated>2009-03-01T16:18:01.302-08:00</updated><title type='text'>Managing Bibtex and PDFs with JabRef!</title><content type='html'>A few days ago, I decided it was time for my assorted bibtex files to grow up and get organized.  I also decided that my assorted pdfs of papers should also get centralized and organized.  I had the crazy idea, that there might be a tool that could do both of these tasks in an open, portable format that I could easily transition if I grew tired of the tool.  Unlike past endeavors, in this task I succeeded beyond all expectation.  The &lt;a href=&quot;http://jabref.sourceforge.net/&quot;&gt;JabRef&lt;/a&gt; reference manager is an open source reference manager that uses the standard bibtex format as a backend.  Unlike other tools I tried, it works, simple and pretty feature-complete, it provides a centralized location to store references, notes/review/summaries of said references, as well as links to the actual pdfs (when you have them).  JabRef will automatically generate bibtex keys for you, if you ask it, and if you save your pdfs with the respective key in its filename, JabRef can automatically find the file and associate it with the intended reference!  Plus, like many of the other tools, it can automatically sync up to many of the online databases and download documents if you have access to them.&lt;br /&gt;&lt;br /&gt;It also has extremely flexible keyword/category-based organization of references.  Your categories can inherit either supercategories, subcategories, or neither.</content><link rel='replies' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/1814206649794138673/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfolson.blogspot.com/2009/03/managing-bibtex-and-pdfs-with-jabref.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/1814206649794138673'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/1814206649794138673'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/2009/03/managing-bibtex-and-pdfs-with-jabref.html' title='Managing Bibtex and PDFs with JabRef!'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7729215530786039380.post-1016216995276677608</id><published>2009-03-01T15:58:00.000-08:00</published><updated>2009-03-01T16:07:55.793-08:00</updated><title type='text'>Cisco VPN client in Ubuntu 8.10</title><content type='html'>I&#39;m not sure what changed, but I can&#39;t seem to get the Cisco VPN client working in Ubuntu 8.10.  In 8.04 I&#39;d gotten things working by following instructions like &lt;a href=&quot;http://www.blog.arun-prabha.com/2008/05/01/cisco-vpn-installation-issue-with-ubuntu-804-hardy-heron/&quot;&gt;these&lt;/a&gt;.  Once again, the good folks at tuxx-home.at had a few &lt;a href=&quot;http://projects.tuxx-home.at/?id=cisco_vpn_client&quot;&gt;solutions&lt;/a&gt;, but I couldn&#39;t get them to work.  Either seg faults when it ran or compiler errors and it wouldn&#39;t.  Anyway, I&#39;d been leery, of trying &lt;a href=&quot;http://www.debuntu.org/how-to-connect-to-a-cisco-vpn-using-vpnc&quot;&gt;vpnc&lt;/a&gt; since my .pcf files for the SCS at Carnegie Mellon didn&#39;t quite look like any of the examples.  However, when I eventually tried &lt;a href=&quot;http://www.debuntu.org/how-to-connect-to-a-cisco-vpn-using-vpnc&quot;&gt;this decoder&lt;/a&gt; with my encoded group password, everything worked out!  Now I just use the NetworkManager applet to manage my vpn and it all (mostly) works.</content><link rel='replies' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/1016216995276677608/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfolson.blogspot.com/2009/03/cisco-vpn-client-in-ubuntu-810.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/1016216995276677608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/1016216995276677608'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/2009/03/cisco-vpn-client-in-ubuntu-810.html' title='Cisco VPN client in Ubuntu 8.10'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7729215530786039380.post-6572677284658134830</id><published>2009-03-01T15:55:00.000-08:00</published><updated>2009-03-01T15:58:29.841-08:00</updated><title type='text'>FGLRX Sadness</title><content type='html'>I was reminded once again of why other people give up on linux when I walked over to my new desktop only to find that the 3d graphics driver, fglrx, had decided not to work.  I spent several hours over the next few days trying to track down the issue, uninstalling, reinstalling, reverting to older versions of the driver, xserver, the linux kernel, all to no avail.  Eventually, I gave up and am now using the 2d open source drivers.  The fglrx driver had even stopped working adequately for simple window management things.</content><link rel='replies' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/6572677284658134830/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfolson.blogspot.com/2009/03/fglrx-sadness.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/6572677284658134830'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/6572677284658134830'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/2009/03/fglrx-sadness.html' title='FGLRX Sadness'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7729215530786039380.post-8044751513838851545</id><published>2009-01-29T19:01:00.001-08:00</published><updated>2009-01-29T19:03:09.924-08:00</updated><title type='text'>HIDPoint Rocks</title><content type='html'>&lt;a href=&quot;http://www.hidpoint.com/home.html&quot;&gt;HIDPoint&lt;/a&gt; makes a free driver and configuration program for a variety of mice and keyboards for linux.  I just installed their drivers for the Logitech MX3200 wireless keyboard and the MX600 wireless mouse and it works fabulously.  All the special buttons and fancy things work!  I couldn&#39;t be happier!</content><link rel='replies' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/8044751513838851545/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfolson.blogspot.com/2009/01/hidpoint-rocks.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/8044751513838851545'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/8044751513838851545'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/2009/01/hidpoint-rocks.html' title='HIDPoint Rocks'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7729215530786039380.post-2142942219553102581</id><published>2009-01-28T10:53:00.000-08:00</published><updated>2009-01-28T10:54:19.670-08:00</updated><title type='text'>Also</title><content type='html'>I love my G1</content><link rel='replies' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/2142942219553102581/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfolson.blogspot.com/2009/01/also.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/2142942219553102581'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/2142942219553102581'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/2009/01/also.html' title='Also'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7729215530786039380.post-4722150374830635113</id><published>2009-01-28T10:42:00.000-08:00</published><updated>2009-01-28T10:51:57.480-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Android"/><category scheme="http://www.blogger.com/atom/ns#" term="java"/><title type='text'>Android HTML parsing</title><content type='html'>A few weeks ago, I spent an extended period of time trying to find an html parser that would work reasonably well in the &lt;a href=&quot;http://code.google.com/android/&quot;&gt;Android&lt;/a&gt; mobile operating system.  Long story short: fail.  &lt;a href=&quot;http://java-source.net/open-source/html-parsers&quot;&gt;This site&lt;/a&gt; provides a helpful list of open source html parsers for java, but alas each and every one of them was too slow to be usable in android.  I wasn&#39;t even trying to parse a large page, only ~100K, but it was still taking ~ 1 minute to parse.  In the end, I gave up.  Instead of parsing the site and redisplaying some of the info, I just manually skipped down to the content part of the page and fed the content as a String to the built-in webkit browser.  Fortunately, Android makes it extremely straightforward to embed the browser in an app.</content><link rel='replies' type='application/atom+xml' href='http://jfolson.blogspot.com/feeds/4722150374830635113/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfolson.blogspot.com/2009/01/android-html-parsing.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/4722150374830635113'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7729215530786039380/posts/default/4722150374830635113'/><link rel='alternate' type='text/html' href='http://jfolson.blogspot.com/2009/01/android-html-parsing.html' title='Android HTML parsing'/><author><name>jfolson</name><uri>http://www.blogger.com/profile/17741840871174299587</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry></feed>