<?xml version="1.0" encoding="utf-8"?>

<!DOCTYPE rdf:RDF [
<!ENTITY % HTMLlat1 PUBLIC
 "-//W3C//ENTITIES Latin 1 for XHTML//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent">
]>

<rdf:RDF 
  xmlns="http://purl.org/rss/1.0/"
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:admin="http://webns.net/mvcb/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"
  xmlns:sub="http://purl.org/rss/1.0/modules/subscription/"
> 

  <channel rdf:about="http://aperiodic.net/phil/">
    <title>Phil! Gregory</title>
    <link>http://aperiodic.net/phil/</link>
    <description>Mostly books that phil!'s read recently; other stuff that's interesting to him
and probably few others.</description>
    <language>en</language>
    <dc:creator>Phil Gregory (phil_g@pobox.com)</dc:creator>
    <dc:rights>Copyright Phil Gregory</dc:rights>
    <admin:generatorAgent rdf:resource="http://www.raelity.org/apps/blosxom/?v=2.0" />
    <admin:errorReportsTo rdf:resource="mailto:phil_g@pobox.com"/>

    <items>
      <rdf:Seq>
        <rdf:li rdf:resource="http://aperiodic.net/phil/archives/Geekery/lossless-dvd-to-mkv.html" />
        <rdf:li rdf:resource="http://aperiodic.net/phil/archives/Geekery/bluetooth-proximity.html" />
        <rdf:li rdf:resource="http://aperiodic.net/phil/archives/General/nfsn-hosted.html" />
        <rdf:li rdf:resource="http://aperiodic.net/phil/archives/Geekery/java-reflection-noodling.html" />
        <rdf:li rdf:resource="http://aperiodic.net/phil/archives/Recipes/smakelijke-gerechten-pumpkin-pie.html" />
        <rdf:li rdf:resource="http://aperiodic.net/phil/archives/Recipes/grandmas-potato-salad.html" />
        <rdf:li rdf:resource="http://aperiodic.net/phil/archives/Recipes/grandmas-parker-house-rolls.html" />

      </rdf:Seq>
    </items>



  </channel>

  <item rdf:about="http://aperiodic.net/phil/archives/Geekery/lossless-dvd-to-mkv.html">
    <title>DVD Video to Matroska Video, Losslessly</title>
    <link>http://aperiodic.net/phil/archives/Geekery/lossless-dvd-to-mkv.html</link>
    <description>I recently had the desire to rip some DVDs so I could watch them on my
