2010年12月20日月曜日

「VC 2010 Expressでx64コンパイル」を見つけて嬉しかったのでメモ

VC 2010 Expressでx64コンパイル(How to compile x64 app with VC 2010 Express)
mnemotoさんありがとう
<?xml version=”1.0″ encoding=”utf-8″?>
<Project DefaultTargets=”Build” ToolsVersion=”4.0″ xmlns=”http://schemas.microsoft.com/developer/msbuild/2003“>
  <PropertyGroup>
    <SourcePath>$(SourcePath)</SourcePath>
    <IntDir>$(SolutionDir)$(Platform)\$(Configuration)\</IntDir>
    <IncludePath>C:\Program Files\Microsoft SDKs\Windows\v7.1\Include;C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include</IncludePath>
    <LibraryPath>C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\x64;C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\lib\amd64</LibraryPath>
   
    <TrackFileAccess>false</TrackFileAccess>
  </PropertyGroup>
</Project>
set “PATH=%PATH%;C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\amd64″
set “INCLUDE=C:\Program Files\Microsoft SDKs\Windows\v7.1\Include;C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include”
set “LIB=C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\x64;C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\lib\amd64″
bjam release debug threading=multi link=static runtime-link=static toolset=msvc address-model=64

2010年12月18日土曜日

WebGLに触れてみる

WebGLとは「JavaScriptとネイティブのOpenGL ES 2.0のバインディング」だそうだ。 by wikipedia

WebGLを試してみる」がわかりやすくて感動した。

Firefoxよりもchromeが好きなので、Canary Buildをインストールして再生準備完了。

子犬の写真がテクスチャとして貼ってある立方体がくるくる回ってるデモをみた。

早速ソースをみてみたら、VertexShaderとかFragmentShaderが何やってるのかわからなかったけど
メインのJavaScriptの部分はOpenGLを知っていればそれほど苦労せずに読めそう。

気になったのは、OpenGL ES 2.0はOpenGL ES 1.xよりも、いちいち面倒らしいということ。

それでも情報が増えてくれば負担は軽くなっていくだろうし
だれかが、便利な「なんとか.js」を作って公開してくれるだろうから
全力で待っているフリをしてみることにする。

2010年11月30日火曜日

現在のdalvik VMはアプリのヒープを16Mに制限している

http://groups.google.com/group/android-framework/msg/cda4cd5d5da2b2d8 から引用。

the VM currently limits app heaps to 16MB.
This allows for a reasonable number of apps to run.


ふーん。

NTTドコモのAndroid端末のユーザーエージェント

FAQに書いてあった。
【図】答え弊社Android端末標準webブラウザのユーザーエージェントは、下記のとおりとなります。

