內蒙古java團隊

          j2se,j2ee開發組
          posts - 139, comments - 212, trackbacks - 0, articles - 65
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          MapGuide Web API

          Posted on 2011-04-15 00:18 帥子 閱讀(732) 評論(0)  編輯  收藏 所屬分類: MapGuide

          許多人都知道MapGuide提供了.NET、PHP和Java三種類型的Web API,但是不知道MapGuide是如何創建這三種類型的API的。試想一下,如果分別去創建這三種API,這將是一個很難維護的工作。每次增加或修改一些功能,就需要對三種類型的API都進行修改。所以,MapGuide使用了SWIG來自動生成這三種類型的API。我想這個時候許多人會問,什么是SWIG呢?我怎么從來沒有聽說過這個東東呢!其實,我也是在做MapGuide開發的時候才開始了解SWIG的。所以,首先讓我們來認識一下SWIG,然后再來看MapGuide是如何使用SWIG來生成API的。

          1. SWIG簡介

          ??? SWIG是Simple Wrapper and Interface Generator的縮寫,是一個幫助使用C或者C++編寫的軟件創建其他編語言的API的工具。例如,我想要為一個C++編寫的程序創建.NET API,一般情況下我必須使用托管C++(Managed C++)去編寫大量的代碼才能生成它的.NET API。有了SWIG,這個機械的工作將變得非常簡單。你只須要使用一個接口文件告訴SWIG要為那些類創建.NET API,SWIG就會自動幫你生成它的.NET API。是不是非常的酷啊?

          ??? 當然,SWIG不僅僅支持創建.NET API。最新版本的SWIG支持常用腳本語言Perl、PHP、Python、Tcl、Ruby和非腳本語言C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Modula-3, OCAML以及R,甚至是編譯器或者匯編的計劃應用(Guile, MzScheme, Chicken)。

          ??? 下面我們通過一個例子來看看SWIG是如何幫我們創建API的。假設我打算為如下的C++類創建C#和Java的API。

          ??? /* SwigTest.h */

          ??? class CSwigTest {
          ??? public:
          ??????? CSwigTest();
          ??? ??? virtual ~CSwigTest();
          ??????? int Add(int a, int b) { return a + b; }
          ??? ??? int Substract(int a, int b) { return a - b; }

          ??? ??? int Multiple(int a, int b) { return a * b; }
          ??????? float Divide(int a, int b) { return (float)a / (float)b; }
          ??? };

          1.1 接口文件

          ??? 首先,你需要寫一個接口文件(Interface File),告訴SWIG要為那些類的那些方法創建API。如下的接口文件只為類CSwigTest的方法Add(...)和Subtract(...)生成API,因為在接口文件的接口聲明部分只聲明了兩個方法。

          ??? /* SwigTest.i */
          ??? %module SwigTest
          ??? %{
          ??? #include "SwigTest.h"
          ??? %}

          ? ? /*?--- 接口聲明部分 ---*/
          ??? class CSwigTest {
          ??? public:
          ??????? int Add(int a, int b);

          ??????? int Substract(int a, int b);

          ??? };

          ??? 注解:%module標記用于定義SWIG生成的模塊的名稱,%{%}標記中的內容會被一字不差地插入SWIG自動生成的文件xxx_wrapper.c中,其中xxx代表用%module指定的模塊名稱。這個文件會在下面介紹,不必著急去理解它究竟有什么作用。

          ??? 如果打算為類中所有方法創建API,那么有一個非常簡單的辦法,在接口文件的類聲明部分使用%include標記。SWIG將對%include所指定的文件進行語法分析,類中所有公有方法(Public Method)都將在API中暴露。

          ??? /* SwigTest.i */
          ??? %module SwigTest
          ??? %{
          ??? #include "SwigTest.h"
          ??? %}
          ??? #include “SwigTest.h”?

          1.2 編譯模塊

          ??? 有了接口文件之后,剩下的事就是執行幾條命令。下面我們以Windows平臺上生成.NET API為例介紹這些命令。

          ??? (a) 調用SWIG自動生成代碼

          ??? swig -csharp? SwigTest.i

          ??? 執行上面的命令會產生一個C語言文件SwigTest_wrapper.c和多個C#文件。在文件SwigTest_wrapper.c中,SWIG為接口文件中接口聲明部分指定的每個方法產生一個全局方法,以便C#使用Pinvoke調用這些函數。而那些C#文件就是用來生成.NET API的。

          ????(b) 為C++代碼生成DLL(動態鏈接庫)

          ??? cl SwigTest_wrapper.c *.cpp

          ??? link *.obj /out:SwigTest.dll

          ??? 執行上面的命令,會為我們編寫C++代碼生成DLL。在編譯C++文件時,一定要包括SWIG為我們生成的C++文件SwigTest_wrapper.cpp。

          ??? 注意:為了讓大家便于理解上述命令,這些命令并沒有列出完整的編譯和鏈接選項。

          ????(C) 生成.NET模塊

          ??? csc /out:SwigTestNotNetAPI.dll /target:library *.cs

          ??? 執行上面的命令就生成了.NET API模塊SwigTestNotNetAPI.dll。如果用戶想使用這些API,只需要添加對SwigTestNotNetAPI.dll的引用(Reference)就可以了。

          ??? 生成其它語言類型API的命令基本類似,下面我們再以Java在Unix平臺下的命令為例結束對SWIG的介紹。事實上,SWIG也是一個開源項目。如果想了解更多關于SWIG的信息,大家可以登陸SWIG的官方網站www.swig.org,那里有SWIG最詳細的資料。
          ????$ swig -java SwigTest_wrapper.i
          ?? ?$ gcc -c *.cpp SwigTest_wrapper.c -I/c/jdk1.3.1/include -I/c/jdk1.3.1/include/win32
          ?? ?$ gcc -shared *.o -mno-cygwin -Wl,--add-stdcall-alias? -o SwigTest.dll

          2. SWIG在MapGuide中的應用

          ??? 我們在前面已經提到過,MapGuide使用了SWIG來自動生成.NET、Java和PHP這三種類型的API。但是,SWIG也有不少限制和缺陷,所以MapGuide對SWIG源代碼進行了大量的修改,以滿足自己的要求。下面,我們看看這些改進。

          2.1 IMake工具

          ??? SWIG要求開發人員編寫一個接口文件,那么能否讓接口文件自動生成呢?借用一句中國移動的廣告詞,我能!雖然SWIG沒有提供這方面的工具,但是我們可以自己開發嗎!IMake(Interface Maker)就是為了滿足這樣的要求而開發一個工具,給定一個XML文件,它能幫你自動生成SWIG接口文件。登錄MapGuide開源版的代碼瀏覽頁面(http://trac.osgeo.org/mapguide/browser),在root/trunk/MgDev/BuildTools/WebTools/IMake文件夾下可以找到IMake的源代碼。

          ???? 下面我們以MapGuide中使用的XML文件/trunk/MgDev/Web/src/MapGuideApi/MapGuideApiGen.xml為例,介紹一下IMake的用法。為了便于理解,在此我刪掉了文件中的部分內容。

          ??? <?xml version="1.0" encoding="UTF-8"?>
          ??? <Parameters>
          ????? <!-- 對應于%Module標記. -->
          ????? <Module name="MapGuideApi" />

          ????? <!-- 生成的接口文件的名稱. -->
          ????? <Target path="./MapGuideApi.i" />

          ????? <!-- 對應于%{%}標記 -->
          ????? <CppInline>
          ??????? #include &lt;string&gt;
          ??????? #include &lt;map&gt;
          ??????? #include "MapGuideCommon.h"
          ??????? #include "WebApp.h"
          ??????? ......
          ????? </CppInline>

          ????? <!-- 用于替換接口中使用的部分類型 -->
          ????? <TypeReplacements>
          ??????? <TypeReplacement oldtype="CREFSTRING" newtype="STRINGPARAM" />
          ??????? <TypeReplacement oldtype="INT64" newtype="long long" />
          ????? </TypeReplacements>

          ????? <!-- 此部分的內容添加在%{%}之后,接口聲明部分之前 -->
          ????? <SwigInline>
          ??????? %include "language.i"?? //typemaps specific for each language
          ??????? ......
          ????? </SwigInline>
          ??????
          ????? <!-- 為指定的C++文件生成接口聲明 -->
          ????? <Headers>
          ??????? <Header path="../../../Common/Foundation/Data/Property.h" />
          ??????? ......
          ????? </Headers>
          ??? </Parameters>

          ??? 執行命令“IMake MapGuideApiGen.xml”,IMake就幫我們自動生成了如下SWIG接口文件MapGuideApi.i。

          ??? /* MapGuideApi.i */
          ??? %module MapGuideApi?
          ??? %{
          ??????? #include <string>;
          ??????? #include <map>;
          ??????? #include "MapGuideCommon.h"
          ??????? #include "WebApp.h"
          ??????? ......
          ??? %}
          ?
          ??? %include "language.i"?? //typemaps specific for each language
          ??? ......

          ??? class MgProperty: public MgNamedSerializable
          ??? {
          ??? public:
          ??????? virtual INT16 GetPropertyType();
          ??????? STRING GetName();
          ??????? void SetName(CREFSTRING name);
          ??? };

          ??? ......

          ??? 如果打開文件Proper.h,我們可以看到MgProperty有更多的方法,例如CanSetName(...)。為什么只有三個方法添加到了SWIG接口文件中?IMake在生成接口文件時,它會查找C++頭文件中的宏PUBLISHED_API。只有被PUBLISHED_API修飾的方法,才會添加到接口文件中。

          ??? 注:宏PUBLISHED_API和INTERNAL_API的定義如下。

          ??? #define PUBLISHED_API public

          ??? #define INTERNAL_API public

          ??? class MG_FOUNDATION_API MgProperty : public MgNamedSerializable?
          ??? {?
          ??? PUBLISHED_API:

          ??????? virtual INT16 GetPropertyType() = 0;? /// __get???
          ??????? STRING GetName();? /// __get, __set?
          ??????? void SetName(CREFSTRING name);?
          ??
          ??? INTERNAL_API:?
          ??????? virtual bool CanSetName();?
          ??
          ??? protected:??
          ??????? INT32 GetClassId();?
          ??????? MgProperty();?
          ??????? virtual ~MgProperty();?
          ??????? virtual void Dispose();???
          ??????? virtual void ToXml(string &str, bool includeType = true, string rootElmName = "Property") = 0;?
          ??
          ??? private:?
          ??????? friend class MgPropertyCollection;?
          ??????? STRING m_propertyName;?
          ?
          ??? CLASS_ID:?
          ??????? static const INT32 m_cls_id = Foundation_Property_Property;?
          ??? };?????
          ??? 給定一個C++常量定義文件,IMake還可以自動生成對應的其他語言的常量定義文件。MapGuide .NET Web API中的所有常量都是使用IMake來生成的,例如MgMineType、MgPropertyType等。下面我們以MapGuide中使用的XML文件/trunk/MgDev/Web/src/MapGuideApi/Constants.xml為例,介紹如何自動生成各種語言的常量定義文件。同樣,為了便于理解,在此我刪掉了文件中的部分內容。與MapGuideApiGen.xml不同,Constants.xml包含一個新的元素Classes用來指出需要在目標語言中產生對應的常量類的C++類。
          ??? <?xml version="1.0" encoding="UTF-8"?>

          ??? <Parameters>

          ??? <!-- 用于替換類型 -->

          ??? <PHPTypeReplacements>?
          ??????? <TypeReplacement oldtype="STRING" newtype="" />
          ??????? <TypeReplacement oldtype="INT16" newtype="" />
          ??????? ......
          ??? </PHPTypeReplacements>
          ??? <CSharpTypeReplacements>
          ??????? <TypeReplacement oldtype="STRING" newtype="string" />
          ??????? <TypeReplacement oldtype="INT16" newtype="short" />
          ??????? ......
          ??? </CSharpTypeReplacements>
          ??? <JavaTypeReplacements>
          ??????? <TypeReplacement oldtype="STRING" newtype="String" />
          ??????? <TypeReplacement oldtype="INT16" newtype="short" />
          ??????? ......
          ??? </JavaTypeReplacements>

          ??? <Namespace>OSGeo.MapGuide</Namespace>
          ??? <Package>org.osgeo.mapguide</Package>

          ??? <!--?用于指出需要在目標語言中產生對應的常量類的C++類 -->

          ??? <Classes>
          ??????? <Class name="MgMineType" />
          ??????? <Class name="MgPropertyType" />
          ??????? ......
          ??? </Classes>

          ??? <Headers>
          ??????? <Header path="../../../Common/Foundation/Data/MimeType.h" />
          ??????? <Header path="../../../Common/Foundation/Data/PropertyType.h" />
          ??????? ......
          ??? </Headers>

          ??? </Parameters>

          ??? 執行命令“IMake.exe Constants.xml C# Constants.cs”,IMake就幫我們自動生成了一個C#常量文件Constants.cs。對于文件/trunk/MgDev/Common/Foundation/Data/PropertyType.h中定義了如下常量,

          ??? class MgPropertyType?
          ??? {?
          ??? PUBLISHED_API:??
          ?????? static const int Null???? =? 0;
          ?????? static const int Boolean? =? 1;?
          ?????? static const int Byte???? =? 2;?
          ?????? static const int DateTime =? 3;
          ?????? static const int Single?? =? 4;?
          ?????? ......
          ??? };

          ??? 在生成的Constants.cs文件中,有如下的類定義。
          ??? class MgPropertyType?
          ??? {?
          ?????? static const int Null???? =? 0;
          ?????? static const int Boolean? =? 1;?
          ?????? static const int Byte???? =? 2;?
          ?????? static const int DateTime =? 3;
          ?????? static const int Single?? =? 4;?
          ?????? ......
          ??? };

          ??? 這個文件可以被C#的編譯器直接編譯,所以MapGuide沒有使用SWIG生成常量的API,而是直接使用IMake。?如果想生成PHP或Java的常量定義文件,只需要將IMake命令的參數"C#"替換為"PHP"或"Jave"就可以了。

          2.2 MapGuide對SWIG的修改
          ??? 在MapGuide開始使用SWIG的時候,可用的SWIG的最高版本是1.3.21,從那以后MapGuide在沒有升級過SWIG。所以,到現在為止,MapGuide的SWIG版本仍然是1.3.21。這個版本的SWIG有不少限制和缺陷,

          • 無法創建基于自定義根異常類MgException的異常處理機制。
          • 無法創建屬性(Property)。
          • 對某些方法無法產生正確的API。例如,如果方法GetA(...)返回的是類A的子類B的實例,SWIG創建的API返回的仍然是A類的實例。此時如果你把返回值轉換為類B,那么轉換會失敗。
            ??? A* GetA();
          • ......

          ??? 事實上最新的SWIG版本也沒有全部解決這些問題,所以MapGuide對SWIG源代碼進行了大量的修改,以滿足自己的要求。看看MapGuide在使用SWIG命令是傳入的參數,我們可以發現有許多參數不是SWIG標準的參數,例如proxydir、clsidcode、clsiddata、catchallcode等。


          swig -c++ -csharp -dllname MapGuideUnmanagedApid -namespace OSGeo.MapGuide -proxydir .\custom -baseexception MgException -clsidcode getclassid.code -clsiddata m_cls_id -catchallcode catchall.code -dispose &quot;((MgDisposable*)arg1)-&gt;Release()&quot; -rethrow &quot;e-&gt;Raise();&quot; -nodefault -noconstants -module MapGuideApi -o MgApi_wrap.cpp -lib ..\..\..\Oem\SWIGEx\Lib MapGuideApi.i


          ??? 在此,我們不打算一一介紹這些參數,因為在多數情況下你沒有必要對了解參數的含義。我們只介紹MapGuide是如何來解決上述SWIG的第二和第三個問題的,因為在擴展MapGuide Web API的時候你可能會用得著。


          2.2.1 創建屬性
          ??? 如果你看過MapGuide源代碼的話,你會發現有許多方法聲明之后有“__get”、“__set”或“__get, __set”這樣的注釋,如類MgProperty中的方法。
          ??? class MgProperty : public MgNamedSerializable?
          ??? {?
          ??? PUBLISHED_API:?
          ??????? virtual INT16 GetPropertyType() = 0;? /// __get???
          ??????? STRING GetName();? /// __get, __set?
          ??????? void SetName(CREFSTRING name);?
          ??????? ......
          };
          ??? 這些注釋是有特殊含義的,它們就是用來解決上述SWIG的第二個問題的。當IMake工具掃描C++頭文件時發現這注釋后,會在目錄“.\custom”下為每個類產生一個幫助創建屬性的代碼文件。例如,如果要類MgProperty生成.NET API,IMake會在“.\custom”生成一個文件名為MgProperty的C#代碼文件,它的內容如下:
          ??? public int PropertyType {
          ??????? get {return GetPropertyType(); }
          ??? }
          ??? public int Name {
          ??????? get { return GetPropertyType(); }
          ??????? set { setName(value);}
          ??? }
          ??? 如果在SWIG的命令行中使用了參數proxydir,那么SWIG在為每個類生成代碼的時候,會在proxydir所指定的目錄下查找和類名相同的文件,并且將這個文件中的代碼插入類的目標代碼中。通過這種辦法,就解決了上述SWIG的第二個問題。

          2.2.2 ClassId
          ??? MapGuide Web API中的所有類都是從MgObject繼承而來的,在類MgObject中有一個方法GetClassId()用來返回每個類唯一的ID值。MapGuide就是用這個方法來解決上述SWIG的第三個問題的,所以如果要在MapGuide Web API中增加一個新類,一定要覆蓋(override)這個方法,并且提供一個唯一的ID值。
          ??? class MgObject
          ??? {
          ??? EXTERNAL_API:
          ??????? virtual INT32 GetClassId();
          ??????? virtual STRING GetClassName();
          ??? INTERNAL_API:
          ??????? virtual ~MgObject();
          ??????? bool IsOfClass(INT32 classId);
          ??? };

          3. 擴展MapGudie Web API

          ????如果你發現現有的MapGuide Web API無法滿足你的要求,沒有關系,你可以去嘗試擴展它,因為MapGuide是開源的。

          ??? 如果要新添類,基本步驟如下:
          ??? (a) 修改C++代碼,添加新的類。對于需要暴露于API的方法,使用宏PUBLISHED_API修飾。
          ??? (b) 修改XML文件/trunk/MgDev/Web/src/MapGuideApi/MapGuideApiGen.xml的Headers部分,為每個新添加類所在的C++頭文件增加一個Header元素。下面的示例中,"path"代表C++頭文件的路徑,"filename.h"代表文件的名稱。
          ??? <Headers>
          ??????? <Header path="path/filename.h" />
          ??????? ......
          ??? </Headers>
          ??? (c) 重新編譯MapGuide的Web模塊(/trunk/MgDev/Web/src/)。
          ?
          ??? 如果要增加一些新的方法到現有的類中,基本步驟如下:
          ??? (a) 修改C++代碼,添加新的方法,并且使用宏PUBLISHED_API修飾這些方法。
          ??? (b) 重新編譯MapGuide的Web模塊(/trunk/MgDev/Web/src/)。

          ??? 如果要新增常量類,基本步驟如下:
          ??? (a) 修改C++代碼,添加新的常量類。
          ??? (b) 修改XML文件/trunk/MgDev/Web/src/MapGuideApi/Constants.xml,在Classes部分為每個新添加常量類增加一個Class元素,在Headers部分為每個新添加常量類所在的C++頭文件增加一個Header元素。下面的示例中,"ClassName"代表新添加的C++常量類的名稱。

          ??? <Classes>
          ??????? <Class name="ClassName" />
          ??????? ......
          ??? </Classes>
          ??? <Headers>
          ??????? <Header path="path/filename.h" />
          ??????? ......
          ??? </Headers>????
          ??? (c) 重新編譯MapGuide的Web模塊(/trunk/MgDev/Web/src/)。

          主站蜘蛛池模板: 连山| 进贤县| 定南县| 舞阳县| 常熟市| 德令哈市| 海阳市| 于田县| 专栏| 漾濞| 三台县| 沙雅县| 平南县| 佛坪县| 南漳县| 集贤县| 陇西县| 平顶山市| 新乐市| 贵港市| 库伦旗| 万山特区| 凤城市| 峨眉山市| 广宗县| 新建县| 白玉县| 临洮县| 顺义区| 峡江县| 从化市| 甘洛县| 庆阳市| 合作市| 辰溪县| 永顺县| 庆安县| 三河市| 延川县| 嘉兴市| 芮城县|