computer without swapping discs...</description>
    <dc:subject>/Geekery</dc:subject>
    <dc:creator>Phil Gregory</dc:creator>
    <dc:date>2008-07-11T14:35-04:00</dc:date>
    
    <content:encoded><![CDATA[<p>I recently had the desire to rip some DVDs so I could watch them on my
computer without swapping discs.  I figured I could just pull everything
from the DVD into <a href="http://www.matroska.org/">Matroska</a> files, since Matroska supports
everything that DVDs do.  When I went looking on the Internet, I found few
resources for moving from DVD to MKV, and everything that did talk about
it actually reencoded the DVD video to get it into its final destination.
Since Matroska can contain all of the codecs native to DVDs, I wanted to
transfer everything losslessly.  This is how I did it.  (Note that I'm
using the Linux command line; I prefer Linux to Windows, and the command
line to X.)</p>

<p>The programs I used are as follows:</p>

<ul>
<li><a href="https://launchpad.net/dvdbackup" title="Rip DVDs from the command line.">dvdbackup</a> (optional)</li>
<li><a href="http://xinehq.de/" title="Free video player.">xine</a> (optional; only for cracking CSS)</li>
<li><a href="http://untrepid.com/acidrip/lsdvd.html" title="Read and display DVD TOC.">lsdvd</a></li>
<li><a href="http://www.transcoding.org/" title="Command line program suite for transcoding audio and video.">transcode</a>, mostly <code>tcextract</code></li>
<li><a href="http://subtitleripper.sourceforge.net" title="DVD subtitle ripper.">subtitleripper</a>, for <code>subtitle2vobsub</code></li>
<li><a href="http://www.bunkus.org/videotools/ogmtools/index.html" title="Tools for manipulating OGG media streams.">ogmtools</a>, for <code>dvdxchap</code></li>
<li><a href="http://www.bunkus.org/videotools/mkvtoolnix" title="Tools for manipulating Matroska media streams.">mkvtoolnix</a>, for <code>mkvmerge</code></li>
<li><a href="http://www.mplayerhq.hu/" title="Multipurpose media player">mplayer</a>, for most of the stream dumping</li>
</ul>

<h4>Some Background</h4>

<p>I don't know all the details of how data is stored on DVDs, but here's a
rough overview.  The video on DVDs is encoded in either <a href="http://en.wikipedia.org/wiki/MPEG-2" title="MPEG-2, Part 2">MPEG-2</a> or
<a href="http://en.wikipedia.org/wiki/MPEG-1#Video" title="MPEG-1, Part 2">MPEG-1</a> with a variable bit rate.  The audio can be in raw
<a href="http://www.digitalpreservation.gov/formats/fdd/fdd000016.shtml" title="Pulse code modulation">PCM</a>, <a href="http://en.wikipedia.org/wiki/DTS_Coherent_Acoustics" title="DTS Coherent Acoustics">DTS</a>, <a href="http://en.wikipedia.org/wiki/MPEG-1_Audio_Layer_II" title="MPEG-1 Audio Layer II">MP2</a>, or <a href="http://en.wikipedia.org/wiki/Dolby_Digital" title="Dolby Digital">AC3</a>.  Most DVDs use AC3.
Not all DVD players support DTS.  Subtitles are stored as bitmaps with
associated timecodes governing when to show them on screen.</p>

<p>In a DVD, the basic unit of video is a <em>title</em>.  Each title consists of
one or more video streams, zero or more audio streams, zero or more
subtitle streams, and a list of timepoints to mark chapter boundaries.
The titles on a DVD are grouped together into <em>titlesets</em>.  All of the
data for each titleset is concatenated together into <a href="http://www.mpucoder.com/DVD/vobov.html" title="Video OBject">VOB</a> format and
then split into 1GB chunks.  The net result is that there's no one-to-one
correspondence between files on the DVD and individual titles.</p>

<p>If a title has more than one video stream, one will be the primary stream
while the others represent alternate angles.  Few DVDs have multiple
angles, so I'm not sure how the data for those works; all of the DVDs I've
seen just have a single video stream for each title.</p>

<p>Also note that not all of the titles on a DVD are the feature content.
Practically everything displayed by a DVD, including the splash image,
copyright warning, ads and trailers, and even the menus is a DVD title.</p>

<p>Any of a DVD's titles may be encrypted with <a href="http://www.dvdcca.org/css/" title="Content Scramble System">CSS</a>.  Either the DVD
player or the DVD drive must have a licensed CSS decryption key in order
to read the encrypted data.  Fortunately, CSS is somewhat weak, and most
Linux programs for accessing DVDs use <a href="http://www.videolan.org/developers/libdvdcss.html" title="CSS handling library.">libdvdcss</a> to bypass the
encryption.</p>

<h4>Ripping the DVD</h4>

<p>Ripping the DVD isn't strictly necessary, but it helps to have all of the
data on your hard drive for processing.  Even if you don't copy the videos
to your hard drive, you'll have to mount the DVD and use its IFO files;
I'll get to that later.</p>

<p>The easiest way to rip the DVD is with <code>dvdbackup</code>.  It creates a
directory for the DVD and then puts a VIDEO_TS subdirectory in the DVD
directory.  The VIDEO_TS directory contains all of the files in the DVD's
VIDEO_TS directory.  (Or, at least, it will if you use the <code>-M</code> option;
other options give more restricted results.)</p>

<pre><code>dest_dir=&lt;destination directory&gt;
dvd_name=&lt;DVD name&gt;
dvd_device=&lt;DVD device, e.g. /dev/dvd&gt;
dvdbackup -M -i &#036;dvd_device -o &#036;dest_dir -n &#036;dvd_name
</code></pre>

<p>In theory, you could also mount the DVD and just copy all of the files
over, but that has not worked well for me in the past, partly because of
CSS problems, but also partly because my drive is a little wonky.</p>

<p>You can also just take an image of the DVD with dd.  You'll need to
disable the CSS beforehand.  I've found that just running <code>xine</code> on the
DVD is sufficient.</p>

<pre><code>dest_dir=&lt;destination directory&gt;
dvd_name=&lt;DVD name&gt;
dvd_device=&lt;DVD device, e.g. /dev/dvd&gt;
xine dvd://
dd if=&#036;{dvd_device} bs=2048 conv=sync,noerror of=&#036;{dest_dir}/&#036;{dvd_name}.iso
</code></pre>

<p>If you have <a href="http://www.ivarch.com/programs/pv.shtml" title="Pipe Viewer">pv</a> installed, you can get a fancy progress bar.</p>

<pre><code>dest_dir=&lt;destination directory&gt;
dvd_name=&lt;DVD name&gt;
dvd_device=&lt;DVD device, e.g. /dev/dvd&gt;
xine dvd://
dd if=&#036;{dvd_device} bs=2048 conv=sync,noerror |
  pv -s &#036;(fdisk -l &#036;dvd_device |
          perl -nle 'm{^Disk '&#036;{dvd_device}': \d+ MB, (\d+) bytes&#036;} and print &#036;1') \
  &gt;&#036;{dest_dir}/&#036;{dvd_name}.iso
</code></pre>

<h4>Get Disc Info</h4>

<p>Whether you've ripped the DVD to disk or not, you need to see what's on
it.  Change into your working directory and run <code>lsdvd</code>.  (NB: From here
on out, unless otherwise noted, all commands that reference a DVD will
work equally well with a device (e.g. /dev/dvd), a disc image (like the
one created with <code>dd</code>), or a directory containing a VIDEO_TS directory
structure.)</p>

<pre><code>dvd=&lt;DVD device, image, or directory&gt;
lsdvd -a -n -c -s -v &#036;dvd &gt; contents
</code></pre>

<h4>Rip Each Title</h4>

<p>The first order of business is to get the title data off of the DVD.
<code>mplayer</code>'s <code>-dumpstream</code> option will pull just the given title's stream out
of the DVD.  (Note that the resulting file has the possibility of
exceeding 7GB in size; make sure your filesystem can handle files that
large.)</p>

<pre><code>title=&lt;title number, e.g. 01&gt;
dvd=&lt;DVD device, image, or directory&gt;
mplayer dvd://&#036;{title} -dvd-device &#036;dvd -dumpstream -dumpfile &#036;{title}.vob
</code></pre>

<p>The information about the title's chapters isn't in the VOB, so you'll
have to extract that separately with <code>dvdxchap</code>.  In my experience,
<code>dvdxchap</code> never gets useful information for the chapter names (perhaps
the DVD only contains the timepoints with no names associated), so you may
want to edit the resulting file to put in more meaningful names.  (Note
that <code>mplayer</code> will output chapter information if you use its <code>-identify</code>
option, but <code>dvdxchap</code> is more precise in its timing and also generates
the data in the format that <code>mkvmerge</code> wants.)</p>

<pre><code>dvdxchap -t &#036;title &#036;dvd &gt; &#036;{title}.chapters
</code></pre>

<p>I've seen DVDs where the TOC info as reported by <code>lsdvd</code> doesn't match the
actual streams in the titles, so it's good to check the track directly
with <code>mplayer</code>.  <code>mplayer</code> gives audio stream ids in decimal, not hex, so
the first audio stream will show as 128, not 0x80.  It numbers the
subtitle streams from zero, though, so you have to add 0x20 to the numbers
it gives to get the actual subtitle stream ids.</p>

<pre><code>mplayer -dvd-device &#036;dvd -vo null -ao null -frames 0 -v dvd://&#036;{title} 2&gt;&amp;1 | egrep '[as]id' &gt; &#036;{title}.streams
</code></pre>

<p>In an ideal world, <code>mkvmerge</code> would be able to operate directly on the
VOB, but when I tried that, it had problems demuxing the data and it died
halfway through.  So I'll use <code>mplayer</code> to pull out the individual
components.  Video first.</p>

<pre><code>mplayer &#036;{title}.vob -dumpvideo -dumpfile &#036;{title}.video.m2v
</code></pre>

<p>Next up are the audio tracks.  The VOB may contain more than one audio
track.  They should be labeled as to to their language, but check
<code>mplayer</code>'s info, not <code>lsdvd</code>'s.  <code>mplayer</code>'s info will also tell what
format the audio is in.  <code>mplayer</code> will accept the audio stream ids in
either decimal or hex.</p>

<pre><code>lang=&lt;language code&gt;
track=&lt;source audio track: 128, 129, 0x80, 0x81, etc.&gt;
format=&lt;extension for audio format; e.g. ac3, mp2&gt;
mplayer &#036;{title}.vob -aid &#036;audio_track -dumpaudio -dumpfile &#036;{title}.audio-&#036;{lang}.&#036;{format}
</code></pre>

<p>The VOB also contains subtitles, although most programs that query it
won't see them.  I tried using mplayer's <code>-dumpsub</code> command to get the
subtitles, but it didn't work properly for me.  So we'll just use the info
from <code>mplayer</code> to get the language and stream id for each stream, but then
use transcode's <code>tcextract</code> to actually pull the data out.  Remember to
add 0x20 to <code>mplayer</code>'s stream ids.  Some of the information for subtitles
is stored in .IFO files on the DVD.  Each titleset has its own .IFO file;
check the contents file to see what titleset contains the track and use
that titleset's .IFO file.  It will be in the <code>VIDEO_TS</code> directory, named
<code>VTS_&lt;titleset number&gt;_0.IFO</code>.</p>

<p>Matroska supports several subtitle formats, but VobSub is probably the
easiest to use, because it's a series of bitmaps, just like the DVD
subtitles.  If you're not happy with VobSub, you'll need to OCR each image
to get its text; there are instructions for doing so elsewhere on the
Internet.</p>

<pre><code>lang=&lt;language code&gt;
stream_id=&lt;id of the subtitle stream&gt;
ifo=&lt;IFO file; e.g. /path/to/VIDEO_TS/VTS_nn_0.IFO&gt;
&lt;&#036;{title}.vob tcextract -t vob -x ps1 -a &#036;stream_id &gt;&#036;{title}.subs-&#036;{lang}.raw
subtitle2vobsub -p &#036;{title}.subs-&#036;{lang}.raw -i &#036;ifo -o &#036;{title}.subs-&#036;{lang}
</code></pre>

<p>Finally, it's time to bring everything together with <code>mkvmerge</code>.  When I
use <code>&lt;title&gt;</code>, I mean the actual textual title for the video, like "Bob's
House of Horror 2" or whatever.  <code>&#036;{title}</code> still refers to the title
number on the DVD.</p>

<pre><code>mkvmerge -o &lt;final filename&gt; \
         --title &lt;title&gt; \
         --chapters &#036;{title}.chapters \
         &#036;{title}.video.mpg \
         &lt;audio clauses&gt; \
         &lt;subtitle clauses&gt;
</code></pre>

<p>For each audio file, you'll need a clause giving the file and its
language.  The first file you list on the command line will be the default
audio, unless you use <code>mkvmerge</code>'s <code>--default-track</code> option to change it.</p>

<pre><code>--language 0:&#036;{lang} &#036;{title}.audio-&#036;{lang}.ac3
</code></pre>

<p>Likewise, you'll need a clause for each subtitle file.  Since I generally
don't want any subtitles displayed by default, I set things so that there
isn't a default subtitle track.</p>

<pre><code>--language 0:&#036;{lang} --default-track 0:0 &#036;{title}.subs-&#036;{lang}.idx
</code></pre>

<p>And that should do it.  After a fair bit of disk-churning, you should have
a Matroska file containing all of the elements from the original DVD
title.  You can now delete all of your intermediate files and just keep
the MKV on your computer and the DVD in its box.</p>
]]></content:encoded>
  </item>

  <item rdf:about="http://aperiodic.net/phil/archives/Geekery/bluetooth-proximity.html">
    <title>Auto-locking My Computer When I Walk Away</title>
    <link>http://aperiodic.net/phil/archives/Geekery/bluetooth-proximity.html</link>
    <description>The other day, while I was wating for several GB to transfer over the
