« 2008年03月30日 - 2008年04月05日 | メイン | 2008年04月13日 - 2008年04月19日 »



2008年04月06日 - 2008年04月12日 アーカイブ

2008年04月06日

やる夫がデザインパターンをやるようです 第8回

≪第7回第9回≫

ワタシはC++からオブジェクト指向言語に触れたので、newしたインスタンスは必ず明示的にdeleteするという作法が体に染み付いております。スマートポインタなどを利用することもありますが、基本的にはアロケートした領域は自分で開放するのが当然であると思っていて、このことがワタシにとってファクトリの理解を阻むものだったのでは?と今では思っています。 今回のファクトリパターンはインスタンスをnewして返すものなので、開放はどこで行うべきかを意識する必要があるのですが、できればnewを押し付けたりしないで、自分でメモリのアロケート→開放を管理したいというのが当時ワタシが思っていたことでした。

とまぁ、ワタシのことはどうでもよいのですがやる夫の続きです。
インスタンスの生成くらいでごちゃごちゃ規制されたくないお
デザインパターンだかなんだか知らんが、
おいらのようなスーパーハカーに任せておけばプロジェクトは成功するお
        ノ L____
       ⌒ \ / \
      / (○) (○)\
     /    (__人__)   \
     |       |::::::|     |
     \       l;;;;;;l    /l!| !
     /     `ー'    \ |i
   /          ヽ !l ヽi
   (   丶- 、       しE |そ  ドンッ!!
    `ー、_ノ       ∑ l、E ノ <
               レY^V^ヽl 





   ふふーふーん♪コンストラクタをたくさん用意して
   みんなに「使いやすいっ」て言われるクラスをつくるお
   間口を広げておけばきっとたくさん釣れr使ってくれるお
           ____ 
        /⌒  ⌒\ 
.      /( ●)  (●)\ 
     / ::::::⌒(__人__)⌒::::: \ 
    |      |r┬-|     |  
     \       `ー'´     / 
    ノ            \ 
  /´               ヽ                 カ 
 |    l   l||l 从人 l||l      l||l 从人 l||l   カ    タ 
 ヽ    -一''''''"~~``'ー--、   -一'''''''ー-、.     タ 
  ヽ ____(⌒)(⌒)⌒) )  (⌒_(⌒)⌒)⌒)) 
     ┌┬┬┐┌┬┬┬┐┌┬┬┬┐┌┬┬┬┐ 
  ,. - ''"| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ρ ̄`l 
   ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ノ ̄ 






   このまま続けちゃったらソースコメントに
   // やるお神光臨ktkr(よくやった。アーキより)
   なんてかかれちゃうお
         ___ 
       / ⌒  ⌒\  
      / (●)  (●) \ 
    /   ///(__人__)/// \ 
     |   u.   `Y⌒y'´    |
      \       ゙ー ′  ,/
      /⌒ヽ   ー‐    ィヽ
      / rー'ゝ       〆ヽ
    /,ノヾ ,>      ヾ_ノ,|
    | ヽ〆        |´ | 






<1週間後>
  バグの修正したらコンパイルがとおらなくなったお。。。
  おいらのクラスにさらにコンストラクタ追加したボケはだれだお。
  ::::::::  ::    ::     :::::  ::  :::
  ::::::  ::   ____   :::::  ::::  :::
  ::::::  :::: /  :::  \ :::   :::   ::
  ::::  ::::/   ::     \::  :::  ::
 ::::  /:::    ─    ─ \ ::  ::
 :::  |  ::   .(○)  (○)  | :  ::::
  :::   \     (__人__)  ,/ :  :::
  ::  ノ      ` ⌒´   \ :   :::
  /´               ヽ  :::
 |    l              \:::
 ヽ    -一''''''"~~``'ー--、   -一'''''''ー-、.
  ヽ ____(⌒)(⌒)⌒) )  (⌒_(⌒)⌒)⌒))







