数値と、Excel の列番号風のアルファベット表記の相互変換 in JavaScript

こんな感じでいいんだろうか。

var ExcelUtils = (function() {
  var RADIX = 26;
  var A = 'A'.charCodeAt(0);

  return {
    to_numeric_col_index: function(string_col_index) {
      var s = string_col_index.toUpperCase();
      var n = 0;
      for (var i=0,len=s.length; i<len; i++) {
        n = (n * RADIX) + (s.charCodeAt(i) - A + 1);
      }
      return n;
    },

    to_string_col_index: function(numeric_col_index) {
      var n = numeric_col_index;
      var s = "";
      while (n >= 1) {
        n--;
        s = String.fromCharCode(A + (n % RADIX)) + s;
        n = Math.floor(n / RADIX);
      }
      return s;
    }
  };
})();

ExcelUtils.to_numeric_col_index("A");   // => 1
ExcelUtils.to_numeric_col_index("Z");   // => 26
ExcelUtils.to_numeric_col_index("AA");  // => 27
ExcelUtils.to_numeric_col_index("ZZ");  // => 702
ExcelUtils.to_numeric_col_index("AAA"); // => 703
ExcelUtils.to_string_col_index(1);      // => "A"
ExcelUtils.to_string_col_index(26);     // => "Z"
ExcelUtils.to_string_col_index(27);     // => "AA"
ExcelUtils.to_string_col_index(702);    // => "ZZ"
ExcelUtils.to_string_col_index(703);    // => "AAA"

JScriptExcelファイルのパースをしたりする場合に役に立つ可能性もなくは無い?

久々の konozama

体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践」で久々に konozama 発動。特典などが特に無い普通の本では初めて。すごい人気だなぁ……。

Amazon.co.jpをご利用いただき、ありがとうございます。

誠に申し訳ありませんが、ご注文いただいた以下の商品の入荷遅延により、お届け予定日を変更させていただきました。

当初予定していたお届け予定日までにお客様のご注文商品をお届けできなかったことをお詫びいたします。

現時点での最新のお届け予定日は以下のとおりです。

徳丸 浩 "体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践"
お届け予定日: 2011-03-10 - 2011-03-16

商品は引き続き入荷できるよう手配しますが、万が一、仕入先から入荷の見込みがないことが判明した場合、またはご注文数が入荷数を上回った場合、
やむを得ずご注文をキャンセルさせていただくこともありますので、ご了承ください。

サーバサイドでProcessingを動かす

こちら Processing+POIをエクセルグラフウィザードの代替として使う - Object Design に触発されて、Webアプリケーションでも(サーバサイドで) Processing を使って情報視覚化ができないか試してみた。

まずは、Processing をJava Servlet上で動かして画像を生成するところまで*1

とりあえず、Processing の本体であるアプレット(PApplet)の初期化、PAppletで直接サポートしていない生イメージの返却を行うユーティリティを作ってみる。

package example.processing;

import java.awt.image.BufferedImage;

import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PGraphics;