端末ファームウェア
バージョン
(ビルド番号)
ユーザーエージェント
HT-03AAndroid 1.5Mozilla/5.0 (Linux; U; Android 1.5; ja-jp; HT-03A Build/CDB72)
AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1
Android 1.6Mozilla/5.0 (Linux; U; Android 1.6; ja-jp; Docomo HT-03A Build/DRD08)
AppleWebKit/528.5+(KHTML, like Gecko) Version/3.1.2 Mobile Safari/ 525.20.1
Xperia™Android 1.6
(R1EA018)
Mozilla/5.0 (Linux; U; Android 1.6; ja-jp; SonyEricssonSO-01B Build/R1EA018)
AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1
Android 1.6
(R1EA025)
Mozilla/5.0 (Linux; U; Android 1.6; ja-jp; SonyEricssonSO-01B Build/R1EA025)
AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1
Android 1.6
(R1EA029)
Mozilla/5.0 (Linux; U; Android 1.6; ja-jp; SonyEricssonSO-01B Build/R1EA029)
AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1
Android 2.1
(2.0.B.0.138)
Mozilla/5.0 (Linux; U; Android 2.1-update1; ja-jp; SonyEricssonSO-01B Build/2.0.B.0.138)
AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17
LYNX SH-10BAndroid 1.6Mozilla/5.0 (Linux; U; Android 1.6; ja-jp; SH-10B Build/S7023)
AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1
GALAXY SAndroid 2.2Mozilla/5.0 (Linux; U; Android 2.2; ja-jp; SC-02B Build/FROYO)
AppleWebKit/533.1(KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
GALAXY TabAndroid 2.2Mozilla/5.0 (Linux; U; Android 2.2; ja-jp; SC-01C Build/FROYO)
AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1

2010年11月27日土曜日

HTML5でファイルのドラッグアンドドロップしてみた

jQuerySyntaxhighlighter使って、HTMLファイルを読みやすくしてみた。
ファイルのドラッグアンドドロップよりも、むしろ、SyntaxHighlighter.highlight()に気づくまで時間かかった。

Chrome 7.0.517.44とFirefox 3.6.10で動いた。
IE8とSafari 5.0.2は動かなかった。

ここにHTMLファイルをドロップする。

Drop your html files here.

<style>
.drophere {
  padding: 0.25em;
  width: 50%;
  border: 1px solid #666;
  background: #eee;
}
.dragover {
  background: #8f8
}
</style>
<div id="dropzone">
<div class="drophere" align="center">ここにHTMLファイルをドロップする。<br/>
Drop your html files here.</div>
</div>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">google.load("jquery", "1.3");</script>
<script type="text/javascript">
$(function(){
  $('#dropzone .drophere')
    .bind('dragenter', function(ev) {
      $(ev.target).addClass('dragover');
      return false;
    })
    .bind('dragleave', function(ev) {
      $(ev.target).removeClass('dragover');
      return false;
    })
    .bind('dragover', function(ev) {
      return false;
    })
    .bind('drop', function(ev) {
      $(ev.target).removeClass('dragover');
      var dt = ev.originalEvent.dataTransfer;
      for (var i = 0; i < dt.files.length; ++i) {
        var file = dt.files[i];
        var reader = new FileReader();
        reader.onloadend = function() {
          $('#dropzone')
          .append(
          $('<pre/>')
          .html($('<div/>').text(reader.result).html())
          .addClass('brush: html'));
          SyntaxHighlighter.highlight();
        };
        reader.readAsText(file, 'utf-8');
      }
      return false;
    });
});
</script>




2010年11月17日水曜日

Androidでスクリーンの方向を指定してみたらもやもやした

ゲームとかで画面の表示方向を固定したいことがある。

AndroidManifest.xmlでactivityのscreenOrientation属性で指定する方法が一般的だと思う。

<activity
  ...
  android:screenOrientation="landscape"
  ...>

この指定は実行時に変更することができる。

縦長固定に変える
Activity#setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

横長固定に変える
Activity#setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

システムに決めてもらう
Activity#setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);

「システムに決めてもらう」設定にしておくと、重力センサーなどに従って、自動的に自然な方向に回転したりする。

普通の時は自由に回転させておくけれど、ボタンを押したときには「その時の方向で固定」したいこともある。

public void lockScreenOrientation() {
    switch (getResources().getConfiguration().orientation) {
    case Configuration.ORIENTATION_LANDSCAPE:
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        break;
    case Configuration.ORIENTATION_PORTRAIT:
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        break;
    case Configuration.ORIENTATION_SQUARE:
        // ここではどうしたらいい?
        break;
    }
}


世の中には正方形のデバイスもあるらしい。
確かに横長でも縦長でもないけれど、正方形だとしてもデバイスの方向ってやつがあるはずでしょぅ
そう考えると、SQUAREっていう報告は来ないはず…
実機で試してみたいなぁー
あーもやもやする

2010年11月16日火曜日

<application>の直下に<meta-data>を書いてもいいのかもしれない

AndroidManifest.xml<meta-data>の説明では、<meta-data>の親になるのは、

  • <activity>
  • <activity-alias>
  • <service>
  • <receiver>

の4つしか書いてない。

だけど、ApplicationInfoにはmetaDataフィールドがあるし、
実機で検証したところ、期待通りに機能している。

public static Object getAppMetaData(Context context, String key) {
    try {
        final Bundle metaData =
            context
            .getPackageManager()
            .getApplicationInfo(
                context.getPackageName(),
                PackageManager.GET_META_DATA)
            .metaData;
        if (null != metaData) {
            return metaData.get(key);
        }
    } catch (NameNotFoundException e) {
    }
    return null;
}

「動く」けど、ドキュメントに書かれていない、この便利な機能を使ってよいのだろうか?

答えがないかと探していたら、見つけたっぽい。

AdMobのガイド
<application>の子要素で<meta-data>使って"ADMOB_PUBLISHER_ID"をセットしている。

AdMobがこの仕組みを使ってるってことは、だれでも使っていいってことだろう。
いつかドキュメントが更新されるに違いない。
もしも問題になったとしても、解決策がすぐに提案されるだろう。

2010年11月8日月曜日

Androidでアプリ実行中にフルスクリーンにしたりしなかったり

ボタンを押したらフルスクリーンにする。
もうひとつのボタンをおしたら元に戻す。
@Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  findViewById(R.id.btnFullScreen).setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    getWindow().addFlags(
      WindowManager.LayoutParams.FLAG_FULLSCREEN);
   }
  });
  findViewById(R.id.btnNotFullScreen).setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    getWindow().clearFlags(
      WindowManager.LayoutParams.FLAG_FULLSCREEN);
   }
  });
 }

2010年10月21日木曜日

Androidの標準ブラウザで開いたHTMLから自作アプリを起動してみた

通常のアプリのように見えて、
かつ、
ブラウザーからもアプリを起動できるようにするためには、
アクティビティにintent-filterを追加すればよい。

AndroidManifest.xml
<activity
    android:name=".MyActivity"
    android:label="@string/app_name"
    >
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="hello" android:host="world"/>
    </intent-filter>
</activity>

HTMLには
<a href="hello://world/">起動</a>
こんなリンクを用意する。

ブラウザーから起動された場合、getIntent().getData()でUriを取得することができる。
アクティビティが普通に起動されたときには、getData()はnullを返す。


2010年8月31日火曜日

画像でボタンを作る方法

これまでスタイルで指定する方法を使っていたけれど、よりよい方法を見つけた。
スタイルで指定するとEclipseのLayoutエディタではプレビューできなかった。
でも、以下の方法ならばプレビューもちゃんとできる。

