Google Bookmark のエクスポート HTML からリンクのリストを作る

Google Bookmark でエクスポートした HTML は、DL/DT/DD を使った定義リストになっている。DT 要素に A 要素とブックマークタイトルが入っているので、これを LI に変換する。ついでに、DT タグには終わりタグが入れられていないので、これもちゃんと閉じタグを入れるように修正する。

#!/bin/sh

sed -e 's/DT/li/g' \
    -ne '/^<li><A/s:$:</li>:p' \
    GoogleBookmarks.html >tmptmptmp.html

echo '<ul>' >GoogleBookmarks_valid.html
cat tmptmptmp.html >>GoogleBookmarks_valid.html
echo '</ul>' >>GoogleBookmarks_valid.html

rm tmptmptmp.html

セルエディタと型

JTable がデフォルトで用意するセルエディタは, 実は入力チェックつきだった. デフォルトで対応しているのは, String/Integer/Boolean の 3 つ.

TableModel の getClumnClass(int) で String/Integer/Boolean のいずれかの型を返すと, その型に応じた入力チェック付きのエディタを選んでくれる. 入力チェックにひっかかると, セルのボーダーが赤くなる.

以下はすべてのカラムを Integer にするサンプル。ついでにエディタから編集後の値を設定されるときに, その値の型を出力したりしてる。

DefaultTableModel model = new DefaultTableModel() {
    @Override
    public boolean isCellEditable(int r, int c) {
         return true;
    }
    @Override
    public Class<?> getColumnClass(int c) {
         return Integer.class;
    }
    @Override
    public void setValueAt(Object o, int r, int c) {
         super.setValueAt(o, r, c);
         System.out.printf("[%d,%d] = %s%n", r, c, o.getClass());
    }
};

入力チェックのみならず, setValueAt に渡される値も, そのカラムの型に変換してから渡してくれる.

変換や入力チェックをどうやってるのかなと調べてみたところ, 単純にコンストラクタ引数に文字列を渡して, インスタンス化できればOK, できなければ入力エラーとしていた. 単純明快.

メソッド最初の引数のインデント位置

cc-mode デフォルトの, メソッド最初の引数のインデント位置が気に入らない.

void longLongNameMethod(
                        Object hoge,
                        Object fuga) {
}

2 つ目以降はこんな感じでいいんだけど, 1 つ目は Eclipse っぽい感じになってほしい.

void longLongNameMethod(
        Object hoge,
        Object fuga) {
}

とりあえず java のときだけこういう風にする.

(add-hook 'java-mode-hook
          (lambda ()
            (c-set-offset 'arglist-intro '++)))

pkgsrc で emacs アプリのインストールに失敗する

emacs アプリと呼ぶのが正しいかどうかはさておき.
inputmethod/skk とか, devel/flim とかのインストールが, ことごとく失敗していた. エラーメッセージを見るに, ロードパス絡みっぽい.

どうやら editors/emacs の modules.mk に問題があるらしく, ちょこちょこと修正した. 以下はその cvs diff -u

Index: modules.mk
===================================================================
RCS file: /cvsroot/pkgsrc/editors/emacs/modules.mk,v
retrieving revision 1.11
diff -u -r1.11 modules.mk
--- modules.mk	23 Aug 2009 18:14:38 -0000	1.11
+++ modules.mk	12 Sep 2009 04:11:58 -0000
@@ -321,7 +321,8 @@
 .endif

 _EMACS_PLIST_SUBST+=	EMACS_FLAVOR=${EMACS_FLAVOR:Q}
-_EMACS_PLIST_SUBST+=	EMACS_VERSION=${_EMACS_VERSION_MAJOR:Q}.${_EMACS_VERSION_MINOR:Q}
+#_EMACS_PLIST_SUBST+=	EMACS_VERSION=${_EMACS_VERSION_MAJOR:Q}.${_EMACS_VERSION_MINOR:Q}
+_EMACS_PLIST_SUBST+=	EMACS_VERSION=${_EMACS_VERSION_MAJOR:Q}.${_EMACS_VERSION_MINOR:Q}.${_EMACS_VERSION_MICRO:Q}
 _EMACS_PLIST_SUBST+=	EMACS_ETCPREFIX=${EMACS_ETCPREFIX:C|^${PREFIX}/||}
 _EMACS_PLIST_SUBST+=	EMACS_INFOPREFIX=${EMACS_INFOPREFIX:C|^${PREFIX}/||}
 _EMACS_PLIST_SUBST+=	EMACS_LISPPREFIX=${EMACS_LISPPREFIX:C|^${PREFIX}/||}
@@ -341,7 +342,8 @@
 _EMACS_VERSION_DIR!=	\
 	(${PKG_INFO} -e emacs || ${ECHO} "") |				\
 	${SED} -e 's/emacs-//' |					\
-	${SED} -e 's/\.[0-9]\{8,\}//'
+	${SED} -e 's/nb.*//'
+#	${SED} -e 's/\.[0-9]\{8,\}//'
 ALL_ENV+=	EMACSLOADPATH=${_EMACS_DIR}/${_EMACS_VERSION_DIR}/lisp:${_EMACS_DIR}/site-lisp
 .include	"${_EMACS_PKGDIR}/buildlink3.mk"
 .endif

マイナーバージョン未満のバージョンをうまく扱えていなかった模様.

バイナリクロック

http://gauc.no-ip.org/awk-users-jp/blis.cgi/DoukakuAWK_179 のお題。時刻の扱いかたを把握するのに時間がかかった。

import Data.Bits (testBit, shiftR)
import Data.Time (localTimeOfDay, utcToLocalTime, getCurrentTime,
                  todHour, todMin, getCurrentTimeZone)

