2014年1月27日月曜日

Lispでxmlの実体参照変換

当ブログでは、プログラムをハイライト表示するときにSyntaxHighlighterを使用しています。
XMLを表示するときは、実体参照へ変換しなければいけません。
「普段使用しているエディタから変換できれば楽ちんだよね」と思い、xyzzy Lispで変換プログラムを書いてみました。

プログラム
; convert-xml
(defparameter *conversion-alist*
  '((#\< . "&lt;")
    (#\> . "&gt;")
    (#\& . "&amp;")
    (#\" . "&quot;")
    (#\' . "&apos;")))

(defun convert-xml ()
  (interactive "*")
  (let ((input-buffer (window-buffer (selected-window))))
    (with-output-to-temp-buffer ("*Converted*")
      (with-open-stream (s (make-buffer-stream input-buffer))
        (loop 
          (let ((c (read-char s nil)))
            (if c (format t "~A" (convert-char c *conversion-alist*))
              (return nil))))))))

(defun convert-char (c alist)
  (let ((conversion (assoc c alist)))
    (if conversion (cdr conversion)
      c)))

実行すると現在のバッファを読み込み、一時バッファへ変換後のxmlを表示します。
工夫したところは、変換の定義をalistにしたところです。

参考

2014年1月20日月曜日

Traceを使用してログを出力するときの設定例(TraceListener)

.Netでログを出力するときの選択肢の1つとして、System.Diagnostics.Traceクラスを使用する方法があります。
この時のアプリケーション構成ファイルの記述例を記載します。
テストプログラムとして、次のプログラムを使用します。

namespace TraceTest 
{
  using System.Diagnostics;
  
  public class TraceTest {
    
    public static void Main(string[] args) {
      Trace.TraceInformation("TraceInformation");
      Trace.TraceWarning("TraceWarning");
      Trace.TraceError("TraceError");
    }
  }
}

次にTraceListenerの設定例を記載します。

<configuration>

  <system.diagnostics>
    <trace autoflush="true">
      <listeners>
        <clear />

        <!-- 標準出力 -->
        <add name="console"
             type="System.Diagnostics.ConsoleTraceListener" />

        <!-- 区切りリスト -->
        <add name="delimited-list"
             type="System.Diagnostics.DelimitedListTraceListener"
             initializeData="DELIMITED-LIST.log" />

        <!-- イベントログ -->
        <add name="eventlog"
             type="System.Diagnostics.EventLogTraceListener"
             initializeData="Application" />

        <!-- テキストファイル -->
        <add name="file"
             type="System.Diagnostics.TextWriterTraceListener"
             initializeData="TEXT-WRITER.log" />

        <!-- XML形式 -->
        <add name="xml-writer"
             type="System.Diagnostics.XmlWriterTraceListener"
             initializeData="XML-WRITER.log" />

      </listeners>
    </trace>
  </system.diagnostics>

</configuration>
参考
<trace> の <listeners> の <add> 要素

2014年1月13日月曜日

FormでShowing/Hidingイベントを実装

System.Windows.Forms.Formにありそうでないイベントの一つに、表示前/非表示前イベントがあります。
なければ作っちゃえということで、SetVisibleCoreをオーバーライドして実装してみました。
処理の流れは以下のようになります。

  1. 引数と現在のVisibleを比較し、等しければイベントを発生させずにSetVisibleCoreを呼んで終了する。
  2. 引数がtrueのとき、Showingイベントを発生させる
  3. 引数がfalseのとき、Hidingイベントを発生させる
  4. 2,3の結果、CancelEventArgs.Cancelがtrueのとき、引数を逆にしてSetVisibleCoreを呼び、終了する。
  5. SetVisibleCoreを呼び、終了する。

なお、注意点として、SetVisibleCoreでShow/Hideメソッドを呼ばないこと、Visibleプロパティを更新しないことがあります。これらに違反すると、StackOverflowExceptionが発生してしまいます。

サンプル

using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace Sample {
public class ShowingHidingForm : Form {
private event FormShowingEventHandler formShowing;
private event FormHidingEventHandler formHiding;
public event FormShowingEventHandler FormShowing {
add { formShowing += value; }
remove { formShowing -= value; }
}
public event FormHidingEventHandler FormHiding {
add { formHiding += value; }
remove { formHiding -= value; }
}
public ShowingHidingForm() {
formShowing += (sender, e) => {};
formHiding += (sender, e) => {};
}
protected override void SetVisibleCore (bool value) {
var current = Visible;
var toBe = value;
if (current == toBe) {
base.SetVisibleCore(toBe);
return;
}
if (toBe && !current) {
var e = new FormShowingEventArgs();
formShowing(this, e);
if (e.Cancel) {
toBe = current;
}
}
if (!toBe && current) {
var e = new FormHidingEventArgs();
formHiding(this, e);
if (e.Cancel) {
toBe = current;
}
}
base.SetVisibleCore(toBe);
}
public static void Main(string[] args) {
var form = new ShowingHidingForm();
form.FormShowing += (sender, e) => {
Console.WriteLine("Showing");
};
form.FormHiding += (sender, e) => {
Console.WriteLine("Hiding");
};
form.FormClosing += (sender, e) => {
form.Hide();
Console.WriteLine("Closing");
};
Application.Run(form);
}
}
public delegate void FormShowingEventHandler(object sender, FormShowingEventArgs e);
public delegate void FormHidingEventHandler(object sender, FormHidingEventArgs e);
public class FormShowingEventArgs : CancelEventArgs { }
public class FormHidingEventArgs : CancelEventArgs { }
}

参考

Form.SetVisibleCore メソッド

2013年7月21日日曜日

CからC#を呼ぶ面倒な方法の1つ

今回は、CからC#で作ったDLLを呼ぶ方法について書きます。タイトルにも書きましたが、かなり面倒なやり方なので、みなさんは頑張って回避してください。

さて、手順は次のようになります。

  1. C#でDLLを作成する
  2. 上で作成したDLLをレジストリへ登録する
  3. Cでプログラムを作成する
C#でDLLを作成する
Cから呼び出されるライブラリをC#で作成します。

DLLをレジストリへ登録する
警告が出ますが気にしない。だってサンプルですもの。

Cでプログラムを作成する
さあ、ここからが本番です。先ほど登録したDLLからクラス名とメソッド名を指定してオブジェクトに働いてもらいましょう。大まかなイメージはJavaやC#のリフレクションに近いです。

  1. 初期化
  2. クラス名からCLSIDを取得
  3. インスタンス作成
  4. IDispatchへキャスト
  5. メソッド名からDISPIDを取得
  6. パラメータの設定
  7. メソッドの呼び出し
  8. 終了処理
ここで注意が必要なのが、パラメータの設定です。適切なVariantTypeと適切なプロパティを選ばなくてはいけないところ、さらに、パラメータの順番が逆転するところに注意してください。

// cl callCOM.c ole32.lib oleaut32.lib
#include <Objbase.h>
int main() {
HRESULT hr;
CLSID clsid;
DISPID dispid;
VARIANTARG rgvArg[2];
int result = 0;
DISPPARAMS dispParams = {NULL, NULL, 0, 0};
VARIANT varResult;
EXCEPINFO excepInfo;
UINT uArgErr;
OLECHAR *lpszProgID = L"NamespaceSample.ClassSample";
OLECHAR *rgszNames = {L"MethodSample"};
IUnknown *pUnknown = NULL;
IDispatch *pDispatch = NULL;
// 1. 初期化
hr = CoInitialize(NULL);
if (FAILED(hr)) {
return -10;
}
// 2. クラス名からCLSIDを取得
hr = CLSIDFromProgID(lpszProgID, &clsid);
if (FAILED(hr)) {
CoUninitialize();
return -20;
}
// 3. インスタンス作成
hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnknown);
if (FAILED(hr)) {
CoUninitialize();
return -30;
}
// 4. IDispatchへキャスト
hr = pUnknown->lpVtbl->QueryInterface(pUnknown, &IID_IDispatch, (void **)&pDispatch);
if (FAILED(hr)) {
pUnknown->lpVtbl->Release(pUnknown);
CoUninitialize();
return -40;
}
// 5. メソッド名からDISPIDを取得
hr = pDispatch->lpVtbl->GetIDsOfNames(pDispatch, &IID_NULL, &rgszNames, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
if (FAILED(hr)) {
pUnknown->lpVtbl->Release(pUnknown);
CoUninitialize();
return -50;
}
// 6. パラメータの設定
VariantInit(&rgvArg[0]);
rgvArg[0].vt = VT_I4;
rgvArg[0].lVal = 99;
VariantInit(&rgvArg[1]);
rgvArg[1].vt = VT_BSTR;
rgvArg[1].bstrVal = SysAllocString(L"HOGE");
dispParams.rgvarg = rgvArg;
dispParams.cArgs = 2;
VariantInit(&varResult);
// 7. メソッドの呼び出し
hr = pDispatch->lpVtbl->Invoke(pDispatch, dispid, &IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &dispParams, &varResult, &excepInfo, &uArgErr);
if (FAILED(hr)) {
pUnknown->lpVtbl->Release(pUnknown);
CoUninitialize();
return -60;
}
// 8. 終了処理
printf("%S\n", varResult.bstrVal);
pUnknown->lpVtbl->Release(pUnknown);
CoUninitialize();
return 0;
}
view raw callCOM.c hosted with ❤ by GitHub
// csc /target:library ClassSample.cs
// regasm /codebase ClassSample.dll
// regasm /u ClassSample.dll
namespace NamespaceSample {
using System.Runtime.InteropServices;
// ComVisibleAttributeをCOM経由で呼び出したいクラスへ設定する
[ComVisible(true)]
public class ClassSample {
public string MethodSample(string arg1, int arg2) {
return string.Format("arg1={0} arg2={1}", arg1, arg2);
}
}
}
view raw ClassSample.cs hosted with ❤ by GitHub
お疲れ様でした。

2012年12月6日木曜日

WicketでWebSocketを使ってみた

Wicketの6からWebSocketに対応したらしいので使ってみた。

  • 作ったもの
  • 感想
  • 引っかかったところ
作ったもの
簡単にいうと、チャットアプリケーション。
メッセージをAjaxで受け取り、チャット参加者へWebSocketでpush通知する。
クライアント側はjQueryで受け取ったメッセージをDOMへ追加する。

引っかかったところ
IWebSocketConnectionにはComponentをaddできない。送信できるのは文字列またはbyte配列。WebSocketRequestHandlerにはaddできるので、WebSocketBehaviorのonMessageで実装すればよいが、それってAjaxとなにが違うのかな?

web.xmlで、サーブレットコンテナに対応したfilterを設定しなければならない。wikiのCustom WicketFilterを参照。

JavaScriptの依存関係を設定しなければいけない。サンプルを参照。

感想
IWebSocketConnectionで送受信できるデータが文字列かバイト配列なので、実際はJSONを送受信することになると思われる。Javaオブジェクトとの相互変換を考慮する必要があると思う。

push通知が動作するまでは、手間がかかる割に動作はAjaxと変わらないのでモチベーションが上がらなかった。が、push通知で画面が更新されるようになると一気に楽しくなった。

やや話はそれるが、JavaScriptのライブラリがjQueryになったのも大きいと思う。

2012年9月23日日曜日

ClojureでDesign By Contract

Clojureでは、関数を定義する際に事前条件と事後条件を定義できます。
この機能を使えば、Design By Contract(契約による設計)を容易に実践できます。

事前条件を定義する
さっそく実験してみます。
「パラメータが文字列であること」という事前条件をもつ関数aを定義しました。

user=> (defn a [x] {:pre [(instance? String x)]} x)
#'user/a
user=> (a 1)
AssertionError Assert failed: (instance? String x) user/a (NO_SOURCE_FILE:1)
user=> (a "123")
"123"
view raw assert.clj hosted with ❤ by GitHub
パラメータとして1を渡したときにAssertionErrorが発生しました。

Assertionを無効にする
Assertionを無効にするには、*assert*へfalseをセットします。
この値はコンパイル時に参照されるため、REPLの場合はdefnを実行する前に設定しておく必要があります。
*assert*へfalseを設定してから、上と同じ関数を定義しました。

user=> (set! *assert* false)
false
user=> (defn a [x] {:pre [(instance? String x)]} x)
#'user/a
user=> (a 1)
1
user=> (a "123")
"123"
パラメータとして1を渡しても例外が発生しなくなりました。

例をもう少し
最後に、もう少し実践的な例として日付の比較をする関数を上げておきます。
この関数はCalendar.beforeを呼ぶため、パラメータはCalendarクラスのインスタンスでなければいけません。

user=> (import java.util.Calendar)
java.util.Calendar
;; 関数定義
user=> (defn before? [base comp]
#_=> {:pre [(instance? Calendar base)
#_=> (instance? Calendar comp)]
#_=> :post [(instance? Boolean %)]}
#_=> (.before base comp))
#'user/before?
;; 動作確認
user=> (def cal1 (Calendar/getInstance))
#'user/cal1
user=> (def cal2 (Calendar/getInstance))
#'user/cal2
user=> (before? cal1 cal2)
true
user=> (before? cal2 cal1)
false
;; Calendar以外を渡してみる
user=> (before? 1 2)
AssertionError Assert failed: (instance? Calendar base) user/before? (NO_SOURCE_FILE:1)
view raw before.clj hosted with ❤ by GitHub
事前条件と事後条件をシンプルに定義できるのが素敵だと思いました。

2012年7月30日月曜日

DropDownChoiceでenumを使用する

突然ですが、enumって便利ですよね。
業務で使用する場面としては、"○○区分"というものを表現するのによく使います。
そしてそれを画面では<select>として表示することもよくあります。
このとき、画面に表示するテキストをenum自身から分離するための方法です。

enumを定義する

普通にenumを定義します。
public enum RockPaperScissors {
ROCK,PAPER,SCISSORS;
}

アプリケーションのプロパティファイルに表示する文字列を定義する

型名.フィールド名=文字列の形式で定義します。
# for RockPaperScissors
RockPaperScissors.ROCK=\u30b0\u30fc
RockPaperScissors.PAPER=\u30d1\u30fc
RockPaperScissors.SCISSORS=\u30c1\u30e7\u30ad

DropDownChoiceのコンストラクタにEnumChoiceRendererを渡す

public class RockPaperScissorsDropDownChoice
extends DropDownChoice<RockPaperScissors> {
public RockPaperScissorsDropDownChoice(String id) {
super(id,
Arrays.asList(RockPaperScissors.values()),
new EnumChoiceRenderer<RockPaperScissors>());
}
}

画面ごとに表示するテキストを変えたい場合、以下のようにします。

  1. 画面ごとにプロパティファイルを作成する
  2. EnumChoiceRendererのコンストラクタにPageのインスタンスを渡す