まず、画像を3つ用意する。
  • 通常時の画像。
  • トラックパッドなどで選択されている時の画像。
  • タッチされている時の画像。
例えばこんな感じ。
res/drawable/normal.png
res/drawable/focused.png
res/drawable/pressed.png

次に、ボタンの状態に応じて表示するものが変わるdrawableをxmlで作成する。

drawable/button.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/focused" android:state_focused="true"/>
    <item android:drawable="@drawable/pressed" android:state_pressed="true"/>
    <item android:drawable="@drawable/normal"/>
</selector>


そしてボタンに適用する。

layoutファイルにて
<Button
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:background="@drawable/button"/>

自作Viewのカスタム属性

MyViewという名前で自作Viewを作り、カスタム属性helloを作る方法のメモ。

まず res/values/attrs.xml を作って属性を定義する。

res/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="MyView">
    <attr name="hello" format="string"/>
  </declare-styleable>
</resources>

これで、R.styleable.MyViewとR.styleable.MyView_helloというフィールドが自動生成される。

次に、MyViewのコンストラクタでカスタム属性を読むコードを書く。

MyView.java
public MyView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);

  TypedArray a = context.obtainStyledAttributes(
    attrs, R.styleable.MyView, defStyle, 0);
  this.hello = a.getString(R.MyView_hello);
  a.recycle();
}

MyViewを使う時は、属性のために名前空間を宣言する。

layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:takumak="http://schemas.android.com/apk/res/[Rクラスのパッケージ名]"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <com.blogspot.takumakei.lib.MyView
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    takumak:hello="hello world"/>
</LinearLayout>

参考にした情報

2010年8月30日月曜日

Galaxy Sは魅力的な端末だった

たまたま実機に触れるチャンスがあった。
反応が速くて素敵。
有機ELディスプレイも綺麗だし。
すごい魅力的。

2010年8月18日水曜日

SIMとICCIDとIMSIとIMEI

ICCIDとはIC Card IDのこと。SIMカード固有の番号。
SIMカードとはSubscriber Identity Module Cardのこと。
加入者識別番号モジュールカード。

android.telephony.TelephonyManager#getSimSerialNumberでICCIDを取得可能。

IMSIとはInternational Mobile Subscriber Identityのこと。

国際移動体加入者識別番号。
キャリアがSIMに割り当てた識別番号。
携帯電話の通信ではIMSIが電話番号の役割を担う。
IMSIは再利用される可能性がある。
IMSIは滅多に送信されることがないらしい。

IMEIはInternational Mobile Equipment Identifierのこと。
国際移動体装置識別番号。
端末に割り当てられた番号。
*#06#と入力すると携帯電話の画面に表示できる。


TelephonyManagerのインスタンスは、
READ_PHONE_STATEパーミッションが必要。

2010年8月17日火曜日

みたことあるAndroid端末のUser Agentたち

HT-03A
Mozilla/5.0 (Linux; U; Android 1.6; ja-jp; Docomo HT-03A Build/DRD08) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1

X06HT Desire
Mozilla/5.0 (Linux; U; Android 2.1-update1; ja-jp; HTC Desire Build/ERE27) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17

SO-01B XPERIA
Mozilla/5.0 (Linux; U; Android 1.6; ja-jp; SonyEricssonSO-01B Build/R1EA018) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1

IS01
Mozilla/5.0 (Linux; U; Android 1.6; ja-jp; IS01 Build/S7070) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1

2010年7月18日日曜日

JavaでXPathを使ってみた

Sample.java
import java.io.FileInputStream;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

public class Sample {
    public static void main(String[] args) throws Exception {
        Document document =
            DocumentBuilderFactory
            .newInstance()
            .newDocumentBuilder()
            .parse(new FileInputStream("books.xml"));
        XPathExpression xpath =
            XPathFactory
            .newInstance()
            .newXPath()
            .compile("//book[@year='2000']/title/text()");
        Node node = (Node)xpath.evaluate(document, XPathConstants.NODE);
        if (null != node)
            System.out.println(node.getNodeValue());
        else
            System.out.println("not found");
    }
}
books.xml
<?xml version="1.0" encoding="utf-8"?>
<inventory>
    <book year="2000">
        <title>Snow Crash</title>
        <author>Neal Stephenson</author>
        <publisher>Spectra</publisher>
        <isbn>0553380958</isbn>
        <price>14.95</price>
    </book>
    <book year="2005">
        <title>Burning Tower</title>
        <author>Larry Niven</author>
        <author>Jerry Pournelle</author>
        <publisher>Pocket</publisher>
        <isbn>0743416910</isbn>
        <price>5.99</price>
    </book>
</inventory>

そもそも、AndroidでXMLしたくて調べたのだけれど、
XPathはAPI Level 8 (Android 2.2) でプラットフォームに取り込まれた。
最近ターゲットにしているAPI Level 4 (Android 1.6) では標準ではなかった。

参考: Java XPath API

2010年6月27日日曜日

JavaScript on Java で Swing してみた

コードを書きまくって指が痛くなり、
もっとサボらなくちゃいけないと思い、
Javaの代わりにスクリプト言語で書くという選択肢にたどり着く。