<2週間後>
           ___
     ____,./      \ コンパイルを通したら
    ノ   /         \  新しいバグがいくつもでてきたお。。。
  /   /            \
 |     |::..           ...::::| 
 ヽ    `一ー――――-、;;;;::/`一ー―-、
  ヽ____(⌒)(⌒)⌒) )  (⌒_(⌒)⌒)⌒))







      / ̄ ̄\てめぇのアタマをデバグしてから出社しやがれ
    /ノ( _ノ  \  
    | ⌒(( ●)(●)
    .|     (__人__) /⌒l 
     |     ` ⌒´ノ |`'''| 
    / ⌒ヽ     }  |  |               ぺきょ
   /  へ  \   }__/ /             / ̄ ̄\ 
 / / |      ノ   ノ           / ●)) ((●\’, ・ 
( _ ノ    |      \´       _    (   (_人_)’∴ ),
       |       \_,, -‐ ''"   ̄ ゙̄''―---└'´ ̄`ヽ   て 
       .|                  ______ ノ    ( 
       ヽ           _,, -‐ ''"  ノ       ヽ   r'
         \       , '´        し/..     | J 
          \     (           /      | 
            \    \         し-  '^`-J 

<シンプルファクトリ>

というわけで、まずシンプルファクトリについてソースを含めて解説してみます。
// ファクトリ
class SimpleFactory {
    public Game createGame( String name ){
        Game gameInst = null;
        if( name.equals( "やるおの大冒険" ) ){
            gameInst = new ActionGame();
        }
        else if( name.equals( "ニコニコクロニクル" ) ){
            gameInst = new RPGGame();
        }
        // ほかにもたくさん。
        return gameInst;
    }
}

// ニコニコゲーム製作会社クラス
class NiconicoGameCompany {
    SimpleFactory fctryInst;
    // コンストラクタでファクトリをもらう。
    public NiconicoGameCompany( SimpleFactory param ){
        this.fctryInst = param;
    }
    
    public Game orderGame( String name ){
        Game gameInst;
        
        // ファクトリを使ってゲーム名を渡して返してもらう。
        gameInst = fctryInst.createGame( name );
        
        gameInst.prepare();
        gameInst.produce();
        gameInst.checkProduct();
        gameInst.package();
        return gameInst;
    }
}
ソースは上記のように書いてみました。一見するとファクトリは適宜インスタンスをnewして返すだけなので、自分でnewすりゃいいじゃんかと思うかもしれませんが、ソースのNiconicoGameCompanyクラス以外でGameクラスを扱うゲーム製作会社クラスがファクトリにnewを委譲してしまうことで、先に書いたやる夫のようなハマリ方はなくなります。
この使い方そのものはシンプルファクトリなどと呼ばれていて、SimpleFactoryが返すcreateGameの部分はスタティックにして使われたりすることがよくあります。

<ファクトリメソッド>

■ファクトリとプロダクト(工場と製品)
ファクトリメソッドは、
  1. ゲーム製作会社(:ファクトリ)スーパークラス(抽象クラス)を用意して
  2. これを継承するサブクラスで、インスタンスの生成を拡張してデザインされるものです。
ゲーム製作会社は、NiconicoGameCompany以外にもNyantendoGameCompany、SquareEnixoなどが考えられます。これらのゲーム製作会社がGameCompanyクラスを継承して、createGameを実装することで、独自のゲーム製作方法をそれぞれの製作会社が保持することになります。
そのため、プロダクトとなるゲームもサブクラスで拡張するようになります。生成される側のゲームのほうはといいますと、
  1. ゲーム(:プロダクト)スーパークラス(抽象クラス)を用意して
  2. これを継承するゲームサブクラスで、その機能を拡張してデザインされるものです。
これらのデザインを実現するには、上述シンプルファクトリのSimpleFactoryクラス、createGame()メソッドをGameCompanyに持たせることになります。このメソッドは以下のようにGameCompanyの抽象メソッドとし、
    // ファクトリオブジェクトから受け取ったcreateGameメソッド
    abstract Game createGame( String name );
GameCompanyを継承したサブクラスが製作方法を(オーバーライド)実装することで拡張していくのです。

■変更部分の切り出し
シンプルファクトリにも実装されていますが、orderGame()メソッドは、ゲーム製作の過程が実装されています。
  • ゲーム製作の準備(prepare)
  • 製作(produce)
  • 試験(checkProduct)
  • パッケージ(package)
これらは将来変更されることはないでしょう。ゲーム製作というものは、準備をして製作、試験をおこなってからパッケージして販売されるものです。これらのゲーム製作過程そのものがドラスティックにしょっちゅう変更されるということは、ビジネスモデルそのものを抽象化できていないということです。分析からやり直したほうがよいかもしれません。
ちょっと強引な話の進め方ですが、変更や追加・削除されていく箇所は上記のorderGame()ではなく、「どんなゲームをつくるのか?」になります。つまりcreateGame()になります。ファクトリメソッドはこの変更部分を分離してcreateGame()メソッドとしてサブクラスがオーバーライドするという仕組みです。(毎度のことながら、ゲーム会社のゲーム製作工程はよく知りません。話を進める上でのことですので誤りなどありましても気にしないでください。)
// ゲーム製作会社スーパークラス
abstract class GameCompany {
    public Game orderGame( String name ){
        // サブクラスに作成するゲームを決定させる
        Game gameInst = createGame( name );

        gameInst.prepare();
        gameInst.produce();
        gameInst.checkProduct();
        gameInst.package();
        return gameInst;
    }
    
    // ファクトリオブジェクトから受け取ったcreateGameメソッド
    abstract Game createGame( String name );
}
class NiconicoGameCompany extends GameCompany {
    // サブクラスが作成するゲームを決定する
    Game createGame( String name ){
        Game gameInst = null;
        if( name.equals( "やるおの大冒険" ) ){
            gameInst = new ActionGame();
        }
        else if( name.equals( "ニコニコクロニクル" ) ){
            gameInst = new RPGGame();
        }
        // ほかにもたくさん。
        return gameInst;
    }
}

// ゲームクラス。
abstract class Game {
    String category;
    void prepare(){ System.out.println( "設計" ); }
    void produce(){ System.out.println( "製作" ); }
    void checkProduct(){ System.out.println( "テスト" ); }
    void package(){ System.out.println( "パッケージ" );}
}

// ゲームクラスを継承して実装
class ActionGame extends Game {
    public ActionGame(){
        category = "アクション"
    }
    void package(){
        System.out.println( "アクションはパッケージがかっこよくないと!" );
    }
}
     ____       ヽ v / 
   /⌒  ⌒\     -(m)-   
  /( ●)  (●)\       ≡ 
/::::::⌒(__人__)⌒:::::\    
|      `ー'´     | 前々から「変更」するであろう箇所を
/     ∩ノ ⊃  /    抜き出してクラス化・メソッド化してたお。
(  \ / _ノ |  |   今回はnewするインスタンスをいろんな条件に
.\ “  /__|  |    基づいて変えるからここが「変更」箇所だお
  \ /___ / 







   / ̄ ̄\  ( ;;;;( 
 / _ノ  ヽ\ ) ;;;;) 
 |  ( ○) (○)/;;/   
. |   (__人__) l;;,    
  |    ∩ ノ)━・'/   
.  |   /  ノ´ } 条件文がいっぱいあるから
.  ヽ  / /    } 「変更」がかかる箇所ってわけじゃねーだろjk
   ヽ/ /   ノ 







      ____ インスタンスの生成を切り離しておくことで
    /_ノ   ヽ_\たくさんあるゲームの種類の生成が
   /( ●) ( ●)\管理しやすくなったお。
 / ::::::⌒(__人__)⌒:::::\
 |        ̄      |
 \              / 








     ____これ以上おいらが天才になったら
    / ⌒  ⌒  \アーキテクトの立場がなくなるから
  ./( ―) ( ●)  \馬鹿のフリしとくお。
  /::⌒(_人_)⌒:::::  | チラッ
  |    ー       .|
  \          / 







        / ̄ ̄\
      /       \
      |::::::        | とりあえずお前が作りこんだバグは
     . |:::::::::::     | 徹夜してでもなんとかしとけ
       |::::::::::::::    |          ....,:::´, .
     .  |::::::::::::::    }          ....:::,,  ..
     .  ヽ::::::::::::::    }         ,):::::::ノ .
        ヽ::::::::::  ノ        (:::::ソ: .
        /:::::::::::: く         ,ふ´..
-―――――|:::::::::::::::: \ -―,――ノ::ノ――
         |:::::::::::::::|ヽ、二⌒)━~~'´ 







           ____ 
        /⌒  ⌒\ ファクトリわかったから
.      /( ●)  (●)\ 徹夜なんてなんてことないお
     / ::::::⌒(__人__)⌒::::: \ 
    |      |r┬-|     |  
     \       `ー'´     / 
    ノ            \ 
  /´               ヽ                 カ 
 |    l   l||l 从人 l||l      l||l 从人 l||l   カ    タ 
 ヽ    -一''''''"~~``'ー--、   -一'''''''ー-、.     タ 
  ヽ ____(⌒)(⌒)⌒) )  (⌒_(⌒)⌒)⌒)) 
     ┌┬┬┐┌┬┬┬┐┌┬┬┬┐┌┬┬┬┐ 
  ,. - ''"| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ρ ̄`l 
   ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ノ ̄ 






    |┃三 ガラッ
    |┃  ____
    |┃/⌒  ⌒\
    |┃(●)  (●) \
――‐.|┃:⌒(__人__)⌒:::::\  徹夜すると居室に誰もいなくなって
    |┃  |r┬-|     |⌒)サイコーだお
    |┃   `ー'ォ     //
    (⌒ヽ・    ・ ̄ /
    |┃ノ       /
    |┃   つ   <
    |┃  (::)(::)   ヽ
    |┃/    >  )
    |┃     (__)


    |┃
    |┃  ____
    |┃/⌒  ⌒\
    |┃ (―)  (―)\  フヒー。
――‐.|┃:⌒(__人__)⌒:::::\
    |┃           |
    |┃          /
    |┃ヽ・    ・ ̄ /
    |┃ \    ,.:∴~・:,゜・~・:,゜・ ,
    |┃ヽ_)つ‘∴・゜゜・・∴~・:,゜・・∴
    |┃  (::)(::)  ヽ    ・゜゜・∴~゜
    |┃/    >  )    ゜゜・∴:,゜・~
    |┃     (__)    :,゜・~:,゜・゜゜・~ 











   / ̄ ̄\  やる夫。徹夜ごくろうs
 /   /  \
 |    ( ○)(○)
. |     (__人_(
  |     ` ⌒´(
.  |         }
.  ヽ        }
   ヽ     ノ











    |┃
    |┃  ____
    |┃/⌒  ⌒\
    |┃ (○)  (○)\  お
――‐.|┃:⌒(__人__)⌒:::::\
    |┃           |
    |┃          /
    |┃ヽ・    ・ ̄ /
    |┃ \    ,.:∴~・:,゜・~・:,゜・ ,
    |┃ヽ_)つ‘∴・゜゜・・∴~・:,゜・・∴
    |┃  (::)(::)  ヽ    ・゜゜・∴~゜
    |┃/    >  )    ゜゜・∴:,゜・~
    |┃     (__)    :,゜・~:,゜・゜゜・~ 














        ____
       /      \  ち、違うんです刑事さん・・・。
     /  ─    ─\
    ./    (○)  (○) \
    |       (__人__)    |、
r―n|l\      ` ⌒´   ,/ ヽ
  \\\.` ー‐ ' .// l     ヽ
.     \        |      |
.       \ _  __ | ._   |
        /,  /_ ヽ/、 ヽ_|
\      // /<  __) l -,|__) >
  \.    || | <  __)_ゝJ_)_>
    \.   ||.| <  ___)_(_)_ >
      \_| |  <____ノ_(_)_ ) 
というわけで次回はアブストラクトファクトリでファクトリパターンを締めたいと思います。 ≪第7回第9回≫


[ 最近のエントリーとその関連エントリー ]


[ スポンサードリンク ]

2008年04月10日

SharePointウェブサービスとExtJS連携でmoss2007 viewer for developers

moss2007viewer.PNG SharePointのビューアを作ってみました。
ExtJSの使い方を勉強しながらだったのでソースは汚いです。
使ったウィジェットは
  • Ext.grid.EditorGridPanel(エクセルみたいなパネル)
  • Ext.TabPanel(タブパネル)
  • Ext.form.ComboBox(ドロップダウンリスト)
  • Ext.DataView(データビュー。アイテムのソートやフィルタなんかができる)
  • Ext.Window(ウィンドウがポップする)
  • Ext.Panel(普通のパネル)
などです。これだけ使ったのでなんとなくExtJSをどうやって使うのか理解できてきましたが、基本的にはサンプルソースを見ながらいろいろといじくってできたものです。あまり参考にならないかもしれませんし、正しい実装ではないかもしれません。
今回の実装は、基本的にはウェブサービスが返すXMLのビューアみたいな使い方をしています。使っているうちに、そういう風に使うことが効果的なのかなと思いました。
ほいでは、以下にnoticeを記します。アーカイブのダウンロードもできるようにしておきますが、免責事項として、ご利用いただいたスクリプトにて生じるいかなる損害についても責任を負いません。

<moss2007 viewer for developersについて>

moss2007 viewer for developers は、サイト・ライブラリ・フィールドの要素や属性などのビューアです。ホストに対してサイト・ライブラリ・フィールドの変更要求は投げていません。SharePointに関係する開発や管理者向けのビューアです。

moss2007のドキュメントライブラリなどにディレクトリごとアップロードしてください
ローカルの端末から使う場合は、moss.jsの71行目

httpInst.open( "POST", "/_vti_bin/SiteData.asmx", false );

の箇所に以下のようにホストヘッダを追加することで使えます。

httpInst.open( "POST", "http://hogehoge/_vti_bin/SiteData.asmx", false );

<使い方>

「▼サイトURLを選んでください」ドロップダウンより、サイトを選んでエンターキーを押下すると、サイトが保持するライブラリ一覧がタブ(LibDef:)で追加されます。また、「▼ライブラリを選んでください」ドロップダウンに一覧が設定されます。
「▼ライブラリを選んでください」ドロップダウンより、ライブラリを選んでエンターキーを押下すると、ライブラリのフィールド一覧と定期情報タブ(FldDef:)が追加されます。FldDef:タブ内の左ペインのフィールド情報をクリックすると、右ペインに詳細な情報が表示されます。ダブルクリックすると、該当のフィールドを加えて、ライブラリアイテム一覧がタブが追加されます。

ダウンロード
String.prototype.ellipse = function(maxLength){
    if(this.length > maxLength){
        return this.substr(0, maxLength-3) + '...';
    }
    return this;
};


// ----------------------------------------------------------------------------
// tabCloseFunc.
// ----------------------------------------------------------------------------
Ext.ux.TabCloseMenu = function(){
    var tabs, menu, ctxItem;
    this.init = function(tp){
        tabs = tp;
        tabs.on('contextmenu', onContextMenu);
    }

    function onContextMenu(ts, item, e){
        if(!menu){
            menu = new Ext.menu.Menu([{
                id: tabs.id + '-close',
                text: 'Close Tab',
                handler : function(){
                    tabs.remove(ctxItem);
                }
            },{
                id: tabs.id + '-close-others',
                text: 'Close Other Tabs',
                handler : function(){
                    tabs.items.each(function(item){
                        if(item.closable && item != ctxItem){
                            tabs.remove(item);
                        }
                    });
                }
            }]);
        }
        ctxItem = item;
        var items = menu.items;
        items.get(tabs.id + '-close').setDisabled(!item.closable);
        var disableOthers = true;
        tabs.items.each(function(){
            if(this != item && this.closable){
                disableOthers = false;
                return false;
            }
        });
        items.get(tabs.id + '-close-others').setDisabled(disableOthers);
        menu.showAt(e.getPoint());
    }
};

// ----------------------------------------------------------------------------
// XMLHttpRequest Func.
// ----------------------------------------------------------------------------
function getHttpReq(){
    return window.XMLHttpRequest ? new XMLHttpRequest() : ( function() {
        try        { return new ActiveXObject( "Msxml2.XMLHTTP" );    }
        catch( e ) { return new ActiveXObject( "Microsoft.XMLHTTP" ); }
    }());
}

// ----------------------------------------------------------------------------
// SiteData.asmx::GetSite Func.
// ----------------------------------------------------------------------------
function getSiteHttpInst()
{
    var httpInst = getHttpReq();
    var retHttpInst;
    httpInst.open( "POST", "/_vti_bin/SiteData.asmx", false );
    httpInst.setRequestHeader( 'Content-Type','text/xml; charset=utf-8' );
    httpInst.setRequestHeader( 'SOAPAction','http://schemas.microsoft.com/sharepoint/soap/GetSite' );
    var postBody = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetSite xmlns="http://schemas.microsoft.com/sharepoint/soap/" /></soap:Body></soap:Envelope>';

    httpInst.onreadystatechange = function () 
    {
        if ( httpInst.readyState == 4 ){
            retHttpInst  = httpInst;
        }
    }
    httpInst.send( postBody );
    return retHttpInst;
}

// ----------------------------------------------------------------------------
// Parse get SingleNode list Func.
// ----------------------------------------------------------------------------
function getSingleTagList( xml, tagNm )
{
    var urlList = new Array();
    var urlElem  = xml.getElementsByTagName( tagNm );
    var listSize = urlElem.length;
    for( idx = 0; idx < listSize; idx++ ){
       var tempList = [ urlElem[ idx ].firstChild.nodeValue ];
       urlList.push( tempList );
    }
    return urlList;
}

// ----------------------------------------------------------------------------
// SiteData.asmx::GetListCollection Func.
// ----------------------------------------------------------------------------
function getListCollectionHttpInst( url )
{
    var httpInst = getHttpReq();
    httpInst.open( "POST",  url + "/_vti_bin/SiteData.asmx", false );
    httpInst.setRequestHeader( 'Content-Type','text/xml; charset=utf-8' );
    httpInst.setRequestHeader( 'SOAPAction','http://schemas.microsoft.com/sharepoint/soap/GetListCollection' );
    var postBody = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetListCollection xmlns="http://schemas.microsoft.com/sharepoint/soap/" /></soap:Body></soap:Envelope>';
    var retHttpInst;
    httpInst.onreadystatechange = function () 
    {
        if ( httpInst.readyState == 4 ){
            retHttpInst = httpInst;
        }
    }
    httpInst.send( postBody );
    return retHttpInst;
}

// ----------------------------------------------------------------------------
// Lists.asmx::GetList Func.
// ----------------------------------------------------------------------------
function getLibHttpInst( url, listNm )
{
    var httpInst = getHttpReq();
    var retHttpInst;
    httpInst.open( "POST", url + "/_vti_bin/Lists.asmx", false );
    httpInst.setRequestHeader( 'Content-Type','text/xml; charset=utf-8' );
    httpInst.setRequestHeader( 'SOAPAction','http://schemas.microsoft.com/sharepoint/soap/GetList' );
    var postBody = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetList xmlns="http://schemas.microsoft.com/sharepoint/soap/"><listName>' + listNm + '</listName></GetList></soap:Body></soap:Envelope>';

    httpInst.onreadystatechange = function () 
    {
        if ( httpInst.readyState == 4 ){
            retHttpInst  = httpInst;
        }
    }
    httpInst.send( postBody );
    return retHttpInst;
}

// ----------------------------------------------------------------------------
// get Grid Func.
// ----------------------------------------------------------------------------
function getSiteDataGrid( xml )
{
    
    var tagList = [ 
        "InternalName", 
        "Title", 
        "BaseTemplate", 
        "DefaultViewUrl", 
        "LastModified", 
        "InheritedSecurity", 
        "AllowAnonymousAccess", 
        "AnonymousViewListItems", 
        "ReadSecurity" 
    ];
    var dataAry = new Array();
    var headList = new Array();
    var clmnList = new Array();
    for( var idx = 0; idx < tagList.length; idx++ ){
        headList.push( { name: tagList[ idx ] } );
        clmnList.push( {header:tagList[ idx ],width:120,sortable:true,dataIndex:tagList[ idx ],editor:new Ext.form.TextField()} );
    }

    var listElem  = xml.getElementsByTagName( "_sList" );
    var listSize = listElem.length;
    for( var idx = 0; idx < listSize; idx++ ){
        var fieldDataList = new Array();
        for( var sdx = 0; sdx < tagList.length; sdx++ ){
            fieldDataList.push( xml.getElementsByTagName( tagList[ sdx ] )[ idx ].firstChild.nodeValue );
        }
        dataAry.push( fieldDataList );
    }
    var reader = new Ext.data.ArrayReader( {}, headList );
    var clmn = new Ext.grid.ColumnModel( clmnList );
    clmn.defaultSortable = true;

    var grid = new Ext.grid.EditorGridPanel({
        store: new Ext.data.GroupingStore({
            reader: reader,
            data: dataAry,
            sortInfo:{ field: 'LastModified', direction: "DESC" },
            groupField:'BaseTemplate'
        }),
        cm: clmn,
        clicksToEdit:1,
        stripeRows:true,
        view: new Ext.grid.GroupingView({
            hideGroupedColumn: true,
            groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
        }),
        frame:true
    });
    return grid;
}

// ----------------------------------------------------------------------------
// get Value List Func.
// ----------------------------------------------------------------------------
function getLibDataList( xml, atrList, parseTarget )
{
    var xmlDoc = new ActiveXObject( "Microsoft.XMLDOM" );
    xmlDoc.async = false;
    xmlDoc.loadXML( xml );
    var rowsItem = xmlDoc.selectNodes( '/soap:Envelope/soap:Body/GetListResponse/GetListResult/' + parseTarget );
    var dataAry = new Array();
    for( var idx = 0; idx < rowsItem.length; idx++ ){
        var fieldDataList = new Array();
        for( var sdx = 0; sdx < atrList.length; sdx++ ){
            fieldDataList.push( rowsItem[ idx ].getAttribute( atrList[ sdx ] ) );
        }
        dataAry.push( fieldDataList );
    }
    return dataAry;
}

// ----------------------------------------------------------------------------
// get Attribute List Func.
// ----------------------------------------------------------------------------
function getLibDataAtrList( xml, parseTarget )
{
    var xmlDoc = new ActiveXObject( "Microsoft.XMLDOM" );
    xmlDoc.async = false;
    xmlDoc.loadXML( xml );
    var rowsItem = xmlDoc.selectNodes( '/soap:Envelope/soap:Body/GetListResponse/GetListResult/' + parseTarget );
    var dataAry = new Array();
    var findFlg = false;
    for( var idx = 0; idx < rowsItem.length; idx++ ){
        for( var sdx = 0; sdx < rowsItem[ idx ].attributes.length; sdx++ ){
            for( var pushItemIdx = 0; pushItemIdx < dataAry.length; pushItemIdx++ ){
                if( dataAry[ pushItemIdx ] == rowsItem[ idx ].attributes.item( sdx ).name ){
                    findFlg = true;
                    break;
                }
            }
            if( !findFlg ){
                dataAry.push( rowsItem[ idx ].attributes.item( sdx ).name );
            }
            findFlg = false;
        }
    }
    return dataAry;
}

// ----------------------------------------------------------------------------
// get List Items Grid Func.
// ----------------------------------------------------------------------------
function getListItems( url, listName, staticNm, dispNm )
{
    var httpInst = getHttpReq();
    var dataAry = new Array();
    var grid;

    httpInst.open( "POST", url + "/_vti_bin/Lists.asmx", false );
    httpInst.setRequestHeader( 'Content-Type','text/xml; charset=utf-8' );
    httpInst.setRequestHeader( 'SOAPAction','http://schemas.microsoft.com/sharepoint/soap/GetListItems' );
    var postBody = '<?xml version="1.0" encoding="utf-8"?><soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"><soap12:Body><GetListItems xmlns="http://schemas.microsoft.com/sharepoint/soap/"><listName>' + listName + '</listName><viewName></viewName><query></query><viewFields><ViewFields><FieldRef Name="ID" /><FieldRef Name="Title" /><FieldRef Name="Editor" /><FieldRef Name="Author" /><FieldRef Name="Modified" /><FieldRef Name="Created" /><FieldRef Name="' + staticNm + '" /><FieldRef Name="UniqueId"/></ViewFields></viewFields><rowLimit></rowLimit><queryOptions><QueryOptions/></queryOptions><webID></webID></GetListItems></soap12:Body></soap12:Envelope>';

    httpInst.onreadystatechange = function()
    {
        if ( httpInst.readyState == 4 ){
            xmlDoc = new ActiveXObject( "Microsoft.XMLDOM" );
            xmlDoc.async = false;
            xmlDoc.loadXML( httpInst.responseText );
            rowsItem = xmlDoc.selectNodes( '/soap:Envelope/soap:Body/GetListItemsResponse/GetListItemsResult/listitems/rs:data/z:row' );

            for( idx = 0; idx < rowsItem.length; idx++ ){
                var fieldDataList = new Array();
                fieldDataList.push( rowsItem[ idx ].getAttribute( "ows_ID" ) );
                fieldDataList.push( rowsItem[ idx ].getAttribute( "ows_Title" ) );
                fieldDataList.push( rowsItem[ idx ].getAttribute( "ows_" + staticNm ) );
                fieldDataList.push( rowsItem[ idx ].getAttribute( "ows_Editor" ) );
                fieldDataList.push( rowsItem[ idx ].getAttribute( "ows_Author" ) );
                fieldDataList.push( rowsItem[ idx ].getAttribute( "ows_Modified" ) );
                fieldDataList.push( rowsItem[ idx ].getAttribute( "ows_Created" ) );
                dataAry.push( fieldDataList );
            }

            var reader = new Ext.data.ArrayReader( {}, [ 
                { name: 'ID' }, 
                { name: 'Title' },
                { name: staticNm },
                { name: 'Editor' },
                { name: 'Author' },
                { name: 'Modified' },
                { name: 'Created' }
            ]);

            var clmn = new Ext.grid.ColumnModel( [
                { header: 'ID', width: 120, sortable: true, dataIndex: 'ID',editor:new Ext.form.TextField() },
                { header: 'タイトル', width: 120, sortable: true, dataIndex: 'Title',editor:new Ext.form.TextField() },
                { header: dispNm, width: 120, sortable: true, dataIndex: staticNm,editor:new Ext.form.TextField() },
                { header: '更新者', width: 120, sortable: true, dataIndex: 'Editor',editor:new Ext.form.TextField() },
                { header: '作成者', width: 120, sortable: true, dataIndex: 'Author',editor:new Ext.form.TextField() },
                { header: '更新日時', width: 120, sortable: true, dataIndex: 'Modified',editor:new Ext.form.TextField() },
                { header: '作成日時', width: 120, sortable: true, dataIndex: 'Created',editor:new Ext.form.TextField() }
            ] );

            grid = new Ext.grid.EditorGridPanel({
                store: new Ext.data.GroupingStore({
                    reader: reader,
                    data: dataAry,
                    sortInfo:{ field: 'Modified', direction: "DESC" },
                    groupField:'Editor'
                }),
                cm: clmn,
                clicksToEdit:1,
                stripeRows:true,
                view: new Ext.grid.GroupingView({
                    hideGroupedColumn: true,
                    groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
                }),
                frame:true
            });
        }
    }
    httpInst.send( postBody );
    return grid;
}

Ext.onReady( function(){

    var isOpen = false;
    var tb = new Ext.Toolbar();
    var index = 0;

    var panel = new Ext.TabPanel({
        renderTo:'container',
        resizeTabs:true,
        minTabWidth: 235,
        tabWidth:235,
        enableTabScroll:true,
        width:720,
        height:480,
        tbar: tb,
        bodyStyle:'padding:12px;',
        defaults: {autoScroll:true},
        plugins: new Ext.ux.TabCloseMenu()
    });

    var siteLists = getSingleTagList( getSiteHttpInst().responseXML, 'Url' );
    var store = new Ext.data.SimpleStore({
        fields: [ 'siteurl' ],
        data : siteLists
    });

    var libCombo = new Ext.form.ComboBox({
        store: new Ext.data.SimpleStore( { fields: [ 'libnm' ] } ),
        displayField: 'libnm',
        typeAhead: true,
        mode: 'local',
        triggerAction: 'all',
        emptyText: '▼ライブラリを選んでください',
        selectOnFocus:true,
        listeners: {
            'specialkey': function( textField, evt ){
                if( evt.getKey() != Ext.EventObject.ENTER || combo.getValue() == '' || libCombo.getValue() == '' ) return;
                if( panel.getComponent( 'img-chooser-dlg' ) != null ){
                    panel.remove( panel.getComponent( 'img-chooser-dlg' ) );
                }
                var curHttpInst = getLibHttpInst( combo.getValue(), libCombo.getValue() );
                var atrList = getLibDataAtrList( curHttpInst.responseText, 'List/Fields/Field' );
                var libDataList = getLibDataList( curHttpInst.responseText, atrList, 'List/Fields/Field' );
                var lookup = {};
                var dataViewStore = new Ext.data.SimpleStore({
                    data: libDataList,
                    fields: atrList
                });
                var thumbTemplate = new Ext.XTemplate(
                    '<tpl for=".">',
                        '<div class="thumb-wrap" id="{StaticName}">',
                        '<div class="thumb"><input type="text" size="8" value="{DisplayName}"/></div>',
                        '<span class="x-editable">{shortName}</span></div>',
                    '</tpl>',
                    '<div class="x-clear"></div>'
                );
                var templateSet = '';
                for( var atrIdx = 0; atrIdx < atrList.length; atrIdx++ ){
                    templateSet = templateSet + '<b>' + atrList[ atrIdx ] + ':</b>' + '<span>{' + atrList[ atrIdx ] + '}</span>';
                }
                var detailsTemplate = new Ext.XTemplate(
                    '<div class="details">',
                        '<tpl for=".">',
                            '<img src="js/img/detail.gif"><div class="details-info">',
                            templateSet,
                        '</tpl>',
                    '</div>'
                );

                var formatData = function( data ){
                    data.shortName = data.Type.ellipse( 15 );
                    lookup[ data.StaticName ] = data;
                    return data;
                };

                var view = new Ext.DataView({
                    tpl: thumbTemplate,
                    singleSelect: true,
                    overClass:'x-view-over',
                    itemSelector: 'div.thumb-wrap',
                    store: dataViewStore,
                    listeners: {
                        'selectionchange': {fn:function(){
                            var selNode = view.getSelectedNodes();
                            var detailEl = Ext.getCmp('img-detail-panel').body;
                            if(selNode && selNode.length > 0){
                                selNode = selNode[0];
                                var data = lookup[ selNode.id ];
                                detailEl.hide();
                                detailsTemplate.overwrite(detailEl, data);
                                detailEl.slideIn('l', {stopFx:true,duration:.2});
                            }else{
                                detailEl.update('');
                            }
                        }},
                        'beforeselect' : {fn:function(view){
                            return view.store.getRange().length > 0;
                        }},
                        'dblclick' : {fn:function(){
                            var selNode = view.getSelectedNodes();
                            var detailEl = Ext.getCmp('img-detail-panel').body;
                            if(selNode && selNode.length > 0){
                                selNode = selNode[0];
                                var data = lookup[ selNode.id ];
                                var statNm = data.StaticName;
                                var dispNm = data.DisplayName;
                                var listGrid = getListItems( combo.getValue(), libCombo.getValue(), statNm, dispNm );
                                listGrid.closable = true;
                                listGrid.setTitle( libCombo.getValue() );
                                panel.add( listGrid ).show();
                            }
                        }}
                    },
                    prepareData: formatData.createDelegate( view )
                });

                var dataView = new Ext.Panel({
                    title:'FldDef: [ ' + libCombo.getValue() + ' ]',
                    id: 'img-chooser-dlg',
                    layout: 'border',
                    frame:true,
                    items:[{
                        id: 'img-chooser-view',
                        region: 'center',
                        autoScroll: true,
                        items: view,
                        tbar:[{
                            text: 'Filter:'
                        },{
                            xtype: 'textfield',
                            id: 'filter',
                            selectOnFocus: true,
                            width: 100,
                            listeners: {
                                'render': {fn:function(){
                                    Ext.getCmp('filter').getEl().on('keyup', function(){
                                        var filter = Ext.getCmp('filter');
                                        view.store.filter('DisplayName', filter.getValue());
                                    });
                                }}
                            }
                        }, ' ', '-', {
                            text: 'Sort By:'
                        }, {
                            id: 'sortSelect',
                            xtype: 'combo',
                            typeAhead: true,
                            triggerAction: 'all',
                            width: 100,
                            editable: false,
                            mode: 'local',
                            displayField: 'desc',
                            valueField: 'DisplayName',
                            lazyInit: false,
                            value: 'DisplayName',
                            store: new Ext.data.SimpleStore({
                                fields: ['DisplayName', 'desc'],
                                data : [['DisplayName', '表示名'],['Type', 'フィールド型'],['StaticName', 'StaticName']]
                            }),
                            listeners: {
                                'select': {fn:function(){
                                    var v = Ext.getCmp('sortSelect').getValue();
                                    view.store.sort(v, v == 'DisplayName' ? 'asc' : 'desc');
                                }}
                            }
                        }, ' ', '-', {
                            text: 'ライブラリ定義参照',
                            handler: onLibDefineBtnClick
                        }]
                    },{
                        id: 'img-detail-panel',
                        region: 'east',
                        autoScroll: true,
                        split: true,
                        width: 240,
                        minWidth: 240,
                        maxWidth: 300
                    }]
                });
                dataView.closable = true;
                panel.add( dataView ).show();
                var libDefWindow;

                function onLibDefineBtnClick( btn ){
                    var libDefAtrList = getLibDataAtrList( curHttpInst.responseText, 'List' );
                    var libDefList = getLibDataList( curHttpInst.responseText, libDefAtrList, 'List' );

                    var libDefData = new Array();
                    for( var idx = 0; idx < libDefAtrList.length; idx++ ){
                        var fieldDataList = new Array();
                        fieldDataList.push( libDefAtrList[ idx ] );
                        fieldDataList.push( libDefList[ 0 ][ idx ] );
                        libDefData.push( fieldDataList );
                    }
                    var reader = new Ext.data.ArrayReader( {}, [ 
                        { name: 'Attribute' }, 
                        { name: 'Value' }
                    ]);

                    var clmn = new Ext.grid.ColumnModel( [
                        { header: 'Attribute', width: 200, sortable: true, dataIndex: 'Attribute',editor:new Ext.form.TextField() },
                        { header: 'Value', width: 200, sortable: true, dataIndex: 'Value',editor:new Ext.form.TextField() }
                    ] );

                    var grid = new Ext.grid.EditorGridPanel({
                        store: new Ext.data.GroupingStore({
                            reader: reader,
                            data: libDefData
                        }),
                        cm: clmn,
                        clicksToEdit:1,
                        stripeRows:true,
                        frame:true
                    });

                    if( !libDefWindow ){
                        libDefWindow = new Ext.Window({
                            title:'[ ' + libCombo.getValue() + ' ] ライブラリの定義',
                            layout:'fit',
                            width:400,
                            height:300,
                            closeAction:'hide',
                            plain: true,
                            items:grid,
                            buttons: [{
                                text: 'Close',
                                handler: function(){
                                    libDefWindow.hide();
                                }
                            }]
                        });
                    }
                    libDefWindow.show();
                }
            }
        },
        width:300
    });

    var combo = new Ext.form.ComboBox({
        store: store,
        displayField: 'siteurl',
        typeAhead: true,
        mode: 'local',
        triggerAction: 'all',
        emptyText: '▼サイトURLを選んでください',
        selectOnFocus:true,
        listeners: {
            'specialkey': function( textField, evt ){
                if( evt.getKey() != Ext.EventObject.ENTER || combo.getValue() == '') return;
                var curHttpInst = getListCollectionHttpInst( combo.getValue() );
                libCombo.store.loadData( getSingleTagList( curHttpInst.responseXML, 'Title' ), false );
                var siteDataGrid = getSiteDataGrid( curHttpInst.responseXML );
                siteDataGrid.setTitle( 'LibDef: [ ' + combo.getValue() +' ]' );
                siteDataGrid.closable = true;
                siteDataGrid.frame = true;
                panel.add( siteDataGrid ).show();
            }
        },
        width:300
    });

    panel.getTopToolbar().add( combo );
    panel.getTopToolbar().add( "-", libCombo );
    panel.add({
        title: 'how to use',
        iconCls: 'tabs',
        html:document.getElementById( "description" ).innerHTML
    }).show();
    document.getElementById( "description" ).style.display = "none";
});

[ 最近のエントリーとその関連エントリー ]


2008年04月07日

SharePointのLists.asmxのGetListItemsの条件句XML

以前、SharePointのGetListItemsを使ってみましたというSharePointのポストをしたのですが、条件区であるqueryタグ部分に複数の条件を設ける方法がわかりませんでした。ちょっとSharePointをいじっていましたら<And>タグや<Or>タグで表記されている部分を見つけまして、このとおりにXMLを組み立てれば複数の条件でアイテムを取得できるんじゃないか?と思ってポストしました。(未テスト)
<query><Query><Where><Or><Eq><FieldRef Name="ID" /><Value Type="Counter">1</Value></Eq>
<Eq><FieldRef Name="ID" /><Value Type="Counter">2</Value></Eq></Or></Where></Query></query>
上記のようにID=1または、ID=2をもってきたい場合、Orタグでくくってあげればよさそうです。うまく使えば、柔軟なフィルタが表現できそうですね。これもそのうちちゃんとしたソースでポストします。

[ 最近のエントリーとその関連エントリー ]


咲と五月女の「咲」が3代目になります。

DSCF2340.jpg ストップモーションアニメ:「咲と五月女」の主人公である「咲」はペーパークラフトでつくられているのですが、6本も撮っているとギミックもかなりへたってきて、いろんなジョイント部分がぐらぐらになってきてしまいました。そのため咲はアルミとボルトで作られた3代目に世代交代することになります。
写真はその腕のギミックの部分です。ガッチリとボルトで間接を作っているので強度については十分ですし生産性も格段にあがりました。アニメを作る際にも腕がポキっと取れちゃうとかはなくなりそうです。ですが、紙の見た目のやわらかさがなくなってしまいました。どうしよう。あはは。なんとか温かみを表現しながら3代目を作ってみます。

[ 最近のエントリーとその関連エントリー ]


SharePointとExtJS連携でデータシートビューフォームを自前でつくったよ!(EditorGridPanelの使い方)

SharePointExtJSGrid.PNG やる夫で学ぶデザインパターンのアブストラクトファクトリをやるつもりでしたが、ちょっと横道にそれます。SharePointのフォームを自前で作るというエントリの続きです。
ExtJSにはEditorGridPanelというものがあるのですが、エクセルのような表をフォームとして扱えるウィジェットです。これをSharePointのフォームとして扱えれば、データシートビューのようなフォームが作れてしまいます。基本的にはLists.asmxのUpdateListItemsを使っているのですが、まとめて更新、新規作成なんかができちゃうので、ちゃんと作りこめばかなり便利なものになるかと思いますよ。
いかにサンプルソースを載せておきます。簡単ではありますが、ソースにコメントを残しておきました。ご自身の環境に合わせてパスなどを書き換えてみてください。また、このサンプルソースをそのまま使う場合はSharePoint側でライブラリ(カスタムリストかな)を作る際のフィールド名などもあわせてください。(ソースに記述しておきました。)
<link rel="stylesheet" type="text/css" href="<yourSitePath>/ext/resources/css/ext-all.css" />
<script type="text/javascript" src="<yourSitePath>/ext/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="<yourSitePath>/ext/ext-all.js"></script>
<script type="text/javascript">
function getHttpReq(){
    return window.XMLHttpRequest ? new XMLHttpRequest() : ( function() {
        try        { return new ActiveXObject( "Msxml2.XMLHTTP" );    }
        catch( e ) { return new ActiveXObject( "Microsoft.XMLHTTP" ); }
    }());
}

function getFieldItems()
{
    var dataAry = new Array();
    var libName = '<yourLibName>'

    // SharePointでのフィールド定義
    var targetFields = 
    '<FieldRef Name="ID" />' +          // デフォルト
    '<FieldRef Name="Title" />' +       // デフォルト
    '<FieldRef Name="selectClmn" />' +  // 選択肢フィールドを"selectClmn"で作成しておく
    '<FieldRef Name="numField" />' +    // 数値フィールドを"numField"で作成しておく
    '<FieldRef Name="dateField" />';    // 日付フィールドを"dateField"で作成しておく

    var httpInst = getHttpReq();
    httpInst.open( "POST", "<yourSitePath>/_vti_bin/Lists.asmx", false );
    httpInst.setRequestHeader( 'Content-Type','text/xml; charset=utf-8' );
    httpInst.setRequestHeader( 'SOAPAction','http://schemas.microsoft.com/sharepoint/soap/GetListItems' );
    var postBody = '<?xml version="1.0" encoding="utf-8"?><soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"><soap12:Body><GetListItems xmlns="http://schemas.microsoft.com/sharepoint/soap/"><listName>' + libName + '</listName><viewName></viewName><query></query><viewFields><ViewFields>' + targetFields + '</ViewFields></viewFields><rowLimit></rowLimit><queryOptions><QueryOptions/></queryOptions><webID></webID></GetListItems></soap12:Body></soap12:Envelope>';

    httpInst.onreadystatechange = function()
    {
        if ( httpInst.readyState == 4 ){
            xmlDoc = new ActiveXObject( "Microsoft.XMLDOM" );
            xmlDoc.async = false;
            xmlDoc.loadXML( httpInst.responseText );
            var rowsItem = xmlDoc.selectNodes( '/soap:Envelope/soap:Body/GetListItemsResponse/GetListItemsResult/listitems/rs:data/z:row' );

            for( idx = 0; idx < rowsItem.length; idx++ ){
                var fieldDataList = new Array();

                // 日付フィールドは "2008-04-07 13:00:00"で返ってくるので
                // "2008/04/07"にフォーマットを変換しておく。
                var dateRow = rowsItem[ idx ].getAttribute( "ows_dateField" ).replace( /-/g, "/" );
                dateRow = dateRow.substring( 0, dateRow.lastIndexOf( " " ) );

                // 数値フィールドは 0.0000000・・・で返ってくるのでroundする。
                var numRow = Math.round( rowsItem[ idx ].getAttribute( "ows_numField" ) );
                fieldDataList.push( rowsItem[ idx ].getAttribute( "ows_ID" ) );
                fieldDataList.push( rowsItem[ idx ].getAttribute( "ows_Title" ) );
                fieldDataList.push( rowsItem[ idx ].getAttribute( "ows_selectClmn" ) );
                fieldDataList.push( numRow );
                fieldDataList.push( dateRow );
                dataAry.push( fieldDataList );
            }
        }
    }
    httpInst.send( postBody );
    return dataAry;
}

function updateListItems( paramXML )
{
    var httpInst = getHttpReq();
    var libName = '<yourLibName>';
    httpInst.open( "POST", "<yourSitePath>/_vti_bin/Lists.asmx", false );
    httpInst.setRequestHeader( 'Content-Type','text/xml; charset=utf-8' );
    httpInst.setRequestHeader( 'SOAPAction','http://schemas.microsoft.com/sharepoint/soap/UpdateListItems' );

    var postBody = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><UpdateListItems xmlns="http://schemas.microsoft.com/sharepoint/soap/"><listName>' + libName + '</listName><updates>' + paramXML + '</updates></UpdateListItems></soap:Body></soap:Envelope>';
/*
    httpInst.onreadystatechange = function()
    {
        // エラーハンドリングはここで行う。
        if ( httpInst.readyState == 4 ){
        }
    }
*/
    httpInst.send( postBody );
    alert( "更新しました" );
}


Ext.onReady( function(){

    var changeList = new Array();

    var headAry = [
        { name : 'ID' },
        { name : 'Title' },
        { name : 'selectClmn' },
        { name : 'numField' },
        { name : 'dateField', type:'date', dateFormat: 'Y/m/d' }
    ];

   var clmn = new Ext.grid.ColumnModel([
        { 
            id : 'ID',
            header: "ID",
            dataIndex: 'ID',
            width: 50 
        },
        { 
            header: "タイトル", 
            dataIndex: 'Title', 
            width: 100, 
            editor: new Ext.form.TextField( 
            {
                allowBlank: false
            }) 
        },
        {
            // 選択肢フィールド定義はHTML下部のid:ddfieldより取得する。
            header: "選択肢フィールド", 
            dataIndex: 'selectClmn', 
            width: 100, 
            editor: new Ext.form.ComboBox({
                typeAhead: true,
                triggerAction: 'all',
                transform:'ddfield',
                lazyRender:true
             })
        },
        {
            header: "数値フィールド",
            dataIndex: 'numField',
            width: 100,
            align: 'right',
            editor: new Ext.form.NumberField()
        },
        {
            header: "日付フィールド",
            dataIndex: 'dateField',
            width: 120,
            renderer: Ext.util.Format.dateRenderer( 'Y/m/d' ),
            editor: new Ext.form.DateField( { format: 'Y/m/d' } )
        }
    ]);
    clmn.defaultSortable = true;

    var MyForm = Ext.data.Record.create( headAry );
    var reader = new Ext.data.ArrayReader( {}, headAry );
    var store = new Ext.data.Store({
        reader: reader,
        data: getFieldItems(),
        sortInfo:{ field:'ID', direction:'DESC' }
    });

    var grid = new Ext.grid.EditorGridPanel({
        store: store,
        cm: clmn,
        renderTo: 'editor-grid',
        width:640,
        height:300,
        title:'Form',
        frame:true,
        clicksToEdit:1,
        stripeRows:true,

        // リスナー登録。
        listeners:{

            // gridセルに対する編集が加わった際のイベントを取得する。
            'afteredit': function( evt ) {

                var curChange = grid.getStore().getAt( evt.row ).get( 'ID' );

                // 編集が加わったIDを参照して、空の場合は新規作成とする
                if( curChange == '' ) return;

                // 既存のアイテムで編集されたIDをためておく。
                for( var idx = 0; idx < changeList.length; idx++ ){
                    if( changeList[ idx ] == curChange ){
                        return;
                    }
                }
                changeList.push( curChange );
            }
        },

        // ツールバー。「新規作成」と「送信」
        tbar: [{
            text: '新規作成',
            handler : function(){
                var newForm = new MyForm({
                    ID:'',
                    Title: '',
                    selectClmn: '',
                    numField: 0,
                    dateField: ( new Date() ).clearTime()
                });
                grid.stopEditing();
                store.insert( 0, newForm );
                grid.startEditing( 0, 0 );
            }
        },
        {
            text: '送信',
            handler : function(){
                var idx = 0;
                var methodCnt = 1;
                var batchXML = '';
                var curStore = grid.getStore();
                curStore.each( function(){

                    // 新規作成はIDが空
                    if( curStore.getAt( idx ).get( 'ID' ) == '' ){
                        var fmtDate = Ext.util.Format.date( curStore.getAt( idx ).get( 'dateField' ), 'Y-m-d' );
                        batchXML = batchXML + 
                        '<Method ID="' + methodCnt + '" Cmd="New">' +
                        '<Field Name="Title">' + curStore.getAt( idx ).get( 'Title' ) + '</Field>' +
                        '<Field Name="selectClmn">' + curStore.getAt( idx ).get( 'selectClmn' ) + '</Field>' +
                        '<Field Name="numField">' + curStore.getAt( idx ).get( 'numField' ) + '</Field>' +
                        '<Field Name="dateField">' + fmtDate + '</Field>' +
                        '</Method>'
                        methodCnt++;
                    }
                    
                    // 既存のアイテムを変更した場合、
                    // aftereditイベントでためたIDを参照してXMLを構築する。
                    else {
                        for( var sdx = 0; sdx < changeList.length; sdx++ ){
                            if( curStore.getAt( idx ).get( 'ID' ) == changeList[ sdx ] ){
                                
                                // SharePointの日付フォーマットに直す。"2008/04/07" → "2008-04-07"
                                var fmtDate = Ext.util.Format.date( curStore.getAt( idx ).get( 'dateField' ), 'Y-m-d' );

                                batchXML = batchXML + 
                                '<Method ID="' + methodCnt + '" Cmd="Update">' +
                                '<Field Name="ID">' + curStore.getAt( idx ).get( 'ID' ) + '</Field>' +
                                '<Field Name="Title">' + curStore.getAt( idx ).get( 'Title' ) + '</Field>' +
                                '<Field Name="selectClmn">' + curStore.getAt( idx ).get( 'selectClmn' ) + '</Field>' +
                                '<Field Name="numField">' + curStore.getAt( idx ).get( 'numField' ) + '</Field>' +
                                '<Field Name="dateField">' + fmtDate + '</Field>' +
                                '</Method>'
                                methodCnt++;
                            }
                        }
                    }
                    idx++;
                });

                // 新規作成や、更新があった場合はmethodCntが1以上
                if( methodCnt > 1 ){
                    // XMLを渡して更新する。
                    updateListItems( '<Batch>' + batchXML + '</Batch>' );

                    // リロードしてgridフォームを読み直すと更新されてます。
                    // location.reload();
                }
                else {
                    alert( "フォームの更新がされていません" );
                }
            }
        }]
    });
});
</script>
</head>
<body>
<div style="margin: 12px 12px">
<div id="editor-grid"></div>
<select name="ddfield" id="ddfield" style="display: none;">
    <option value="hoge">hoge</option>
    <option value="moge">moge</option>
    <option value="fuga">fuga</option>
</select>
</div>
</body>

[ 最近のエントリーとその関連エントリー ]


2008年04月06日

やる夫がデザインパターンをやるようです 第8回

≪第7回第9回≫

ワタシはC++からオブジェクト指向言語に触れたので、newしたインスタンスは必ず明示的にdeleteするという作法が体に染み付いております。スマートポインタなどを利用することもありますが、基本的にはアロケートした領域は自分で開放するのが当然であると思っていて、このことがワタシにとってファクトリの理解を阻むものだったのでは?と今では思っています。 今回のファクトリパターンはインスタンスをnewして返すものなので、開放はどこで行うべきかを意識する必要があるのですが、できればnewを押し付けたりしないで、自分でメモリのアロケート→開放を管理したいというのが当時ワタシが思っていたことでした。

とまぁ、ワタシのことはどうでもよいのですがやる夫の続きです。
インスタンスの生成くらいでごちゃごちゃ規制されたくないお
デザインパターンだかなんだか知らんが、
おいらのようなスーパーハカーに任せておけばプロジェクトは成功するお
        ノ L____
       ⌒ \ / \
      / (○) (○)\
     /    (__人__)   \
     |       |::::::|     |
     \       l;;;;;;l    /l!| !
     /     `ー'    \ |i
   /          ヽ !l ヽi
   (   丶- 、       しE |そ  ドンッ!!
    `ー、_ノ       ∑ l、E ノ <
               レY^V^ヽl 





   ふふーふーん♪コンストラクタをたくさん用意して
   みんなに「使いやすいっ」て言われるクラスをつくるお
   間口を広げておけばきっとたくさん釣れr使ってくれるお
           ____ 
        /⌒  ⌒\ 
.      /( ●)  (●)\ 
     / ::::::⌒(__人__)⌒::::: \ 
    |      |r┬-|     |  
     \       `ー'´     / 
    ノ            \ 
  /´               ヽ                 カ 
 |    l   l||l 从人 l||l      l||l 从人 l||l   カ    タ 
 ヽ    -一''''''"~~``'ー--、   -一'''''''ー-、.     タ 
  ヽ ____(⌒)(⌒)⌒) )  (⌒_(⌒)⌒)⌒)) 
     ┌┬┬┐┌┬┬┬┐┌┬┬┬┐┌┬┬┬┐ 
  ,. - ''"| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ρ ̄`l 
   ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ノ ̄ 






   このまま続けちゃったらソースコメントに
   // やるお神光臨ktkr(よくやった。アーキより)
   なんてかかれちゃうお
         ___ 
       / ⌒  ⌒\  
      / (●)  (●) \ 
    /   ///(__人__)/// \ 
     |   u.   `Y⌒y'´    |
      \       ゙ー ′  ,/
      /⌒ヽ   ー‐    ィヽ
      / rー'ゝ       〆ヽ
    /,ノヾ ,>      ヾ_ノ,|
    | ヽ〆        |´ | 






<1週間後>
  バグの修正したらコンパイルがとおらなくなったお。。。
  おいらのクラスにさらにコンストラクタ追加したボケはだれだお。
  ::::::::  ::    ::     :::::  ::  :::
  ::::::  ::   ____   :::::  ::::  :::
  ::::::  :::: /  :::  \ :::   :::   ::
  ::::  ::::/   ::     \::  :::  ::
 ::::  /:::    ─    ─ \ ::  ::
 :::  |  ::   .(○)  (○)  | :  ::::
  :::   \     (__人__)  ,/ :  :::
  ::  ノ      ` ⌒´   \ :   :::
  /´               ヽ  :::
 |    l              \:::
 ヽ    -一''''''"~~``'ー--、   -一'''''''ー-、.
  ヽ ____(⌒)(⌒)⌒) )  (⌒_(⌒)⌒)⌒))







<2週間後>
           ___
     ____,./      \ コンパイルを通したら
    ノ   /         \  新しいバグがいくつもでてきたお。。。
  /   /            \
 |     |::..           ...::::| 
 ヽ    `一ー――――-、;;;;::/`一ー―-、
  ヽ____(⌒)(⌒)⌒) )  (⌒_(⌒)⌒)⌒))







      / ̄ ̄\てめぇのアタマをデバグしてから出社しやがれ
    /ノ( _ノ  \  
    | ⌒(( ●)(●)
    .|     (__人__) /⌒l 
     |     ` ⌒´ノ |`'''| 
    / ⌒ヽ     }  |  |               ぺきょ
   /  へ  \   }__/ /             / ̄ ̄\ 
 / / |      ノ   ノ           / ●)) ((●\’, ・ 
( _ ノ    |      \´       _    (   (_人_)’∴ ),
       |       \_,, -‐ ''"   ̄ ゙̄''―---└'´ ̄`ヽ   て 
       .|                  ______ ノ    ( 
       ヽ           _,, -‐ ''"  ノ       ヽ   r'
         \       , '´        し/..     | J 
          \     (           /      | 
            \    \         し-  '^`-J 

<シンプルファクトリ>

というわけで、まずシンプルファクトリについてソースを含めて解説してみます。
// ファクトリ
class SimpleFactory {
    public Game createGame( String name ){
        Game gameInst = null;
        if( name.equals( "やるおの大冒険" ) ){
            gameInst = new ActionGame();
        }
        else if( name.equals( "ニコニコクロニクル" ) ){
            gameInst = new RPGGame();
        }
        // ほかにもたくさん。
        return gameInst;
    }
}

// ニコニコゲーム製作会社クラス
class NiconicoGameCompany {
    SimpleFactory fctryInst;
    // コンストラクタでファクトリをもらう。
    public NiconicoGameCompany( SimpleFactory param ){
        this.fctryInst = param;
    }
    
    public Game orderGame( String name ){
        Game gameInst;
        
        // ファクトリを使ってゲーム名を渡して返してもらう。
        gameInst = fctryInst.createGame( name );
        
        gameInst.prepare();
        gameInst.produce();
        gameInst.checkProduct();
        gameInst.package();
        return gameInst;
    }
}
ソースは上記のように書いてみました。一見するとファクトリは適宜インスタンスをnewして返すだけなので、自分でnewすりゃいいじゃんかと思うかもしれませんが、ソースのNiconicoGameCompanyクラス以外でGameクラスを扱うゲーム製作会社クラスがファクトリにnewを委譲してしまうことで、先に書いたやる夫のようなハマリ方はなくなります。
この使い方そのものはシンプルファクトリなどと呼ばれていて、SimpleFactoryが返すcreateGameの部分はスタティックにして使われたりすることがよくあります。

<ファクトリメソッド>

■ファクトリとプロダクト(工場と製品)
ファクトリメソッドは、
  1. ゲーム製作会社(:ファクトリ)スーパークラス(抽象クラス)を用意して
  2. これを継承するサブクラスで、インスタンスの生成を拡張してデザインされるものです。
ゲーム製作会社は、NiconicoGameCompany以外にもNyantendoGameCompany、SquareEnixoなどが考えられます。これらのゲーム製作会社がGameCompanyクラスを継承して、createGameを実装することで、独自のゲーム製作方法をそれぞれの製作会社が保持することになります。
そのため、プロダクトとなるゲームもサブクラスで拡張するようになります。生成される側のゲームのほうはといいますと、
  1. ゲーム(:プロダクト)スーパークラス(抽象クラス)を用意して
  2. これを継承するゲームサブクラスで、その機能を拡張してデザインされるものです。
これらのデザインを実現するには、上述シンプルファクトリのSimpleFactoryクラス、createGame()メソッドをGameCompanyに持たせることになります。このメソッドは以下のようにGameCompanyの抽象メソッドとし、
    // ファクトリオブジェクトから受け取ったcreateGameメソッド
    abstract Game createGame( String name );
GameCompanyを継承したサブクラスが製作方法を(オーバーライド)実装することで拡張していくのです。

■変更部分の切り出し
シンプルファクトリにも実装されていますが、orderGame()メソッドは、ゲーム製作の過程が実装されています。
  • ゲーム製作の準備(prepare)
  • 製作(produce)
  • 試験(checkProduct)
  • パッケージ(package)
これらは将来変更されることはないでしょう。ゲーム製作というものは、準備をして製作、試験をおこなってからパッケージして販売されるものです。これらのゲーム製作過程そのものがドラスティックにしょっちゅう変更されるということは、ビジネスモデルそのものを抽象化できていないということです。分析からやり直したほうがよいかもしれません。
ちょっと強引な話の進め方ですが、変更や追加・削除されていく箇所は上記のorderGame()ではなく、「どんなゲームをつくるのか?」になります。つまりcreateGame()になります。ファクトリメソッドはこの変更部分を分離してcreateGame()メソッドとしてサブクラスがオーバーライドするという仕組みです。(毎度のことながら、ゲーム会社のゲーム製作工程はよく知りません。話を進める上でのことですので誤りなどありましても気にしないでください。)
// ゲーム製作会社スーパークラス
abstract class GameCompany {
    public Game orderGame( String name ){
        // サブクラスに作成するゲームを決定させる
        Game gameInst = createGame( name );

        gameInst.prepare();
        gameInst.produce();
        gameInst.checkProduct();
        gameInst.package();
        return gameInst;
    }
    
    // ファクトリオブジェクトから受け取ったcreateGameメソッド
    abstract Game createGame( String name );
}
class NiconicoGameCompany extends GameCompany {
    // サブクラスが作成するゲームを決定する
    Game createGame( String name ){
        Game gameInst = null;
        if( name.equals( "やるおの大冒険" ) ){
            gameInst = new ActionGame();
        }
        else if( name.equals( "ニコニコクロニクル" ) ){
            gameInst = new RPGGame();
        }
        // ほかにもたくさん。
        return gameInst;
    }
}

// ゲームクラス。
abstract class Game {
    String category;
    void prepare(){ System.out.println( "設計" ); }
    void produce(){ System.out.println( "製作" ); }
    void checkProduct(){ System.out.println( "テスト" ); }
    void package(){ System.out.println( "パッケージ" );}
}

// ゲームクラスを継承して実装
class ActionGame extends Game {
    public ActionGame(){
        category = "アクション"
    }
    void package(){
        System.out.println( "アクションはパッケージがかっこよくないと!" );
    }
}
     ____       ヽ v / 
   /⌒  ⌒\     -(m)-   
  /( ●)  (●)\       ≡ 
/::::::⌒(__人__)⌒:::::\    
|      `ー'´     | 前々から「変更」するであろう箇所を
/     ∩ノ ⊃  /    抜き出してクラス化・メソッド化してたお。
(  \ / _ノ |  |   今回はnewするインスタンスをいろんな条件に
.\ “  /__|  |    基づいて変えるからここが「変更」箇所だお
  \ /___ / 







   / ̄ ̄\  ( ;;;;( 
 / _ノ  ヽ\ ) ;;;;) 
 |  ( ○) (○)/;;/   
. |   (__人__) l;;,    
  |    ∩ ノ)━・'/   
.  |   /  ノ´ } 条件文がいっぱいあるから
.  ヽ  / /    } 「変更」がかかる箇所ってわけじゃねーだろjk
   ヽ/ /   ノ 







      ____ インスタンスの生成を切り離しておくことで
    /_ノ   ヽ_\たくさんあるゲームの種類の生成が
   /( ●) ( ●)\管理しやすくなったお。
 / ::::::⌒(__人__)⌒:::::\
 |        ̄      |
 \              / 








     ____これ以上おいらが天才になったら
    / ⌒  ⌒  \アーキテクトの立場がなくなるから
  ./( ―) ( ●)  \馬鹿のフリしとくお。
  /::⌒(_人_)⌒:::::  | チラッ
  |    ー       .|
  \          / 







        / ̄ ̄\
      /       \
      |::::::        | とりあえずお前が作りこんだバグは
     . |:::::::::::     | 徹夜してでもなんとかしとけ
       |::::::::::::::    |          ....,:::´, .
     .  |::::::::::::::    }          ....:::,,  ..
     .  ヽ::::::::::::::    }         ,):::::::ノ .
        ヽ::::::::::  ノ        (:::::ソ: .
        /:::::::::::: く         ,ふ´..
-―――――|:::::::::::::::: \ -―,――ノ::ノ――
         |:::::::::::::::|ヽ、二⌒)━~~'´ 







           ____ 
        /⌒  ⌒\ ファクトリわかったから
.      /( ●)  (●)\ 徹夜なんてなんてことないお
     / ::::::⌒(__人__)⌒::::: \ 
    |      |r┬-|     |  
     \       `ー'´     / 
    ノ            \ 
  /´               ヽ                 カ 
 |    l   l||l 从人 l||l      l||l 从人 l||l   カ    タ 
 ヽ    -一''''''"~~``'ー--、   -一'''''''ー-、.     タ 
  ヽ ____(⌒)(⌒)⌒) )  (⌒_(⌒)⌒)⌒)) 
     ┌┬┬┐┌┬┬┬┐┌┬┬┬┐┌┬┬┬┐ 
  ,. - ''"| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ρ ̄`l 
   ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ノ ̄ 






    |┃三 ガラッ
    |┃  ____
    |┃/⌒  ⌒\
    |┃(●)  (●) \
――‐.|┃:⌒(__人__)⌒:::::\  徹夜すると居室に誰もいなくなって
    |┃  |r┬-|     |⌒)サイコーだお
    |┃   `ー'ォ     //
    (⌒ヽ・    ・ ̄ /
    |┃ノ       /
    |┃   つ   <
    |┃  (::)(::)   ヽ
    |┃/    >  )
    |┃     (__)


    |┃
    |┃  ____
    |┃/⌒  ⌒\
    |┃ (―)  (―)\  フヒー。
――‐.|┃:⌒(__人__)⌒:::::\
    |┃           |
    |┃          /
    |┃ヽ・    ・ ̄ /
    |┃ \    ,.:∴~・:,゜・~・:,゜・ ,
    |┃ヽ_)つ‘∴・゜゜・・∴~・:,゜・・∴
    |┃  (::)(::)  ヽ    ・゜゜・∴~゜
    |┃/    >  )    ゜゜・∴:,゜・~
    |┃     (__)    :,゜・~:,゜・゜゜・~ 











   / ̄ ̄\  やる夫。徹夜ごくろうs
 /   /  \
 |    ( ○)(○)
. |     (__人_(
  |     ` ⌒´(
.  |         }
.  ヽ        }
   ヽ     ノ











    |┃
    |┃  ____
    |┃/⌒  ⌒\
    |┃ (○)  (○)\  お
――‐.|┃:⌒(__人__)⌒:::::\
    |┃           |
    |┃          /
    |┃ヽ・    ・ ̄ /
    |┃ \    ,.:∴~・:,゜・~・:,゜・ ,
    |┃ヽ_)つ‘∴・゜゜・・∴~・:,゜・・∴
    |┃  (::)(::)  ヽ    ・゜゜・∴~゜
    |┃/    >  )    ゜゜・∴:,゜・~
    |┃     (__)    :,゜・~:,゜・゜゜・~ 














        ____
       /      \  ち、違うんです刑事さん・・・。
     /  ─    ─\
    ./    (○)  (○) \
    |       (__人__)    |、
r―n|l\      ` ⌒´   ,/ ヽ
  \\\.` ー‐ ' .// l     ヽ
.     \        |      |
.       \ _  __ | ._   |
        /,  /_ ヽ/、 ヽ_|
\      // /<  __) l -,|__) >
  \.    || | <  __)_ゝJ_)_>
    \.   ||.| <  ___)_(_)_ >
      \_| |  <____ノ_(_)_ ) 
というわけで次回はアブストラクトファクトリでファクトリパターンを締めたいと思います。 ≪第7回第9回≫


[ 最近のエントリーとその関連エントリー ]