数値と、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"
久々の 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
プログラミング関連の発音
普段、周りにプログラミング関係のことを話す人が少ないので、それ系の単語の正しい 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が何故かツイッター」みたいに、日本ではそう発音することに決まってしまった、となったら仕方ないけど*1、Maven がそこまでメジャーになることはないでしょう。、
SQLite
SQLite - Wikipedia を見ると、「エスキューエ(ル)ライト」「シークエ(ル)ライト」*2の両方が書いてある。わたしは何となく「エスキューライト」と言っていたんだけどこれはダメっぽい…。
なぜ2つ併記されているかというと、これ以上ない根拠と言える D. Richard Hipp 氏自身のプレゼンでの発音からして、二種類の発音を使っているから。なので、公式に「どっちでもいい」ということに…
sudo
普段使っている「スードゥー」でよさそうだ。
でも、"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は単なるバージョン番号っぽく感じるので「エイチティーエムエル よんてんぜろいち」とか。この辺のダブルスタンダードも何となく心にしこりが残らないでもない。
結論
コマンドとかプログラムとかのオリジナル開発者の人は、ホームページに発音を書いてください。困ります。
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
と書いたら直った。
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のページからダウンロードしてクラスパス上に置く。
<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>
/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);
動作結果
以下のようにJSONデータがテンプレートに埋め込まれて表示された(太字の箇所が「動的」な部分)。デザイン能力がまったくないのでとんでもなくしょぼいけど…
IE8とChromeで試したけど、どちらも普通にうまくいった。
どうせ今時のWebアプリケーションならJavaScriptを書かないということはありえないので、いっそのことMVCのVは完全にHTML+JavaScriptのみに集約してしまう、というのはすごく良さげな気がする。HTMLを操作するならJavaScript(というかjQuery)を使うのが一番楽だし、JSTLやらStrutsやらの汚らしいタグを必死こいて使ってHTMLをこねくり回すのに比べたら遥かにシンプルで自然だ。