「Javaの代わりにスクリプト」 == 「Groovy」 かなぁ?

「Javaの代わりにスクリプト」 == 「JRuby」 かなぁ?

そうこうしているうちに、Scripting for Java Platform。こんな機能、知りませんでした。


JS.java
import java.io.FileReader;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class JS {
    public static void main(String[] args) throws Exception {
        ScriptEngineManager m = new ScriptEngineManager();
        ScriptEngine e = m.getEngineByName("js");
        FileReader source = new FileReader("swing.js");
        try {
            e.eval(source);
        } finally {
            source.close();
        }
    }
}

swing.js
importPackage(java.awt);
importPackage(java.awt.event);
importPackage(javax.swing);

var frame = new JFrame("Sample");
frame.setLayout(new FlowLayout());

var button = new JButton("OK");

button.addActionListener(new ActionListener({
  actionPerformed: function(event) {
    JOptionPane.showMessageDialog(frame, "JavaScript on Java で Swing");
  }
}));

frame.add(button);

frame.setSize(100, 100);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
frame.setVisible(true);


参考
言語の中の言語 - Scripting
スクリプティング機能

ところで、スクリプト言語に換えたところで指の痛みは軽減できないことに気がついた。
「スクリプト使う→打鍵数が減る→プログラムが早くできあがる」だけだった
別ののサボり方を探そう。

2010年6月9日水曜日

Nettyと戯れてみた

Netty

まずはMaven 2.2.1 をインストール。apache-maven-2.2.1\binをPATHに追加。

Mavenでプロジェクトを作成。
C:\Users\Kei>mvn archetype:generate -DgroupId=com.blogspot.takumakei.studynetty -DartifactId=StudyNetty -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

pom.xmlにnettyを追加。
(nettyのDownloadページからコピペしたらバージョン番号がX.Y.Z.Qになっててエラー吐いた。)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.blogspot.takumakei.studynetty</groupId>
  <artifactId>StudyNetty</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>StudyNetty</name>
  <url>http://maven.apache.org</url>

  <repositories>
    <repository>
      <id>repository.jboss.org</id>
      <url>http://repository.jboss.org/nexus/content/groups/public/</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>

  <dependencies>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.jboss.netty</groupId>
      <artifactId>netty</artifactId>
      <version>3.1.5.GA</version>
      <scope>compile</scope>
    </dependency>

  </dependencies>
</project>

Eclipseのプロジェクトファイルを生成。
C:\Users\Kei\StudyNetty>mvn eclipse:eclipse -DdownloadSource=true -DdownloadJavadocs=true

Eclipseにインポート。


EclipseのWindow > PreferencesでClasspath Variablesに
[Name] M2_REPO
[Path] C:\Users\Kei\.m2\repository
を追加。

mvnで追加できるのを後から知った。
C:\Users\Kei>mvn -Declipse.workspace="C:\Users\Kei\EclipseWorks" eclipse:add-maven-repo

Writing a Discard ServerのDiscardServerHandlerとDiscardServerを追加。


さっそくサーバを起動してつながってみる。
C:\Users\Kei>irb -rsocket
ruby 1.8.7 (2010-01-10 patchlevel 249) [i386-mswin32]
require 'rubygems' -> true
require 'irb/completion' -> true
require 'pp' -> true
irb(main):001:0> c = TCPSocket.open('localhost', 12345)
=> #<TCPSocket:0x3bf1bf4>
irb(main):002:0> c.write 'hello world'
=> 11
irb(main):005:0> c.close
=> nil
irb(main):006:0>
C:\Users\Kei>

動いてる。


あとは、ドキュメント読みつつ実装するのみ。

そういえば…
Maven2.0.11かMaven2.2.1のどっちを使うべきか迷ったので両方インストールしてみた。
情報が少ないかもしれないけど、これまでMavenに依存してなかったから新しい方(2.2.1)を選んだ。

環境はWindows 7 Pro x64にJDK1.6.0_20

2010年5月20日木曜日

久しぶりにNetBeans IDEを使ってみた

NetBeans IDE 6.9 Betaを使ってみた。
最近はAndroidアプリの開発をしているのでEclipseなのだけれど
JavaFXしたいな~と思ってNetBeans。

ところがJavaFXする前に…
awtのデザインをGUIで構築できるのが便利すぎてつい遊んでしまった。

BlogCode.java
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * BlogCode.java
 *
 * Created on 2010/05/20, 2:11:47
 */

package com.blogspot.takumakei.blogcode;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import javax.swing.UIManager;

/**
 *
 * @author Kei
 */
public class BlogCode extends javax.swing.JFrame {