network at work, I finally got around to setting something that's been
dancing at the back of my mind for a while: computer-based proximity
detection using Bluetooth...</description>
    <dc:subject>/Geekery</dc:subject>
    <dc:creator>Phil Gregory</dc:creator>
    <dc:date>2008-05-29T14:13-04:00</dc:date>
    
    <content:encoded><![CDATA[<p>The other day, while I was wating for several GB to transfer over the
network at work, I finally got around to setting something that's been
dancing at the back of my mind for a while: computer-based proximity
detection using Bluetooth.</p>

<p>I have a <a href="http://www.palm.com/us/products/smartphones/treo650/" title="Palm Treo 650 Smartphone">Treo 650</a>.  It has Bluetooth.  I also have a <a href="http://www.targus.com/us/product_Details.asp?SKU=ACB10US" title="Targus USB Bluetooth® Adapter - ACB10US">USB
Bluetooth Adapter</a>.  I originally planned to carry the
bluetooth adapter around and hook it up to different computers whenever I
wanted to talk to the Treo, but I've only been using it at work, so I've
been leaving the adapter connected to my Linux computer at work.  The
thought occurred to me that I could use the Bluetooth adapter to see
whether my phone was nearby and do things based on that information.  At
least to start, I decided to have the computer lock itself when I wasn't
around.</p>

<p>I have the <a href="http://www.bluez.org/" title="Official Linux Bluetooth protocol stack">BlueZ</a> Bluetooth stack installed.  (On Debian, that's
the <code>bluez-utils</code> package.)  They include a <code>l2ping</code> program, but that
establishes a full Bluetooth connection with the device, which makes my
Treo turn on the screen, play a little sound, and show a pop-up dialog.
That's a little intrusive for something that I want checked several times
a minute.  Some people use <code>hcitool rssi</code> to find out the strength of the
phone's (or other device's) Bluetooth signal.  That also requires a full
Bluetooth connection.  I ended up using <code>hcitool name</code>, which returns the
name of the device if it's found and nothing if it's not.  More
importantly, it doesn't cause the Treo to do anything but silently send
its response, and it works even if the Treo screen is off.</p>

<p>So I now have a stupid little shell script that looks like this:</p>

<pre><code>#!/bin/sh

PHONE_ADDR=01:23:45:67:89:AB
PHONE_NAME="glamdring"
WAIT_TIME=15

while true; do
  if [ "&#036;(hcitool name &#036;PHONE_ADDR)" \!= "&#036;PHONE_NAME" ]; then
    xscreensaver-command -lock
  fi
  sleep &#036;WAIT_TIME
done
</code></pre>

<p>There are programs for Windows that do similar things.  Possibly one of
the simplest is <a href="http://members.lycos.co.uk/wuul/bluelock/readme.html">Blue Lock</a>, which is also open-source (and
written in Delphi).  I'm probably just going to write a simple Windows
program to listen on the network for a message from my Linux computer to
tell it to lock the screen.</p>
]]></content:encoded>
  </item>

  <item rdf:about="http://aperiodic.net/phil/archives/General/nfsn-hosted.html">
    <title>New Site Hosting</title>
    <link>http://aperiodic.net/phil/archives/General/nfsn-hosted.html</link>
    <description>In the interests of better site availability and less Comcast