-- | 2 進数を表現するための型
data Binary
    = Zero
    | One

-- | 10 進数を 2 進数表現に変換する.
--
-- 戻り値のリストは, 先頭が最上位桁で, 末尾が最下位桁となる.
dec2Bin :: Int      -- ^ 10 進数
        -> [Binary] -- ^ 2 進数表現
dec2Bin n = iter n []
  where iter 0 bs = bs
        -- 0 番目のビットが立っている場合は 1, そうでなければ 0
        iter n bs | testBit n 0 = iter (next n) (One:bs)
                  | otherwise   = iter (next n) (Zero:bs)
        -- 次のビットを調べるため, 数値を右シフトする.
        next n = shiftR n 1

instance Show Binary where
    show Zero = "_"
    show One = "#"

-- | 時間と分の組.
data HourMin = HourMin { hour :: Int
                       , minute :: Int
                       }

-- | 現在時刻の時間と分を返す.
currentHM :: IO HourMin
currentHM = do utc <- getCurrentTime
               zone <- getCurrentTimeZone
               return $ toHourMin
                      $ localTimeOfDay
                      $ utcToLocalTime zone utc
  where toHourMin tod = HourMin (todHour tod) (todMin tod)

-- | 改行を入れずに print する.
print' :: (Show a) => a -> IO ()
print' = putStr . show

main = do ct <- currentHM
          let hourBin = dec2Bin (hour ct)
              minBin = dec2Bin (minute ct)
          mapM_ print' hourBin >> eol
          mapM_ print' minBin >> eol
  where
        eol = putStrLn ""

Apache Ivy を試す - その2

ライブラリプロジェクトに対して、そのテストプロジェクトが依存するという関係を Ivy で解決する。独自のロガーライブラリを例にとる。logging が本体プロジェクト、logging-test がテストプロジェクト。

logging の build.xml

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

<project name="logger" basedir="." default="compile"
         xmlns:ivy="antlib:org.apache.ivy.ant">

    <property name="revision" value="0.1.0" />

    <property name="src.d" value="src" />
    <property name="bin.d" value="bin" />
    <property name="lib.d" value="lib" />
    <property name="dist.d" value="dist" />

    <macrodef name="m_compile">
        <attribute name="excludes" default="" />
        <attribute name="includes" default="" />

        <sequential>
            <javac srcdir="${src.d}"
                   destdir="${bin.d}"
                   excludes="@{excludes}"
                   includes="@{includes}"
                   debug="on">
                <compilerarg value="-Xlint" />
            </javac>
        </sequential>
    </macrodef>


    <target name="init" description=": initialize project">
        <mkdir dir="${bin.d}" />
        <mkdir dir="${src.d}" />
        <mkdir dir="${lib.d}" />
        <mkdir dir="${dist.d}" />
    </target>

    <target name="resolve" description=": resolve module dependencies">
        <ivy:retrieve />
    </target>

    <target name="compile" depends="init, resolve"
            description=": compile application classes">
        <m_compile />
    </target>

    <target name="clean" description=": clean the project">
        <delete includeemptydirs="true">
            <fileset dir="${basedir}">
                <exclude name="src/**" />
                <exclude name="build.xml" />
                <exclude name="ivy.xml" />
                <exclude name="ivysettings.xml" />
            </fileset>
        </delete>
    </target>

    <target name="jar" depends="compile"
            description=": create jar">
        <jar destfile="${dist.d}/logging.jar"
             basedir="${bin.d}" />
    </target>

    <target name="publish" depends="jar"
            description=": publish this module">
        <ivy:publish
            artifactspattern="${dist.d}/[artifact].[ext]"
            resolver="local"
            pubrevision="${revision}"
            overwrite="true" />
    </target>

</project>

こちらで重要なのは publish target。artifactspattern にマッチする jar ファイル (厳密には (たぶん) jar に限らない、後述) を、resolver に publish してもらうという感じ。

logging の ivy.xml

<ivy-module version="2.0">

    <info organisation="mylib" module="logging" revision="0.1.0" />

    <publications>
    	<artifact />
    </publications>

</ivy-module>

publications が重要。ここではデフォルト設定の artifact だけを定義してるけど、ここで logging プロジェクトに含まれる artifact を色々書くこともできる。artifact には type と ext という属性がある。上で「jar に限らない」と書いたのはこれが関係してて、type や ext に jar 以外を指定すると、jar 以外から成る artifact (?) とすることができる。

logging-test の build.xml

上のとほとんど同じ。ivy:cacheclean 用の target とか jar へのクラスパスを追加してるだけ。

logging-test の ivy.xml

<ivy-module version="2.0">

    <info organisation="mylib" module="logging-test" />

    <dependencies>
        <dependency org="junit" name="junit" rev="4.5" />
        <dependency org="mylib" name="logging" rev="0.1.0" />
    </dependencies>

</ivy-module>

junit-4.5 と logging-0.1.0 に依存してますよーという定義。org やら name は logging artifact のものと一致している必要がある。

初め、jar target の属性が間違っていて、クラスファイルのない jar ファイルを publish してしまっていた。その状態でテストプロジェクトで resolve してコンパイルしたら、当然だけど失敗した。
その後 jar targe を修正してからクラスファイルがある jar ファイルを publish し、テストプロジェクトで resolve しても空の jar が使われ続けていた。

これは Ivy のキャッシュが残っているせいで、一度解決した際に手に入れた jar ファイル (クラスファイルのない jar ファイル) を使い続けていたのが原因だった。一度 ivy:cleancache してやると解決した。これに気づくのに時間がかかった。

この手のツールは、設定同士のつながりが分かってきてはじめて、理解が進む感じがした。