イベントの自動ハンドリング
以前作成した『DIっぽい動作』のソースにイベントの自動ハンドリングを実装。
ネーミングルールとして画面のイベントの場合は「"on"+[イベント名]+"Handler"」、コンポーネントのイベントの場合は「[コンポーネントID]+"On"+[イベント名]+"Handler"」と指定する事で自動ハンドリングするようにしてみた。
/*
* System Name : NkeFlexCommon
*=======================================================================================
* 作成日 : 2010/03/20 んけさん
* 最終更新日 : 2010/04/10 んけさん イベントハンドル処理追加
*=======================================================================================
* [修正履歴]
* <日付> <担当者> <Version> <修正内容>
*---------------------------------------------------------------------------------------
* 2010/03/20 んけさん 1.0.0 新規作成
* 2010/04/04 んけさん 1.0.1 logic, helperが定義されている場合はインスタンス
* の自動生成を行う様に修正。ステージより削除される
* タイミングで初期化する様に修正。
* 2010/04/10 んけさん 1.02 actionクラスに定義されているメソッドと画面のコンポーネント
* のイベントの紐付けを下記命名規約に従い行う様に修正。
* ・画面部 : on[イベント名]Handler
* ・コンポーネント : [コンポーネント名]On[イベント名]Handler
*/
package com.rikopapa.common.action{
import flash.events.Event;
import flash.utils.describeType;
import flash.utils.getQualifiedClassName;
import mx.core.IMXMLObject;
import mx.events.FlexEvent;
/**
* 画面アクションクラス<br />
* 画面とアクションクラスの紐付けを行う。<br />
*
* @author んけさん
* @version 1.0.2
*/
public class NkeViewAction implements IMXMLObject{
/**
* 画面イベントパターン[1.0.2追加]<br />
*/
private static const VIEW_EVENT_HALDER_PATTERN:RegExp = /^on[^a-z].+Handler/;
/**
* 子コンポーネントイベントパターン[1.0.2追加]<br />
*/
private static const CHILD_EVENT_HANDLER_PATTERN:RegExp = /.+On[^a-z].+Handler/;
/**
* アクションクラスID<br />
*/
private var _id:String;
/**
* アクションクラスID<br />
*
* @default null
*/
public function get id():String{
return _id;
}
/**
* イベント発生元画面インスタンス<br />
*/
protected var _view:Object;
/**
* Helperクラスインスタンス[1.0.1追加]<br />
*/
private var _helper:Object;
/**
* Logicクラスインスタンス[1.0.1追加]<br />
*/
private var _logic:Object;
/**
* 子コンポーネントイベントハンドラー一覧[1.0.2追加]<br />
*/
private var _childEventHandlers:Array;
/**
* デフォルトコンストラクタ<br />
*/
public function NkeViewAction(){
super();
}
/**
* initialized処理<br />
*
* @param document イベント発生元画面インスタンス
* @param id アクションクラスID
*
*/
public function initialized(document:Object, id:String):void{
// イベント発生元画面インスタンスとアクションクラスIDを保持
_view = document;
_id = id;
// CreationCompleteイベントリスナー登録
this._view.addEventListener(FlexEvent.CREATION_COMPLETE,
_onCreationCompleteHandler,
false,
0,
true);
// removedFromStageイベントリスナー登録[1.0.1追加]
this._view.addEventListener(Event.REMOVED_FROM_STAGE,
onRemoveFromStageHandler,
false,
0,
true);
// helperクラスのインスタンス生成[1.0.1追加]
setDiInstance(_helper, "helper", "Helper");
// logicクラスのインスタンス生成[1.0.1追加]
setDiInstance(_logic, "logic", "Logic");
// イベントハンドラの設定[1.0.2追加]
settingEventHandler();
// 画面のInitialize処理をコール
onInitializeComplete();
}
/**
* イベントハンドラ設定処理<br />
* Actionクラスのメソッドと画面のイベントの紐付けを行う。<br />
* [1.0.2追加]<br />
*/
private function settingEventHandler():void{
var classInfo:XML;
var methodName:String;
var eventName:String;
// 子コンポーネントイベントハンドラー一覧の初期化
_childEventHandlers = [];
// クラス情報の取得
classInfo = describeType(this);
// メソッド情報を取得しループ
for each(var methodInfo:XML in classInfo.method){
methodName = methodInfo.@name;
if(methodName == "onCreationCompleteHandler" || methodName == "onInitializeComplete"){
// メソッド名が"onCreationCompleteHandler"か"onInitializeComplete"の場合は直接呼び出している
// 為、スキップする
continue;
}
if(VIEW_EVENT_HALDER_PATTERN.test(methodName)){
// メソッド名が画面イベントパターンと一致した場合
eventName = getViewEventName(methodName);
// 画面にイベントリスナー登録
_view.addEventListener(eventName, this[methodName], false, 0, true);
}else if(CHILD_EVENT_HANDLER_PATTERN.test(methodName)){
// メソッド名が子コンポーネントイベントパターンと一致した場合
// 子コンポーネントイベントハンドラー一覧に追加
_childEventHandlers.push(methodName);
}
}
if(_childEventHandlers.length > 0){
// 子コンポーネントイベントパターンに一致したメソッドが存在する場合
// addedイベントで該当コンポーネントにイベントリスナー登録を行う
_view.addEventListener(Event.ADDED, _onAddedEventHandler, false, 0, true);
}
}
/**
* 画面部イベント名取得処理<br />
* [onXxxxxHandler]の形式よりイベント名を取得。<br />
* [1.0.2追加]<br />
*
* @param methodName メソッド名
* @return イベント名
*/
private function getViewEventName(methodName:String):String{
// 先頭の"on"を排除
var eventName:String = methodName.substr(2);
// 先頭から"Handler"までの値を取得
eventName = eventName.substr(0, eventName.indexOf("Handler"));
// 先頭1文字を小文字に変換
eventName = eventName.substr(0, 1).toLowerCase()+eventName.substr(1);
return eventName;
}
/**
* 子コンポーネントイベント名取得処理<br />
* [xxxxxOnXxxxxHandler]の形式よりイベント名を取得。<br />
* [1.0.2追加]<br />
*
* @param methodName メソッド名
* @return イベント名
*/
private function getChildEventName(methodName:String):String{
// 先頭から"On"までを排除
var eventName:String = methodName.substr(methodName.indexOf("On") + 2);
// 先頭から"Handler"までの値を取得
eventName = eventName.substr(0, eventName.indexOf("Handler"));
// 先頭1文字を小文字に変換
eventName = eventName.substr(0, 1).toLowerCase()+eventName.substr(1);
return eventName;
}
/**
* addedイベント<br />
* 画面の表示リストに登録された場合にコールされる。<br />
* イベントのターゲットが子コンポーネントイベントハンドラー一覧に存在する場合、<br />
* ターゲットとメソッドの紐付けを行う。<br />
* [1.0.2追加]<br />
*
* @param event Event
*/
private function _onAddedEventHandler(event:Event):void{
var targetChild:Object;
var targetChildId:String;
var eventId:String;
var eventName:String;
targetChild = event.target;
if(targetChild is NkeViewAction){
// ターゲットがNkeViewActionの場合は処理終了
return;
}else if(!targetChild.hasOwnProperty("id")
|| targetChild.id == null
|| targetChild.id == undefined
|| targetChild.id == ""){
// ターゲットにidプロパティが存在しないまたは未設定の場合は処理終了
return;
}
// ターゲットのid取得
targetChildId = targetChild.id.toString();
if(targetChildId.indexOf("$") >= 0){
// idに$がある場合先頭から$までを取得
targetChildId = targetChildId.substr(0, targetChildId.indexOf("$"));
}
// 子コンポーネントイベントハンドラー一覧をループ
for each(var methodName:String in _childEventHandlers){
// イベントと紐付くコンポーネントのid取得
eventId = methodName.substr(0, methodName.indexOf("On"));
if(targetChildId == eventId){
// メソッド名から取得したidとターゲットのidが一致した場合は対象のイベントと
// メソッド名でイベントリスナーの登録を行う
eventName = getChildEventName(methodName);
targetChild.addEventListener(eventName, this[methodName], false, 0, true);
}
}
}
/**
* CreationCompleteイベント<br />
* イベント発生元画面のCreationCompleteイベント発生時にコールされる。<br />
*
* @param event FlexEvent
*/
private function _onCreationCompleteHandler(event:FlexEvent):void{
// リスナーの削除
this._view.removeEventListener(FlexEvent.CREATION_COMPLETE,
_onCreationCompleteHandler);
// 画面のCreationCompleteイベントをコール
onCreationCompleteHandler(event);
}
/**
* RemoveFromStageイベント<br />
* ステージの表示リストより画面が削除された場合にコールされる。<br />
* [1.0.1追加]<br />
*
* @since 1.0.1
* @param event Event
*/
private function onRemoveFromStageHandler(event:Event):void{
if(_helper){
// Helperクラスが存在する場合は初期化
this["helper"] = null;
_helper = null;
}
if(_logic){
// Logicクラスが存在する場合は初期化
this["logic"] = null;
_logic = null;
}
}
/**
* インスタンス生成処理<br />
* 命名規約に従ったクラスが存在する場合はインスタンスの生成を行う。<br />
* Helperクラス :変数名"helper"でパッケージ"helper"配下に"○○Helper.as"でクラスが作成されていればインスタンス生成<br />
* Logicクラス :変数名"logic"でパッケージ"logic"配下に"○○Logic.as"でクラスが作成されていればインスタンス生成<br />
* [1.0.1追加]<br />
*
* @since 1.0.1
* @param work インスタンスを保持するワーク用オブジェクト
* @param propName プロパティ名
* @param classPrefix クラスの最後に付加するプレフィックス名
*
*/
private function setDiInstance(work:Object, propName:String, classPrefix:String):void{
var actionClassPath:String;
var classPath:String;
var diClass:Class;
if(this.hasOwnProperty(propName)){
// 対象のプロパティ名が宣言されている場合アクションクラスの情報より対象クラスのパス取得
actionClassPath = flash.utils.getQualifiedClassName(this);
classPath = actionClassPath.split("::")[0].toString().replace(".action", "."+propName)+"::"+actionClassPath.split("::")[1].toString().replace("Action", classPrefix);
try{
// 対象のクラスパスよりクラスを取得
diClass = Class(flash.utils.getDefinitionByName(classPath));
}catch(referenceError:ReferenceError){
// 生成されたクラスパスにクラスが存在しない
// (恐らく命名規約通りにクラスが定義されていない)
diClass = null;
}
if(diClass){
// 対象クラスが正しく取得できた場合はインスタンス生成
this[propName] = new diClass();
work = this[propName];
}
}
}
/**
* 画面のCreationCompleteイベント<br />
* 画面側で利用する場合はOverrideして利用する。<br />
*
* @param event FlexEvent
*/
protected function onCreationCompleteHandler(event:FlexEvent):void{}
/**
* 画面のInitialize処理<br />
* 画面側で利用する場合はOverrideして利用する。<br />
*/
protected function onInitializeComplete():void{}
}
}
DIっぽい動作
以前作成した『画面とロジックの分離について』のソースにDIっぽい動作を追加してみた。
最近よくyuiフレームワークを利用しているので似た感じで実装。
Viewクラス、Actionクラス、Helperクラス、Logicクラスを用意し、下記ネーミングルールで作成する前提。
|
種類 |
パッケージ |
クラス名 |
| Viewクラス | /view | ○○View.mxml |
| Actionクラス | /action | ○○Action.as |
| Helperクラス | /helper | ○○Helper.as |
| Logicクラス | /logic | ○○Logic.as |
/*
* System Name : NkeFlexCommon
*=======================================================================================
* 作成日 : 2010/03/20 んけさん
* 最終更新日 : 2010/04/04 んけさん DIっぽい動作を追加
*=======================================================================================
* [修正履歴]
* <日付> <担当者> <Version> <修正内容>
*---------------------------------------------------------------------------------------
* 2010/03/20 んけさん 1.0.0 新規作成
* 2010/04/04 んけさん 1.0.1 logic, helperが定義されている場合はインスタンス
* の自動生成を行う様に修正。ステージより削除される
* タイミングで初期化する様に修正。
*/
package com.rikopapa.common.action{
import flash.events.Event;
import flash.utils.getQualifiedClassName;
import mx.core.IMXMLObject;
import mx.events.FlexEvent;
/**
* 画面アクションクラス<br />
* 画面とアクションクラスの紐付けを行う。<br />
*
* @author んけさん
* @version 1.0.1
*/
public class NkeViewAction implements IMXMLObject{
/**
* アクションクラスID<br />
*/
private var _id:String;
/**
* アクションクラスID<br />
*
* @default null
*/
public function get id():String{
return _id;
}
/**
* イベント発生元画面インスタンス<br />
*/
protected var _view:Object;
/**
* Helperクラスインスタンス<br />
*/
private var _helper:Object;
/**
* Logicクラスインスタンス<br />
*/
private var _logic:Object;
/**
* デフォルトコンストラクタ<br />
*/
public function NkeViewAction(){
super();
}
/**
* initialized処理<br />
*
* @param document イベント発生元画面インスタンス
* @param id アクションクラスID
*
*/
public function initialized(document:Object, id:String):void{
// イベント発生元画面インスタンスとアクションクラスIDを保持
_view = document;
_id = id;
// CreationCompleteイベントリスナー登録
this._view.addEventListener(FlexEvent.CREATION_COMPLETE,
_onCreationCompleteHandler,
false,
0,
true);
// removedFromStageイベントリスナー登録[1.0.1追加]
this._view.addEventListener(Event.REMOVED_FROM_STAGE,
onRemoveFromStageHandler,
false,
0,
true);
// 画面のInitialize処理をコール
onInitializeComplete();
}
/**
* CreationCompleteイベント<br />
* イベント発生元画面のCreationCompleteイベント発生時にコールされる。<br />
*
* @param event FlexEvent
*/
private function _onCreationCompleteHandler(event:FlexEvent):void{
// リスナーの削除
this._view.removeEventListener(FlexEvent.CREATION_COMPLETE,
_onCreationCompleteHandler);
// helperクラスのインスタンス生成[1.0.1追加]
setDiInstance(_helper, "helper", "Helper");
// logicクラスのインスタンス生成[1.0.1追加]
setDiInstance(_logic, "logic", "Logic");
// 画面のCreationCompleteイベントをコール
onCreationCompleteHandler(event);
}
/**
* RemoveFromStageイベント<br />
* ステージの表示リストより画面が削除された場合にコールされる。<br />
* [1.0.1追加]<br />
*
* @since 1.0.1
* @param event Event
*/
private function onRemoveFromStageHandler(event:Event):void{
if(_helper){
// Helperクラスが存在する場合は初期化
this["helper"] = null;
_helper = null;
}
if(_logic){
// Logicクラスが存在する場合は初期化
this["logic"] = null;
_logic = null;
}
}
/**
* インスタンス生成処理<br />
* 命名規約に従ったクラスが存在する場合はインスタンスの生成を行う。<br />
* Helperクラス :変数名"helper"でパッケージ"helper"配下に"○○Helper.as"でクラスが作成されていればインスタンス生成<br />
* Logicクラス :変数名"logic"でパッケージ"logic"配下に"○○Logic.as"でクラスが作成されていればインスタンス生成<br />
* [1.0.1追加]<br />
*
* @since 1.0.1
* @param work インスタンスを保持するワーク用オブジェクト
* @param propName プロパティ名
* @param classPrefix クラスの最後に付加するプレフィックス名
*
*/
private function setDiInstance(work:Object, propName:String, classPrefix:String):void{
var actionClassPath:String;
var classPath:String;
var diClass:Class;
if(this.hasOwnProperty(propName)){
// 対象のプロパティ名が宣言されている場合アクションクラスの情報より対象クラスのパス取得
actionClassPath = flash.utils.getQualifiedClassName(this);
classPath = actionClassPath.split("::")[0].toString().replace(".action", "."+propName)+"::"+actionClassPath.split("::")[1].toString().replace("Action", classPrefix);
try{
// 対象のクラスパスよりクラスを取得
diClass = Class(flash.utils.getDefinitionByName(classPath));
}catch(referenceError:ReferenceError){
// 生成されたクラスパスにクラスが存在しない
// (恐らく命名規約通りにクラスが定義されていない)
diClass = null;
}
if(diClass){
// 対象クラスが正しく取得できた場合はインスタンス生成
this[propName] = new diClass();
work = this[propName];
}
}
}
/**
* 画面のCreationCompleteイベント<br />
* 画面側で利用する場合はOverrideして利用する。<br />
*
* @param event FlexEvent
*/
protected function onCreationCompleteHandler(event:FlexEvent):void{}
/**
* 画面のInitialize処理<br />
* 画面側で利用する場合はOverrideして利用する。<br />
*/
protected function onInitializeComplete():void{}
}
}
次はイベントの自動ハンドリングを実装してみる予定。
自律型トランザクションについて
呼び出し元のトランザクションとは独立したトランザクションを宣言する方法。
ログテーブルへの出力等でよく利用する。
・ログ出力するサンプル
CREATE OR REPLACE PROCEDURE SET_ERROR_LOG (ERR_CD IN NUMBER, ERR_MSG IN VARCHAR2, USER_ID IN VARCHAR2) IS -- 自律型トランザクションプラグマの宣言 PRAGMA AUTONOMOUS_TRANSACTION; BEGIN -- ログテーブルに出力 INSERT INTO ERROR_LOG( SEQ_NO, ERR_CD, ERR_MSG, INS_ID, INS_DATE )VALUES( ERROR_LOG_SEQ.nextval, ERR_CD, ERR_MSG, USER_ID, SYSDATE ); -- コミット -- ※自律型トランザクションの場合、トランザクションを終了しないとエラーとなってしまう COMMIT; END; /
・呼び出し元のサンプル
CREATE OR REPLACE PROCEDURE TEST_LOG
IS
BEGIN
-- NOT NULLの項目にINSERTしているので必ずエラーとなる
INSERT INTO NKE_TABLE(
NKE01
)VALUES(
NULL
);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ERROR:'||SQLCODE||' - '||SQLERRM);
-- エラーログ出力
SET_ERROR_LOG(SQLCODE, SQLERRM, 'NKE');
-- ロールバック
ROLLBACK;
END;
/
呼び出し元のサンプルを実行
ログテーブルを参照
よく使うけど忘れがちなコマンド等 その2
テーブル表(配列?)
1)既存テーブルと同じ構成のテーブル表作成
TYPE [テーブル表名] IS TABLE OF [テーブル名]%ROWTYPE INDEX BY BINARY_INTEGER;
2)項目名指定
TYPE [レコード名] IS RECORD( [項目名] [型], [項目名] [型], [項目名] [型], … ); TYPE [テーブル表名] IS TABLE OF [レコード名] INDEX BY BINARY_INTEGER;
3)フェッチ
FETCH [カーソル名] INTO [レコード名]
※FOR分でカーソル分ループする場合
FOR [レコード名] IN [カーソル名] LOOP … END LOOP;
※カーソル宣言なしでSELECT文で取得
FOR [レコード名] IN (SELECT [項目名] FROM [テーブル名]) LOOP … END LOOP;
マニフェストファイルの設定
eclipseでライブラリプロジェクトの名前空間 URL及びマニフェストファイルを指定する方法。
1)マニフェストファイルの作成
ソースフォルダ内に任意の名前でマニフェストファイル(XML)を作成。
[NkeFlexCommonManifest.xml]
<?xml version="1.0"?> <componentPackage> <!-- Controls --> <component id="NkeTextInput" class="com.rikopapa.common.controls.NkeTextInput"/> <component id="NkeButton" class="com.rikopapa.common.controls.NkeButton"/> <!-- Containers --> <component id="NkeCanvas" class="com.rikopapa.common.containers.NkeCanvas"/> <component id="NkeBox" class="com.rikopapa.common.containers.NkeBox"/> <component id="NkeHBox" class="com.rikopapa.common.containers.NkeHBox"/> <component id="NkeVBox" class="com.rikopapa.common.containers.NkeVBox"/> </componentPackage>
2)名前空間 URL及びマニフェストファイルの設定
プロジェクトのプロパティより『Flex ライブラリコンパイラ』を選択し、名前空間 URLとマニフェストファイルを設定する。
これでライブラリプロジェクトを参照しているプロジェクトから名前空間でライブラリプロジェクトのクラスを利用する事ができる。
<rikopapa:NkeCanvas xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:rikopapa="http://rikopapa.com" xmlns:action="com.rikopapa.sample.action.*" width="100%" height="100%">
名前空間 URLに指定した値の「http://」の次の箇所がデフォルトでネームスペースとして挿入される。
上記例のように「http://rikopapa.com」を指定した場合、「rikopapa」がデフォルトで利用される。
Author:りこパパ
FC2ブログへようこそ!