    /** Creates new form BlogCode */
    public BlogCode() {
        initComponents();
        setTitle("BlogCode");
        jTextArea1.setText(getClipboardString());
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        jButton1 = new javax.swing.JButton();
        jButton2 = new javax.swing.JButton();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTextArea1 = new javax.swing.JTextArea();
        jTextField1 = new javax.swing.JTextField();
        jButton3 = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jButton1.setText("Reset");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        jButton2.setText("Copy");
        jButton2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton2ActionPerformed(evt);
            }
        });

        jTextArea1.setColumns(20);
        jTextArea1.setRows(5);
        jScrollPane1.setViewportView(jTextArea1);

        jButton3.setText("Clear");
        jButton3.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton3ActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 269, Short.MAX_VALUE)
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                        .addComponent(jButton1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jButton3)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 83, Short.MAX_VALUE)
                        .addComponent(jButton2))
                    .addComponent(jTextField1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 269, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 172, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jButton1)
                    .addComponent(jButton2)
                    .addComponent(jButton3))
                .addContainerGap())
        );

        pack();
    }// </editor-fold>

    private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
        final String title = jTextField1.getText();
        final String body = jTextArea1.getText();
        if (0 == body.length()) { return; }

        StringBuilder sb = new StringBuilder();
        if (0 < title.length()) {
            sb.append("<pre class=\"mycodeT\" onclick=\"toggleMyCode(this)\">");
            sb.append(escapeHTML(title));
            sb.append("</pre>");
        }

        sb.append("<pre class=\"mycodeB\">");
        sb.append(escapeHTML(body));
        sb.append("</pre>\n");

        setClipboardString(sb.toString());
    }

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
        jTextField1.setText("");
        jTextArea1.setText(getClipboardString());
    }

    private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {
        jTextField1.setText("");
        jTextArea1.setText(getClipboardString());
    }

    public static String escapeHTML(String s){
        // http://www.rgagnon.com/javadetails/java-0306.html
        StringBuilder sb = new StringBuilder(s.length());
        int n = s.length();
        for (int i = 0; i < n; i++) {
            char c = s.charAt(i);
            switch (c) {
            case '\t': sb.append("    "); break;
            case '<': sb.append("&lt;"); break;
            case '>': sb.append("&gt;"); break;
            case '&': sb.append("&amp;"); break;
            case '"': sb.append("&quot;"); break;
            case 'à': sb.append("&agrave;");break;
            case 'À': sb.append("&Agrave;");break;
            case 'â': sb.append("&acirc;");break;
            case 'Â': sb.append("&Acirc;");break;
            case 'ä': sb.append("&auml;");break;
            case 'Ä': sb.append("&Auml;");break;
            case 'å': sb.append("&aring;");break;
            case 'Å': sb.append("&Aring;");break;
            case 'æ': sb.append("&aelig;");break;
            case 'Æ': sb.append("&AElig;");break;
            case 'ç': sb.append("&ccedil;");break;
            case 'Ç': sb.append("&Ccedil;");break;
            case 'é': sb.append("&eacute;");break;
            case 'É': sb.append("&Eacute;");break;
            case 'è': sb.append("&egrave;");break;
            case 'È': sb.append("&Egrave;");break;
            case 'ê': sb.append("&ecirc;");break;
            case 'Ê': sb.append("&Ecirc;");break;
            case 'ë': sb.append("&euml;");break;
            case 'Ë': sb.append("&Euml;");break;
            case 'ï': sb.append("&iuml;");break;
            case 'Ï': sb.append("&Iuml;");break;
            case 'ô': sb.append("&ocirc;");break;
            case 'Ô': sb.append("&Ocirc;");break;
            case 'ö': sb.append("&ouml;");break;
            case 'Ö': sb.append("&Ouml;");break;
            case 'ø': sb.append("&oslash;");break;
            case 'Ø': sb.append("&Oslash;");break;
            case 'ß': sb.append("&szlig;");break;
            case 'ù': sb.append("&ugrave;");break;
            case 'Ù': sb.append("&Ugrave;");break;
            case 'û': sb.append("&ucirc;");break;
            case 'Û': sb.append("&Ucirc;");break;
            case 'ü': sb.append("&uuml;");break;
            case 'Ü': sb.append("&Uuml;");break;
            case '®': sb.append("&reg;");break;
            case '©': sb.append("&copy;");break;
            case '€': sb.append("&euro;"); break;
            // be carefull with this one (non-breaking whitee space)
            //case ' ': sb.append("&nbsp;");break;

            default:  sb.append(c); break;
            }
        }
        return sb.toString();
    }