AUP-breaking, I've finally gotten around to outsourcing my website
hosting...</description>
    <dc:subject>/General</dc:subject>
    <dc:creator>Phil Gregory</dc:creator>
    <dc:date>2008-05-02T14:48-04:00</dc:date>
    
    <content:encoded><![CDATA[<p>In the interests of better site availability and less Comcast
AUP-breaking, I've finally gotten around to outsourcing my website
hosting.  I'm currently at <a href="https://www.nearlyfreespeech.net/">NearlyFreeSpeech.net</a>, a webhost
committed to the twin goals of free speech and affordable web hosting.</p>

<p>How free is their speech?  Read their <a href="https://www.nearlyfreespeech.net/help/abuse">Abuse page</a>:</p>

<blockquote>
  <p>"A NearlyFreeSpeech.NET member site is defaming me or otherwise injuring
   me civilly."</p>
  
  <p>Please forward a copy of your legal finding from a court of competent
  jurisdiction to our contact address. If you have not yet obtained such a
  finding, a preliminary injunction or court order is also sufficient.</p>
  
  <p>If you are not able to obtain the above, you will need to work directly
  with the site operator to resolve your differences. We will have to fall
  back on our members' contractual assertion that the content they upload
  is legitimate and therefore we will not be able to get involved</p>
</blockquote>

<p>How affordable is their <a href="https://www.nearlyfreespeech.net/services/hosting.php">hosting</a>?  You pay only for the
bandwidth and storage that you actually use: $1 per gigabyte of bandwidth
and $0.01 per megabyte-month of storage.  (Plus the bandwidth cost goes
down the more you use.)</p>

<p>They support a variety of CGI scripting languages,
<a href="http://example.nfshost.com/versions.php">including</a> C, PHP, Perl, Python, and Ruby.  Oh, but also
Fortran, Tcl, <em>Lisp</em>, <em>Scheme</em>, <em>OCaml</em>, and <strong>Haskell</strong>.</p>

<p>We'll see how it goes, but I think I'll like it here.</p>
]]></content:encoded>
  </item>

  <item rdf:about="http://aperiodic.net/phil/archives/Geekery/java-reflection-noodling.html">
    <title>Java Reflection</title>
    <link>http://aperiodic.net/phil/archives/Geekery/java-reflection-noodling.html</link>
    <description>I'm only a few weeks into my Java class and I'm already annoyed