public final class ProcessingUtils {
    /**
     * callback内で描画した内容をイメージとして返却します。
     */
    public static BufferedImage drawImage(ProcessingCallback callback) {
        PApplet applet = new PApplet();
        applet.init();

        // 初期化までタイムラグがあるので待つ
        // (PApplet.main メソッドのソース参照)
        while (applet.defaultSize && !applet.finished) {
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        // ユーザ指定の描画
        callback.draw(applet);

        // BufferedImage に変換
        // PImage#save メソッドのソース参照
        PGraphics g = applet.g;
        g.loadPixels();
        BufferedImage image = new BufferedImage(g.width, g.height,
                (g.format == PConstants.ARGB) ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
        image.setRGB(0, 0, g.width, g.height, g.pixels, 0, g.width);

        // いちおう、グラフィックの廃棄
        applet.destroy();

        return image;
    }
}

上の drawImage() の引数として使うコールバック用のインタフェース。Processing の機能を使いたいユーザは、このインタフェースを実装し、初期化や廃棄処理を除いた純粋な描画処理だけを draw() メソッドに定義する。

package example.processing;

import processing.core.PApplet;

public interface ProcessingCallback {
    void draw(PApplet pa);
}

上のような機構を利用して、楕円描画サーブレットを作成してみる。

package example.processing;

import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import processing.core.PApplet;

@SuppressWarnings("serial")
@WebServlet(name="Ellipse", urlPatterns={"/ellipse.png"})
public class EllipseServlet extends HttpServlet {

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse res) {
        BufferedImage image = ProcessingUtils.drawImage(new ProcessingCallback() {
            /**
             * 具体的な描画内容のみを定義
             */
            @Override
            public void draw(PApplet pa) {
                pa.size(200, 100);
                pa.background(225);
                pa.smooth();
                pa.fill(180, 230, 255);
                pa.ellipse(pa.width/2, pa.height/2, pa.width/2, pa.height/2);
                pa.fill(180, 0, 0);
                pa.text("テストです", 10, 10);
            }
        });

        res.setContentType("image/png");
        try {
            // PNGに変換して出力ストリームに流す
            ImageIO.write(image, "png", res.getOutputStream());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

上記をWebアプリケーションとして、たとえばローカルホストの /processing にデプロイする。*2

デプロイ後、http://localhost:8080/processing/ellipse.png にアクセスすると楕円画像が表示される。

TODO

  • PAppletを使わず、PGraphicsを直接使う(ちょっと試したけどなぜか画像が表示されなくなったため一時断念)
  • 今回はWindows上で行ったので問題なかったが、java.awt.headless=true の純粋なサーバ環境でもうまく動作するか?
  • PApplet生成の負荷は、毎回生成して問題ない程度なのかどうか。リソースリークはないか? JDBC DataSourceのようにプーリングを行う必要はあるか?
  • OpenGL や PDF 等、デフォルトのJava2D以外のレンダラを使えるようにする

*1:Processing.js を使ったほうが早いしサーバリソースにも優しいけど、まあ実験として。

*2:サンプルではServlet 3.0 の仕様を使っているので、Tomcatなら7以降が必要

プログラミング関連の発音

普段、周りにプログラミング関係のことを話す人が少ないので、それ系の単語の正しい or 慣用的に主流の発音が分からなくて困る。

char

「チャー」「キャル」「キャラクタ」など日本でもいろいろ言われていると思われるが、海外でも迷う人はいるらしい。

http://stackoverflow.com/questions/404351/char-vs-char-how-do-you-pronounce-it

上のを見ると、StackOverflowでは「チャー」が人気のようで。

dir

心の中では「ディア」と読んでいるんだけど、何となく器用な読み方っぽい恥ずかしさがあるため口に出すときは略さず「ディレクトリコマンド」。chdir も「チェンジディレクトリ」。rmdirは…「アールエムディレクトリ」。ここらへんになってくると自分でもよくわからなくなっている。

他の読みとしては「ディル」と読んでいる人を見たことがある。どこかのバンドのことかと思ったよ。

これは検索しても主流的読み方がわからなかった。

Maven

日本語版Wikipedia Apache Maven - Wikipedia に「メイヴァン/メイヴィン」とか書かれてしまっている。勉強会とか行かないので知らないが、もしかして日本のコミュニティではこれが主流なんだろうか?

そりゃ確かに発音記号を無理やり日本語にしたら一番近いのは「ァ」かもしれないけど、日本人の耳からすると普通は「メイヴン」にしか聞こえないし不自然だと思う。語尾が同じ "heaven" を「ヘヴァン」なんて普通言わないし。tax haven だって raven だってそうだ。

ということで、「メイブン」をお勧めします。まあ、「Twitterが何故かツイッター」みたいに、日本ではそう発音することに決まってしまった、となったら仕方ないけど*1Maven がそこまでメジャーになることはないでしょう。、

SQLite

SQLite - Wikipedia を見ると、「エスキューエ(ル)ライト」「シークエ(ル)ライト」*2の両方が書いてある。わたしは何となく「エスキューライト」と言っていたんだけどこれはダメっぽい…。

なぜ2つ併記されているかというと、これ以上ない根拠と言える D. Richard Hipp 氏自身のプレゼンでの発音からして、二種類の発音を使っているから。なので、公式に「どっちでもいい」ということに…

sudo

普段使っている「スードゥー」でよさそうだ。

sudo - Wikipedia

でも、"su" は相変わらず迷う。"sudo" がスードゥーなら "su" は「スー」のはずだけど、それだと何のことだかわからないので「エスユー」にしている。ただ、このダブルスタンダードは何となく心にしこりが残らないでもない。

/usr

なんとなく見た目の印象だけで「ユーザー」と読んでしまっているけど、"user" とはまったく関係なく "Unix System Resources" の略なので「ユーエスアール」の方がいいんだろうか…
Filesystem Hierarchy Standard - Wikipedia
ファイルシステムでいうと他に /etc も困る。自分の周りでは「エトセトラ」とか、それを略して「エトセ」とか言われているけど。

WSDL

ずっと「ダブリューエスディーエル」だと思っていたら、なんと「ウィスダル」が正しいらしい。地味に最近で一番の衝撃。
Web Services Description Language - Wikipedia

関連用語で、"UDDI" も「ユーディーディーアイ」だと思っていたら「ユーディー」らしい。どういう理屈だ。
Web Services Discovery - Wikipedia

なんか HTML=「ハトムル」と読んでいた人々と同じ臭いがしないでもない。

その他

プログラミング関係に限らないけど、数字を言うときに、英語で言うか日本語で言うかにとっても迷う。

個人的には、名前の一部であることを強く感じる場合は英語、単なるバージョン番号とかは日本語だけど。

たとえば、"HTML5" の5はバージョン番号というより、今までのHTMLとは別の概念っぽく感じるので「エイチティーエムエルファイブ」だけど、"HTML4.01" の4.01は単なるバージョン番号っぽく感じるので「エイチティーエムエル よんてんぜろいち」とか。この辺のダブルスタンダードも何となく心にしこりが残らないでもない。

結論

コマンドとかプログラムとかのオリジナル開発者の人は、ホームページに発音を書いてください。困ります。

*1:ツイッター」は日本人にはそのほうが言いやすいし「twist⇒ツイスト」等の前例があるけど、「メイヴァン」は言いやすくもなく、凝る意味がまるでない

*2:(ル)と括弧つきなのは、Lが二つ続いている「エスキュエッライト」みたいな発音は日本人的には「ル」が有るようにも無いようにも聞こえるため

KaoriYa Vim + MRUプラグインでのエラー

香り屋 — KaoriYaで配布されているVim 7.3を最近使い出したんだけど、「最近使ったファイルを開く」機能が欲しくて mru.vim をインストール(vimインストールフォルダ/runtime/plugin/ 以下に mru.vim をコピー)したら、以下のようなエラーが出るようになってしまった。

<Vimインストールフォルダ>\runtime\menu.vim 
の処理中にエラーが検出されました:

行  122:

E329: "&Print" というメニューはありません

runtime/menu.vim よりも前に mru.vim がロードされてしまう(?)のが原因っぽいけど、ロード順の制御の方法がよくわからないので、あとから読み込むようにするために mru.vim ファイルを runtime/plugin/ から plugins/ 以下に移動して、%USERPROFILE%/.gvimrc に*1

source <Vimインストールフォルダ>/plugins/mru.vim

と書いたら直った。

*1:%USERPROFILE% はWindowsユーザのホームディレクトリ。コマンドプロンプトで echo %USERPROFILE% と打てば場所がわかる

Windows上のJavaでファイルを上書きリネームする

Javaでファイルの移動を行う File#renameTo メソッドは移動先にファイルが存在する場合にどういった動作をするかについて規定されておらず、プラットフォーム依存になっている。

そのため、UNIX系OSでは概ね他の言語のrename と同じように「ファイルが存在したら上書きする」という動作になるのに、Windowsでは「ファイルが存在すると移動失敗」になる、というクロスプラットフォームが聞いてあきれる状態になっており、しかもJavaでは他にファイルの「移動」ができる手段はなく、Javaの開発元にも対応する意思はない*1

こういう場合は、あきらめてさっさとネイティブコードを使ってしまえばいい。

Javaでネイティブコードの呼び出しが面倒だったのは昔の話で、今はJNA(Java Native Access)という素晴らしいライブラリがあり、.NETのP/Invoke並に簡単にネイディブコードを呼び出すことができる。

ということで、JNAを使い、Windows上でKernel32.dll の MoveFileEx 関数を呼び出して「上書きrename」するコード(エラーメッセージ表示付き)を書いてみる。

jarファイルのダウンロード

最新版の jna.jar と platform.jar をJNAのページからダウンロードしてクラスパス上に置く。

Mavenを使う場合は、pom.xml の中に以下を追加。

  <repositories>
    <repository>
      <id>Java.net Maven2 repository</id>
      <url>http://download.java.net/maven/2/</url>
    </repository>
  </repositories>

  <dependencies>
    <dependency>
      <groupId>net.java.dev.jna</groupId>
      <artifactId>jna</artifactId>
      <version>3.2.7</version>
    </dependency>

    <dependency>
      <groupId>net.java.dev.jna</groupId>
      <artifactId>platform</artifactId>
      <version>3.2.7</version>
    </dependency>
  </dependencies>

ただし、platform.jar はリポジトリにないので、JNAのページから手動でダウンロードした上で、コマンドプロンプトから platform.jar を置いたフォルダに移動し、以下のようなコマンドでインストールする必要がある*2

mvn install:install-file ^
  -DgroupId=net.java.dev.jna -DartifactId=platform ^
  -DgeneratePom=true -DcreateChecksum=true -Dpackaging=jar ^
  -Dfile=platform.jar -Dversion=3.2.7

Javaソースを書く

package example;

import static example.MoveFile.Kernel32.*;

import com.sun.jna.Native;
import com.sun.jna.platform.win32.Kernel32Util;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;

public class MoveFile {
    // 必要な関数を定義した interface を作成
    public interface Kernel32 extends StdCallLibrary {
        // 定数は Platform SDK などに入っているヘッダを参照
        // pinvoke.net に載っている場合もある
        //   http://www.pinvoke.net/default.aspx/Enums/MoveFileFlags.html
        int MOVEFILE_REPLACE_EXISTING           = 0x00000001;
        int MOVEFILE_COPY_ALLOWED               = 0x00000002;
        int MOVEFILE_WRITE_THROUGH              = 0x00000008;

        boolean MoveFileEx(
                String lpExistingFileName, String lpNewFileName, int dwFlags);
    }

    public static void main(String[] args) {
        // - kernel32.dll をロード
        // - interface 中で定数として定義してもいい
        // - UNICODE_OPTIONS を指定すると、以下のような動作になる
        //    - Unicode版の関数 "MoveFileExW" が選択される
        //    - JavaのStringが、WCHAR に変換される
        Kernel32 kernel32 = (Kernel32) Native.loadLibrary(
                    "kernel32", Kernel32.class, W32APIOptions.UNICODE_OPTIONS);

        if (kernel32.MoveFileEx(args[0], args[1],
                    MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH)) {
            System.out.println("OK");
        } else {
            System.err.println(
                    // GetLastError() のエラーコードに対応するメッセージを取得
                    Kernel32Util.formatMessageFromLastErrorCode(Native.getLastError()));
        }
    }
}

これだけ。

テスト

> echo 1 > D:\tmp\1.txt
> echo 2 > D:\tmp\2.txt

> dir /b D:\tmp
1.txt
2.txt

> mvn -q exec:java -Dexec.mainClass=example.MoveFile -Dexec.args="D:\tmp\1.txt D:\tmp\2.txt"
OK

> dir /b D:\tmp
2.txt

> mvn -q exec:java -Dexec.mainClass=example.MoveFile -Dexec.args="D:\tmp\1.txt D:\tmp\2.txt"
指定されたファイルが見つかりません。

*1:追記:JDK7ではできるようになるらしい http://download.java.net/jdk7/docs/api/java/nio/file/Files.html

*2:-Dversion= で指定するバージョンはダウンロードしたものに合わせること

SNBinderを試してみる

Life is beautiful: JavaScript HTMLテンプレートエンジン SNBinder 公開 で紹介されていたSNBinderが面白そうなので試してみる。

サンプルの内容

ブログっぽい何か。

サーバ側は、手元の環境が整えやすいのでJSPだけど、そこはどうでもいい。

/snbinder/index.html

HTML。左カラムに「ようこそ」メッセージ、右カラムにブログ記事の一覧が表示される。

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>test</title>
<style type="text/css">
#navi { float: left; width: 20%; padding: 10px; }
#content { margin-left: 20%; }
.entry { border: 1px black solid; padding: 10px; margin: 10px; }
</style>
</head>
<body>
<div id="navi">
<div id="navi-head">Navigation:</div>
<div id="greeting"></div>
</div>
<div id="content">
<div id="content-head">Recent blog entries:</div>
<div id="entries"></div>
</div>
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script type="text/javascript" src="/snbinder/assets/snbinder-0.5.3-modified.js"></script>
<script type="text/javascript" src="/snbinder/assets/index.js"></script>
</html>

snbinder-0.5.3-modified になっている点については後述。

/snbinder/user/info.jsp

ユーザ情報のJSONデータを返すだけ。

<%@ page language="java" contentType="application/json; charset=utf-8"
    pageEncoding="utf-8"%>
{"name": "Leonardo da Vinci"}
/snbinder/blog/entries.jsp

ブログエントリ一覧のJSONデータを返す。

<%@ page language="java" contentType="application/json; charset=utf-8"
    pageEncoding="utf-8"%>
[
  {
    "id": "2",
    "content": "I know that many will call this useless work.",
    "posted_at": "2011/01/23 23:59:59"
  },
  {
    "id": "1",
    "content": "Hello, world!",
    "posted_at": "2011/01/22 00:00:00"
  }
]
/snbinder/assets/template.htm

テンプレート。

{%}greeting{%}
<p>Hello, <b>$(.name)!</b></p>

{%}blog_entry{%}
<div class="entry">
<div class="index">entry #<b>$(.id)</b></div>
<div class="text">
<b>$(.content)</b>
</div>
<div class="status">
posted at <b>$(.posted_at)</b>
</div>
</div>
  • $(.name) のような箇所が、JSONデータが埋め込まれる箇所。先頭のドットは、エスケープすることを示す。ドットではなくアンダーラインを使うと、エスケープされない。
  • テンプレートは上記のように「{%}セクション名{%}本体」のようにセクションを分けて1ファイルに複数入れられるらしい。上の例はちょっとアレだけど、リクエスト回数削減のほかにも、「ヘッダ」「コンテンツ」「フッタ」のようにセクションを分けてコンテンツ部分のみ後述の bind_rowset で複数回テンプレートを適用する、といった使い方も想定されているっぽい。
/snbinder/assets/index.js

実際にテンプレートエンジンでビューを構築する部分。

var debug = { delay: 0 };

$(function(){
  //SNBinder.init({});

  SNBinder.get_named_sections("/snbinder/assets/template.htm", null, function(templates) {
    SNBinder.get("/snbinder/user/info.jsp", null, true, function(json) {
      // bind() は、テンプレートの "greeting" セクションに
      // info.jsp が返したJSONデータを埋め込んで返す
      $("#greeting").html(SNBinder.bind(templates["greeting"], json));
    });

    SNBinder.get("/snbinder/blog/entries.jsp", null, true, function(json) {
      // bind_rowset() は、entries.jsp が返したJSONデータ内の各要素を使って、
      // テンプレートの "blog_entry" セクションに対して繰り返し埋め込みを行い、
      // 全ての結果を結合して返す。
      $("#entries").html(SNBinder.bind_rowset(templates["blog_entry"], json))
      
      // 以下の省略形と考えればOK
      // var template_func = SNBinder.compile(templates["blog_entry"]);
      // var html = "";
      // for (var key in json) {
      //   html += template_func(json[key], key);
      // }
      // $("#entries").html(html);
    });
  });
})
  • debugという名前の、上のようなグローバルオブジェクトがないとエラーになる模様。最新バージョンではすでに修正されています。
  • SNBinder.init({}); は、READMEには必要とあるけど、エラーハンドリングやログイン処理のカスタマイズをしないなら、必要ない模様。
  • SNBinder.get_named_sections は、上述の {%} つきテンプレートを読んで、「セクション名」がキー、「本体」が値になったオブジェクトを第三引数のコールバック関数に渡してくれる。
  • 実際の関数名がいくつかREADMEと異なっているので、ソースを読もう。最新バージョンではすでに修正されています。
/snbinder/assets/snbinder-0.5.3-modified.js

注:最新バージョンではすでに修正されています(コメント参照)。

オリジナルから以下を修正した。

@@ -57,7 +57,7 @@ var SNBinder = (function() {
         get: function(url, params, isJson, callback, _options) {
             var options  = {
                 bypass_cache: false,
-                cache_result: true,
+                cache_result: true
             };
             $.extend(options, _options);

@@ -167,6 +167,9 @@ var SNBinder = (function() {
             })();
         }, // end of post
         evaluate: function(json) {
+            if (typeof(json) == 'object') {
+              return json;
+            }
             try {
                 var obj;
                 eval("obj=" + json);
  • 最初のは、IEで文法エラーになるため。
  • 2番目のは、きちんとMIME typeを指定してJSONデータを返した場合、jQueryは自動でパースしてobjectに直すので、こうしないとエラーになってしまうため。

動作結果

以下のようにJSONデータがテンプレートに埋め込まれて表示された(太字の箇所が「動的」な部分)。デザイン能力がまったくないのでとんでもなくしょぼいけど…

IE8とChromeで試したけど、どちらも普通にうまくいった。


どうせ今時のWebアプリケーションならJavaScriptを書かないということはありえないので、いっそのことMVCのVは完全にHTML+JavaScriptのみに集約してしまう、というのはすごく良さげな気がする。HTMLを操作するならJavaScript(というかjQuery)を使うのが一番楽だし、JSTLやらStrutsやらの汚らしいタグを必死こいて使ってHTMLをこねくり回すのに比べたら遥かにシンプルで自然だ。