//    public static String stringToHTMLString(String string) {
//        // http://www.rgagnon.com/javadetails/java-0306.html
//        StringBuilder sb = new StringBuilder(string.length());
//        // true if last char was blank
//        boolean lastWasBlankChar = false;
//        int len = string.length();
//        char c;
//
//        for (int i = 0; i < len; i++)
//            {
//            c = string.charAt(i);
//            if (c == ' ') {
//                // blank gets extra work,
//                // this solves the problem you get if you replace all
//                // blanks with &nbsp;, if you do that you loss
//                // word breaking
//                if (lastWasBlankChar) {
//                    lastWasBlankChar = false;
//                    sb.append("&nbsp;");
//                    }
//                else {
//                    lastWasBlankChar = true;
//                    sb.append(' ');
//                    }
//                }
//            else {
//                lastWasBlankChar = false;
//                //
//                // HTML Special Chars
//                if (c == '"')
//                    sb.append("&quot;");
//                else if (c == '&')
//                    sb.append("&amp;");
//                else if (c == '<')
//                    sb.append("&lt;");
//                else if (c == '>')
//                    sb.append("&gt;");
//                else if (c == '\n')
//                    // Handle Newline
//                    sb.append("&lt;br/&gt;");
//                else {
//                    int ci = 0xffff & c;
//                    if (ci < 160 )
//                        // nothing special only 7 Bit
//                        sb.append(c);
//                    else {
//                        // Not 7 Bit use the unicode system
//                        sb.append("&#");
//                        sb.append(new Integer(ci).toString());
//                        sb.append(';');
//                        }
//                    }
//                }
//            }
//        return sb.toString();
//    }

    // http://www.ne.jp/asahi/hishidama/home/tech/java/clipboard.html
    public static String getClipboardString() {
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Clipboard clipboard = toolkit.getSystemClipboard();
        try {
            return (String)clipboard.getData(DataFlavor.stringFlavor);
        } catch (UnsupportedFlavorException e) {
            return "";
        } catch (IOException e) {
            return "";
        }
    }

    // http://www.ne.jp/asahi/hishidama/home/tech/java/clipboard.html
    public static void setClipboardString(String str) {
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Clipboard clipboard = toolkit.getSystemClipboard();
        StringSelection selection = new StringSelection(str);
        clipboard.setContents(selection, selection);
    }

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        try {
                UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
        } catch (Exception e) {
        }

        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new BlogCode().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify
    private javax.swing.JButton jButton1;
    private javax.swing.JButton jButton2;
    private javax.swing.JButton jButton3;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTextArea jTextArea1;
    private javax.swing.JTextField jTextField1;
    // End of variables declaration
}

2010年4月30日金曜日

LLVMのロゴ

LLVMにロゴがあったなんて知らなかった。

LLVM 2.7 がリリースされたけれど、X86-64の対応が甘いのは相変わらずっぽい。
自分で解決しろってことか…。

2010年4月17日土曜日

Xperia X10とWindows 7 64bit の USBドライバ(SDKのドライバをロードする方法)

android_winusb.infを修正しておくだけ。


android_winusb.inf.patch
--- android_winusb.inf.orig     2010-04-17 22:36:28.789163200 +0900
+++ android_winusb.inf  2010-04-17 23:24:14.522073700 +0900
@@ -26,6 +26,9 @@
 %ProviderName% = Google, NTx86, NTamd64

 [Google.NTx86]
+; Xperia X10
+%SingleAdbInterface%        = USB_Install, USB\VID_0FCE&PID_D12E
+%CompositeAdbInterface%     = USB_Install, USB\VID_0FCE&PID_D12E&MI_01
 ; HTC Dream
 %SingleAdbInterface%        = USB_Install, USB\VID_0BB4&PID_0C01
 %CompositeAdbInterface%     = USB_Install, USB\VID_0BB4&PID_0C02&MI_01
@@ -44,6 +47,9 @@
 %CompositeAdbInterface%     = USB_Install, USB\VID_18D1&PID_4E12&MI_01

 [Google.NTamd64]
+; Xperia X10
+%SingleAdbInterface%        = USB_Install, USB\VID_0FCE&PID_D12E
+%CompositeAdbInterface%     = USB_Install, USB\VID_0FCE&PID_D12E&MI_01
 ; HTC Dream
 %SingleAdbInterface%        = USB_Install, USB\VID_0BB4&PID_0C01
 %CompositeAdbInterface%     = USB_Install, USB\VID_0BB4&PID_0C02&MI_01

2010年4月5日月曜日

いまさらだけどRubyをWindows(32bit)にインストールするときのためのメモ

RubyをWindowsでcygwinに依存せずに快適に使うために必要なファイルの場所のメモ。

@ITの「WindowsにRuby1.9.1をインストールする」によると

Ruby mswin32はftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/から持ってこれる
GNU Readlineなどはhttp://jarp.does.notwork.org/win32/から持ってこれる
OpenSSLはhttp://www.deanlee.cn/programming/openssl-for-windows/から持ってこれる
Iconvはhttp://www.meadowy.org/meadow/dists/snapshot/old/から持ってこれる

Ruby1.8.6やRuby1.8.7でもこれらのDLLを使える。

ソースパッケージのほうが微妙に新しくなってるから
できれば(Rubyも含めて)自分でコンパイルしたい。
Cでext書くこともあるし、Cなgemをインストールすることもあるし...
でも、VC6が動くマシンは持ってないからなぁ...

2010年4月1日木曜日

Xperia X10とWindows 7 64bit の USBドライバ

Xperia X10をWindows 7 64bitに認識してもらえなくて困ってた。
いろいろぐぐってようやく見つけた

正解はPdaNet for Androidのインストールを途中までやること。

手順はたぶん次のようだったと思う。

  1. Xperia X10はPCから接続をはずしておく。
  2. PdaNetの64bit用のインストーラを起動して、「Androidをデバッグモードにしてつなげ」というダイアログが出るところまで進める。
  3. Xperia X10をデバッグモードにして、PCとUSBケーブルと接続する
  4. ダイアログのOKで先に進む。(ここでドライバのインストールに成功する)
  5. モデム用のデバイスドライバをインストールするかどうか聞いてくるので、 No と言う。
  6. するとインストーラはインストールに失敗したとみなしてインストールをキャンセルしてロールバックしてくれる。