at the language...</description>
    <dc:subject>/Geekery</dc:subject>
    <dc:creator>Phil Gregory</dc:creator>
    <dc:date>2008-02-19T17:31-04:00</dc:date>
    
    <content:encoded><![CDATA[<p>I'm only a few weeks into <a href="http://www.umuc.edu/programs/undergrad/courses/cmiscat.shtml#cmis141">my Java class</a> and I'm already annoyed
at the language.  I'm completely willing to ascribe this to newbieness,
where I'm just not working with what the language gives me, but the
metaobject stuff in Java seems a bit painful.</p>

<p>I'm working on a project for the class where I have to accept input in
several different units of heat (BTUs, calories, and joules) and output
the measurement in joules.  I've made an abstract base class for the
various units and created concrete classes for each unit the program has
to read.  I'd like to just have a list of available classes and have my
program enumerate them automatically (rather than hardcoding the behavior
for each), but the way I would normally think about doing this is painful
in Java.</p>

<p>In Delphi, I'd do something like this:</p>

<pre><code>type
  THeatUnits = class
   public
    constructor Create(Value : Real); virtual;
    class function GetUnitsName : String; virtual; abstract;
    function ConvertToJoules : Real; virtual; abstract;
  end;

  THeatUnitsClass = class of THeatUnits;

  TBTUs = class(THeatUnits);
  TCalories = class(THeatUnits);
  TJoules = class(THeatUnits);

const
  availableUnits : array [1..3] of THeatUnitsClass
                 = (TBTUs, TCalories, TJoules);

...

procedure DoStuff(Index : Integer; Value : Real);
  var
    Units : THeatUnits;
begin
  Units := availableUnits[Index].Create(Value);
  // Now do things polymorphically with Units.
end;
</code></pre>

<p>Delphi's class types often seem like a quick hack to me, but they beat
what Java does in the same situation.  For one thing, there doesn't seem
to really be a class type in the same way that Delphi does it.  There are
instead objects of type <code>Class</code>.  As far as I can tell, the best way to get
one of those is to call <code>Class.forName("ClassName")</code>.  But the painful
part is that there's no specialization of class types at compile time, so
the Java code equivalent to my <code>Units := availableUnits[Index].Create(Value);</code>
above would be something like this:</p>

<pre><code>static final AVAILABLE_UNITS = new String[] ("BTUs", "Calories", "Joules");

public void doStuff(int index, double value) {
  Class unitClass = Class.forName(AVAILABLE_UNITS[index]);
  Constructor unitConstructor = unitClass.getConstructor(new Class[] (double.class));
  HeatUnits units = (HeatUnits)unitConstructor.newInstance(new Object[] (new Double(value)));
  // now do things polymorphically with units.
}
</code></pre>

<p>(Common Lisp, of course, would be more succinct than Delphi, because
everything is first-class; I would probably do something like this:</p>

<pre><code>(defclass heat-units () ())

(defgeneric get-units-name (unit-class))
(defgeneric convert-to-joules (unit-class))

(defclass btus (heat-units) ())
(defclass calories (heat-units) ())
(defclass joules (heat-units) ())

(defparameter +available-units+ #(btus calories joules))

(defun do-stuff (index value)
  (let ((units (make-instance (svref +available-units+ index)
                              :value value)))
    ;; Now do things polymorphically with units.
    ))
</code></pre>

<p>)</p>

<p>As I said, though, I think this is just an artifact of not thinking in
Java to the appropriate degree.  Most of Java's reflection stuff seems set
up to be useful at run-time while Delphi's run-time reflection is much
uglier than Java's.  And I think I'm going to approach my Java problem
from a different direction, with an enum and a factory method.  I was
still struck by how annoyingly wordy (and not entirely typesafe) my first
approach turned out to be in Java.</p>
]]></content:encoded>
  </item>

  <item rdf:about="http://aperiodic.net/phil/archives/Recipes/smakelijke-gerechten-pumpkin-pie.html">
    <title>Pumpkin Pie</title>
    <link>http://aperiodic.net/phil/archives/Recipes/smakelijke-gerechten-pumpkin-pie.html</link>
    <description>This is from smakelijke gerechten, a cookbook assembled by the
First Reformed Church in Oak Harbor, Washington in 1971...</description>
    <dc:subject>/Recipes</dc:subject>
    <dc:creator>Phil Gregory</dc:creator>
    <dc:date>2007-11-30T18:22-04:00</dc:date>
    
    <content:encoded><![CDATA[<p>This is from <i>smakelijke gerechten</i>, a cookbook assembled by the
First Reformed Church in Oak Harbor, Washington in 1971.  My grandmother's
sister belonged to the church, and the cookbook was a gift for my
grandmother's birthday.</p>

<ul>
<li>2 eggs, slightly beaten</li>
<li>1-3/4 cups pumpkin puree (1 can)</li>
<li>3/4 cup sugar</li>
<li>1/2 teaspoon salt</li>
<li>1 teaspoon cinnamon</li>
<li>1/2 teaspoon ground ginger</li>
<li>1/4 teaspoon ground cloves</li>
<li>1-2/3 cups evaporated milk (1 12-oz can)</li>
<li>1 9" pie crust, unbaked</li>
</ul>

<p>Preheat oven to 425°F.  Mix all ingredients.  Pour into pie crust.  Bake
at 425°F for 15 minutes, then reduce temperature to 350°F and bake for 45
more minutes or until a toothpick inserted in center comes out clean.</p>
]]></content:encoded>
  </item>

  <item rdf:about="http://aperiodic.net/phil/archives/Recipes/grandmas-potato-salad.html">
    <title>Grandma's Potato Salad</title>
    <link>http://aperiodic.net/phil/archives/Recipes/grandmas-potato-salad.html</link>
    <description>While I was down in Texas for Thanksgiving, I got my grandmother to give
me a couple of her recipes...</description>
    <dc:subject>/Recipes</dc:subject>
    <dc:creator>Phil Gregory</dc:creator>
    <dc:date>2007-11-27T11:08-04:00</dc:date>
    
    <content:encoded><![CDATA[<p>While I was down in Texas for Thanksgiving, I got my grandmother to give
me a couple of her recipes.  Unfortunately, she was so familiar with the
ingredients that she didn't have any measurements more specific than "'Til
it tastes right," so that's all I can provide at the moment.  I plan to
make the recipe a few more times myself and figure out what measurements
work well.</p>

<ul>
<li>3-4 lb. potatoes</li>
<li>4 eggs</li>
<li>1 medium white onion, diced</li>
<li>mayonnaise</li>
<li>pickle juice</li>
<li>curry powder</li>
<li>salt</li>
<li>pepper</li>
<li>yellow mustard</li>
<li>paprika</li>
</ul>

<p>Scrub and boil the potatoes.  Hard boil the eggs.  When the potatoes are
done, let them cool then peel them--the skins should rub right off.  Cut
the potatoes into roughly 1" pieces.  Dice the eggs.  Mix together
potatoes, eggs, and onion.</p>

<p>In a separate bowl, mix the mayonnaise and pickle juice until the mixture
is the right consistency.  Add the curry powder, salt, pepper, and mustard
to taste.</p>

<p>Mix the dressing with the potatoes, then sprinkle paprika over the salad.</p>
]]></content:encoded>
  </item>

  <item rdf:about="http://aperiodic.net/phil/archives/Recipes/grandmas-parker-house-rolls.html">
    <title>Grandma's Parker House Rolls</title>
    <link>http://aperiodic.net/phil/archives/Recipes/grandmas-parker-house-rolls.html</link>
    <description>While I was down in Texas for Thanksgiving, I got my grandmother to give
me a couple of her recipes...</description>
    <dc:subject>/Recipes</dc:subject>
    <dc:creator>Phil Gregory</dc:creator>
    <dc:date>2007-11-25T23:47-04:00</dc:date>
    
    <content:encoded><![CDATA[<p>While I was down in Texas for Thanksgiving, I got my grandmother to give
me a couple of her recipes.  She portions many of the ingredients by feel
or taste, though, so this recipe assumes some familiarity with yeast
breads.  I plan to make them a couple of times and get some more exact
measurements.</p>

<ul>
<li>2 cups milk</li>
<li>4-6 tablespoons sugar</li>
<li>1 teaspoon salt</li>
<li>8 tablespoons butter (one stick), divided</li>
<li>2 packets yeast</li>
<li>flour</li>
</ul>

<p>In a small saucepan, mix the milk with the sugar, salt, and 6 tablespoons
of butter.  Heat to just below a boil, then allow to cool to room
temperature.</p>

<p>Proof the yeast in some water with a little sugar.  Mix the yeast into the
milk, then add enough flour to get the right consistency.</p>

<p>Knead the bread, then allow it to rise until doubled in size, about an
hour.</p>

<p>Preheat the oven to 375°F.</p>

<p>Melt the remaining 2 tablespoons of butter.  Flatten the dough, then roll
it out to about 1/4" thick.  Cut circles in the dough with a biscuit
cutter or the opening of a glass.  For each circle, dip half of one side
in the butter, then fold it in half with the butter on the inside.  Place
the rolls on baking sheets and allow to raise until increased in size by
roughly 50%.</p>

<p>Bake at 375°F until done, roughly 8-10 minutes.</p>
]]></content:encoded>
  </item>


</rdf:RDF>
