Having played with threading, I thought I’d have a go with Java integration. Swing was the obvious target, and a simple quote fetcher was the result.

For simplicity, I stuck with URLConnection, but there are a number of Clojure libraries to solve the HTTP problem. All of them are somewhat nicer to deal with.

I also completely failed to get apply working with addItem on the ComboBoxes – I’m assuming PEBKAC at present, given my limited knowledge of the integration.

Still, the Swing integration was surprisingly easy to do – I had envisioned nightmares here, given the variation in style, but it proved not half as horrid as expected.

(import
  '(java.net URL URLConnection)
  '(java.io BufferedReader InputStreamReader)
  '(javax.swing JFrame JLabel JTextField JButton JComboBox BorderFactory)
  '(java.awt.event ActionListener)
  '(java.awt GridBagLayout GridBagConstraints Insets)
  )

(def currencies ["EUR" "GBP" "JPY" "USD"])

(defn build-url [from-code to-code]
  (str "http://quote.yahoo.com/d/quotes.csv?s="
    from-code to-code "=X&f=sl1d1t1c1ohgv&e=.csv")
  )

(defn read-file [input-stream]
  (loop [line (.readLine input-stream)
         content []]
    (if (= line nil)
      content
      (recur (.readLine input-stream) (concat content [line]))
      )
    )
  )

(defn get-quote [quote-string]
  (nth (.split quote-string ",") 1))

(defn read-url [url]
  (let [connection (.openConnection (URL. url))]
    (read-file (BufferedReader. (InputStreamReader.
      (.getInputStream connection)))))
  )

(defn set-currencies [combo-box]
  (loop [current (first currencies)
         remainder (rest currencies)]
    (if (not (= nil current))
      (do
        (.addItem combo-box (str current))
        (recur (first remainder) (rest remainder))
        )
      )
    )
  )

(defn quote-fetcher []
  (let [frame (JFrame. "Quote Fetcher")
        button (JButton. "Get Quote")
        from-label (JLabel. "From:")
        from-field (JComboBox.)
        to-label (JLabel. "To:")
        to-field (JComboBox.)
        value-label (JLabel. "Result:")
        value-field (JTextField.)]

    (.addActionListener button
      (proxy [ActionListener] []
        (actionPerformed [e]
          (let [from-code (.getSelectedItem from-field)
                to-code (.getSelectedItem to-field)]
            (.setText value-field
              (get-quote (first (read-url
                (build-url from-code to-code)))))
            )
          )
        )
      )

    (.setEditable value-field false)

    (set-currencies from-field)
    (set-currencies to-field)

    (.setBorder (.getContentPane frame)
      (BorderFactory/createEmptyBorder 10 10 10 10))

    (doto frame
      (.setLayout (GridBagLayout.))
      (.add from-label (GridBagConstraints. 0 0 1 1 0.0 0.0
        (GridBagConstraints/EAST) (GridBagConstraints/NONE) (Insets. 4 4 4 4) 0 0))
      (.add from-field (GridBagConstraints. 1 0 1 1 1.0 0.0
        (GridBagConstraints/EAST) (GridBagConstraints/HORIZONTAL) (Insets. 4 4 4 4) 0 0))
      (.add to-label (GridBagConstraints. 0 1 1 1 0.0 0.0
        (GridBagConstraints/EAST) (GridBagConstraints/NONE) (Insets. 4 4 4 4) 0 0))
      (.add to-field (GridBagConstraints. 1 1 1 1 1.0 0.0
        (GridBagConstraints/EAST) (GridBagConstraints/HORIZONTAL) (Insets. 4 4 4 4) 0 0))
      (.add button (GridBagConstraints. 0 2 2 1 0.0 0.0
        (GridBagConstraints/EAST) (GridBagConstraints/NONE) (Insets. 4 4 4 4) 0 0))
      (.add value-label (GridBagConstraints. 0 3 1 1 0.0 0.0
        (GridBagConstraints/EAST) (GridBagConstraints/NONE) (Insets. 4 4 4 4) 0 0))
      (.add value-field (GridBagConstraints. 1 3 1 1 1.0 1.0
        (GridBagConstraints/EAST) (GridBagConstraints/HORIZONTAL) (Insets. 4 4 4 4) 0 0))
      (.setSize 300 176)
      (.setVisible true)
      )
    )
  )

(quote-fetcher)