ちなみに、
PdaNetを見つける前にフォーラムで見つけたX10.zipというのではダメだった。
32bit用なのだろうか?

2010年3月26日金曜日

クリティカルスピード

コードが少なくて驚いた。

面白いかも。


リアルアンリアル 高速配信Webサーバシステムをオープンソース化

2010年3月24日水曜日

MinGWでgdbmをビルドしてみた

flockとかfsyncがなくてダメな感じ。

systems.h
#include <io.h>
#include <windows.h>

#define UNLOCK_FILE(dbf) {                              \
    HANDLE h = (HANDLE)_get_osfhandle(dbf->desc);       \
    if (INVALID_HANDLE_VALUE != h) {                    \
      UnlockFile(h, 0, 0, (DWORD)-1, (DWORD)-1);        \
    }                                                   \
  }

#define READLOCK_FILE(dbf) {                                            \
    HANDLE h = (HANDLE)_get_osfhandle(dbf->desc);                       \
    if (INVALID_HANDLE_VALUE == h) {                                    \
      lock_val = -1;                                                    \
    } else {                                                            \
      OVERLAPPED over;                                                  \
      ZeroMemory(&over, sizeof(over));                                  \
      DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;                          \
      if (!LockFileEx(h, flags, 0, (DWORD)-1, (DWORD)-1, &over)) {      \
        lock_val = -1;                                                  \
      } else {                                                          \
        lock_val = 0;                                                   \
      }                                                                 \
    }                                                                   \
  }

#define WRITELOCK_FILE(dbf) {                                           \
    HANDLE h = (HANDLE)_get_osfhandle(dbf->desc);                       \
    if (INVALID_HANDLE_VALUE == h) {                                    \
      lock_val = -1;                                                    \
    } else {                                                            \
      OVERLAPPED over;                                                  \
      ZeroMemory(&over, sizeof(over));                                  \
      DWORD flags = LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK; \
      if (!LockFileEx(h, flags, 0, (DWORD)-1, (DWORD)-1, &over)) {      \
        lock_val = -1;                                                  \
      } else {                                                          \
        lock_val = 0;                                                   \
      }                                                                 \
    }                                                                   \
  }

2010年3月11日木曜日

MinGWでDLLを作ってみる (32bitと64bit)

手軽な方法はこれ。

$ g++ -static -shared -o hello.dll hello.cc world.cc -Wl,--out-implib,libhello.a

gcc 4.4.0 (32bit)を使って、上記の方法でDLLをつくると、必要以上に多くの関数をexportしちゃっている。

ちゃんとDEFファイルを用意して必要な関数だけをexportする場合は次のようにすればよいみたい。

$ g++ -c hello.cc
$ g++ -c world.cc
$ g++ -mdll -o hello.dll.tmp -Wl,--base-file,hello.dll.base hello.o world.o
$ dlltool -l libhello.a --dllname hello.dll --base-file hello.dll.base --output-exp hello.dll.exp --def hello.def
$ g++ -mdll -o hello.dll -Wl,hello.dll.exp -static hello.o world.o

MinGW-w64でx64バイナリを作ってみる

MinGW-w64からmingw-w64-bin_i686-mingw_20100310.zipをダウンロード

適当な場所に展開してPATHを通してコンパイル。

$ x86_64-w64-mingw32-g++ Hello.cpp -static

※ -staticオプションを指定しないと LIBSTDC++-6.DLL が必要。

MinGW/g++-4.4.0でコンパイルしてみた

「-static-libgcc」これをつけると、libgcc_s_dw2-1.dllを配布しなくてすむ

コンソール
$ g++ --version
g++.exe (GCC) 4.4.0
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ ls -l Hello.cpp
-rw-r--r-- 1 takumakei Administrators 97 Mar 11 13:00 Hello.cpp

$ cat Hello.cpp
#include 

int main() {
  std::cout << "Hello world" << std::endl;
  return 0;
}

$ g++ -o Hello -O3 Hello.cpp -static-libgcc

$ ls -l Hello.exe
-rwxr-xr-x 1 takumakei Administrators 3862265 Mar 11 13:56 Hello.exe*

$ strip Hello.exe

$ ls -l Hello.exe
-rwxr-xr-x 1 takumakei Administrators 496142 Mar 11 13:56 Hello.exe*

$ ./Hello.exe
Hello world
ウィンドウ
$ ls -l HelloW.cpp
-rw-r--r-- 1 JP10007 Administrators 150 Mar 11 13:34 HelloW.cpp

$ cat HelloW.cpp
#include 

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPTSTR, int) {
  MessageBox(NULL, "Hello world", "HelloW", MB_OK);
  return 0;
}

$ g++ -o HelloW.exe -O3 HelloW.cpp -static-libgcc -mwindows

$ ls -l HelloW.exe
-rwxr-xr-x 1 JP10007 Administrators 334435 Mar 11 13:59 HelloW.exe*

$ strip HelloW.exe

$ ls -l HelloW.exe
-rwxr-xr-x 1 JP10007 Administrators 75790 Mar 11 13:59 HelloW.exe*

2010年3月10日水曜日

Vaadinに興味津津

JavaだけでWebアプリケーションを作るフレームワークVaadin

GCJでコンパイルしてみた

MinGW/MSYSをインストールしてgcjしてみたがコンパイル後のプログラムを起動できないという罠にはまった。

つぎにthisiscool.comGCC/GCJ 4.3 (gcj-eclipse-merge-branch)をインストールしてようやくコンパイルと実行に成功した。

SWTを使ってウィンドウを表示するだけの単純なアプリなのに、コンパイルにはやたらと時間がかかるし、実行ファイルのサイズが55,842KBと巨大。upxで圧縮しても20,394KB。

JREをインストールしていない環境でも動作するプログラムをJavaで実装できることにはとても魅力を感じるけれど、この結果だと採用しにくいな。

a/Main.java
package a;

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class Main {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("HelloSWT");
        shell.setSize(480, 320);
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }
}
コンパイル
gcj -fjni -c -o swt.o swt.jar
gcj -mwindows -o Hello --classpath=swt.jar --main=a.Main a/Main.java swt.o

2010年2月8日月曜日

Kindle Development Kitがリリースされた

Amazon.comのヒット商品である電子書籍リーダー Kindle開発キットベータリリースされた。

今更ながら、おさらい

レベニューシェア : 開発者 70% 。Amazon 30%。

アクティブコンテントの価格設定は三種類。

  1. 無料コース
    • コンテントが1M以下で、月に100K以下の通信量なら無料でリリースできる
  2. お買い上げコース
    • 月に100K以下の通信量
  3. 月額コース
    • 毎月お支払い。

アクティブコンテントは最大で100Mまで。
10Mを超える場合は無線でダウンロードはできない。PC経由でUSBで転送する必要がある。

Kindleは無料で無線ネットワークを使えるので、それを考慮したガイドラインになっている。

Request to become a Limited Beta Developerに登録しようかと思ったけれど
「君が作ろうと思っているアクティブコンテントアプリケーションはどんなものかね?」
と聞かれた。回答必須だと。

今のところNo Idea。

2010年2月4日木曜日

swfmillのバッチファイルを作ってみた

swfmillを使ってswfとxmlを相互に変換する作業を
繰り返し繰り返しやる必要があったので
ドラッグアンドドロップできるようなバッチファイルを作ってみた。

swf2xml.bat
@ECHO OFF
FOR %%I IN ( %* ) DO (
  ECHO $ swfmill swf2xml %%~nxI %%~nI.xml
  swfmill swf2xml "%%~I" "%%~dpnI.xml"
  ECHO .
)
PAUSE
xml2swf.bat
@ECHO OFF
FOR %%I IN ( %* ) DO (
  ECHO $ swfmill xml2swf %%~nxI %%~nI.swf
  swfmill xml2swf "%%~I" "%%~dpnI.swf"
  ECHO .
)
PAUSE

2010年1月27日水曜日

jrubycでプリコンパイルしてみた

JRubyではRubyスクリプトをプリコンパイルしてclassファイルにすることができる。

% type sample.rb
puts "hello world

% mkdir bin
% jruby -S jrubyc -t bin sample.rb
Compiling sample.rb to class sample

実行する時にはJRubyのライブラリをクラスパスに指定する。
JRuby 1.4.0では、jruby.jarを指定すれば十分なようだ。

% java -cp ./bin;C:/jruby-1.4.0/lib/jruby.jar sample
hello world

jarファイルにjarファイルを埋め込むことができるから、(Fat JarとかOne-Jarとか)
これで「rubyなんてインストールしてないよ」っていう人でも
Javaがインストールされていれば作成したプログラムを使ってもらえる。

ちなみに、jrubyはコンパイル済みのクラスファイルを実行することもできる。

% jruby sample.class

便利そうだけど、JRubyがインストールされている環境でjrubycでコンパイルしたclassファイルを実行できて嬉しいことがあるのかどうかちょっと疑問。

exerbみたいにrequireをトレースして必要なファイルを全てコンパイルしてJarに固めるような機能があったら便利そう。

2010年1月21日木曜日

Groovyは僕を幸せにするか?

Groovyを使ったらプロトタイプ開発=製品開発にできるだろうか?


プロトタイプ作成にRuby使って書いたコードを、製品用にJavaに書き直したとき
Rubyのコードは600行(約15KB)位だったのに
Javaのコードは1200行(約34.5KB)超えてちょっとうんざりした


Ruby→Javaにしなければならなかった理由は

  1. バグ修正、機能拡張 ← 担当者がRuby知ってることはオプション。Javaは必須要件。
  2. パフォーマンス ← 軽くて早いのがいい

という事だったかな。

「Java必須ならRubyも必須でしょ?」はちょっと難しそうだけど
「Java必須ならGroovyも常識でしょ?」ってちょっと頑張ればごり押しできるかも
...とか妄想してみたりしなかったり。

Groovyのパフォーマンスはどうなんだろう。調べてみたい。

Play!

PlayフレームワークはJavaでWebアプリ開発を簡単にする...らしい。
The Play framework makes it easier to build Web applications with Java

Javaで軽快に使える「軽量フレームワーク特集」
~本格的なRoRスタイルフレームワーク「Play!」

ほぅ。Groovyにも興味が出てきた。←え!そっち?