JDeveloperに付属のデータ・コントロール以外のコントロールを使用する必要がある場合は、自分で作成できます。ADFでは、主に次の2つの方法でデータ・コントロールを作成できます。
データソースを表すJavaBeanを作成する。
データソース・タイプのデータ・コントロール・アダプタを作成する。
この章では、2番目のデータ・コントロール・アダプタの作成について説明します。データ・コントロールの詳細は、第1章「Oracle ADFアプリケーションの概要」を参照してください。
この章のトピックは次のとおりです。
この章では、カスタム・データ・コントロール・アダプタの例として、シンプルなCSVデータ・コントロール・アダプタについて説明します。このアダプタは、JDeveloperに付属のCSVデータ・コントロール・アダプタの簡略化バージョンです。
この章では、シンプルなCSVデータ・コントロール・アダプタの機能およびアダプタを構成するクラスについて説明します。
シンプルなCSVデータ・コントロール・アダプタは、ファイルからカンマ区切り値を取得し、ページに表示します。JDeveloperでアダプタを使用するには、次のいずれかを行います。
CSVファイルを表すノードを右クリックし、ポップアップ・メニューから「データ・コントロールの作成」を選択します。
データ・コントロール・パレットにノードをドラッグ・アンド・ドロップします。
どちらの場合も、ノードはCSVテキスト・ファイルにマッピングされ、ファイル名には.csv拡張子が必要です。シンプルなCSVデータ・コントロール・アダプタはノードからメタデータを抽出するため、メタデータを入力する必要はありません。
シンプルなCSVアダプタを使用してデータ・コントロールを作成すると、データ・コントロールがデータ・コントロール・パレットに表示されます。これをビュー・ページにドラッグ・アンド・ドロップできます。
一部の詳細な設定を簡略化するために、シンプルなCSVアダプタは、次のようにハードコード化されています。
CSVファイルのフィールドはカンマで区切られています。
デリミタ文字は二重引用符です。
CSVファイルではUTF-8エンコーディングを使用します。
ファイルの1行目では列名を指定します。
CSVファイルの名前には.csv拡張子が必要です。
(JDeveloperに付属のCSVアダプタではこれらの値を設定できます。)
データ・コントロール・アダプタを作成する場合、ソース・インスタンスではなくソース・タイプを表すように作成します。CSVアダプタの場合、ソース・タイプはCSVファイルです。特定のCSVファイルなど、特定のデータ・インスタンスを指定するには、データ・コントロール・アダプタを使用してデータ・コントロールを作成し、インスタンスをメタデータと関連付けます。メタデータでは、インスタンスのデータを指定します。シンプルなCSVアダプタでは、メタデータには特定のCSVファイルへのパスが含まれています。
データ・コントロール・アダプタは次のことを行います。
データ・コントロール・インスタンス用のメタデータの提供
実行時、ストアド・メタデータを使用したデータ・コントロール・インスタンスの作成
データ・コントロール・アダプタは、アダプタ・フレームワーク内で動作します。アダプタ・フレームワークは、メタデータの格納、データ・コントロール・アダプタとADFライフサイクルの統合、設計時のJDeveloperとの統合を処理します。
データ・コントロール・アダプタの作成手順:
抽象クラスを拡張するクラスを作成し、アダプタ・フレームワークでインタフェースを実装します。これらのクラスは、設計時および実行時に使用されます。次の項で説明する3つのクラスを作成する必要があります。
アダプタの必要に応じて追加クラスを作成することもできます。シンプルなCSVアダプタには、2つの追加クラス、CSVHandlerとCSVParserが含まれています。これらのクラスについては、31.6項「必要なサポート・クラスの作成」で説明します。
定義ファイルadapter-definition.xmlを作成して、アダプタをADFに登録します。このファイルにはアダプタ実装のクラス名が含まれ、アダプタが実行する必要があるライブラリを参照します。31.7項「アダプタを定義するXMLファイルの作成」を参照してください。
クラス・ファイルと定義ファイルをJARファイルにパッケージ化して、そのJARファイルをJDeveloperのクラスパスに置いて、JDeveloperにデータ・コントロール・クラスをインストールします。31.9項「アダプタのパッケージ化とJDeveloperへのデプロイ」を参照してください。
アダプタの起動
JDeveloperにデータ・コントロール・アダプタをインストールした後、このアダプタを起動するには、データ・コントロール・アダプタがサポートするJDeveloperのノードを右クリックし、ポップアップ・メニューから「データ・コントロールの作成」を選択します。データ・コントロール・アダプタは、そのadapter-definition.xml構成ファイルでサポートするノード・タイプを宣言します(31.7項「アダプタを定義するXMLファイルの作成」を参照)。
たとえば、アダプタがデータベース接続ノードをサポートしている場合、データベース接続を右クリックすると、ポップアップ・メニューから「データ・コントロールの作成」を選択して、アダプタを起動できます。
この章では、ウィザードの作成方法およびウィザードからアダプタへの値の渡し方については説明しません。
AbstractAdapterクラスの実装はオプションです。このクラスは、ユーザーがデータ・コントロール・パレットにノードをドラッグ・アンド・ドロップして、データ・コントロールを作成できるようにする場合にのみ必要です。この場合、ドロップしたノードは、作成しているデータ・コントロールに関連付けられたデータソースを表します。この機能が必要ない場合は、このクラスを実装する必要はありません。たとえば、JDeveloperに付属のCSVデータ・コントロール・アダプタは、ドラッグ・アンド・ドロップ操作をサポートしていないため、このクラスを実装していません。このアダプタでは、ユーザーから情報を収集するウィザードが表示されます。
シンプルなCSVアダプタはAbstractAdapterを実装します。ユーザーがデータ・コントロール・パレットにノードをドラッグ・アンド・ドロップすると、JDeveloperはドロップされたタイプのノードを処理できるアダプタを確認します。adapter-definition.xmlファイルで、アダプタが処理できるノード・タイプを指定します。このファイルは、アダプタをJDeveloperに登録するために使用します。このファイルの詳細は、31.7項「アダプタを定義するXMLファイルの作成」を参照してください。
クラスでは、次の項で説明するように、AbstractAdapterクラスにいくつかのメソッドを実装する必要があります。
抽象クラスoracle.adf.model.adapter.AbstractAdapterは、JDEV_HOME/bc4j/lib/adfm.jarファイルにあります。
例31-1に、AbstractAdapterクラスを実装するクラスの概要を示します。
例31-1 AbstractAdapterを実装するクラスの概要
import oracle.adf.model.adapter.AbstractAdapter;
import oracle.adf.model.adapter.DTContext;
import oracle.adf.model.adapter.AbstractDefinition;
public class MyAdapter extends AbstractAdapter
{
public void initialize(Object sourceObj, DTContext ctx)
{
// you need to implement this method.
// see Section 31.3.4, "Implementing the initialize Method".
}
public boolean invokeUI()
{
// you need to implement this method.
// see Section 31.3.5, "Implementing the invokeUI Method".
}
public AbstractDefinition getDefinition()
{
// you need to implement this method.
// see Section 31.3.6, "Implementing the getDefinition Method".
}
}
例31-2に、SampleDCAdapterクラスの完全なソースを示します。これは、シンプルなCSVアダプタ用のAbstractAdapterを実装するクラスです。その後の項では、このクラスのメソッドについて説明します。
例31-2 SampleDCAdapterの完全なソース
package oracle.adfinternal.model.adapter.sample;
import java.net.URL;
import oracle.adf.model.adapter.AbstractAdapter;
import oracle.adf.model.adapter.AbstractDefinition;
import oracle.adf.model.adapter.DTContext;
import oracle.ide.Context;
public class SampleDCAdapter extends AbstractAdapter
{
// JDev Context
private Context mJdevCtx = null;
// Source object of data
private Object mSrc = null;
// Source Location
private String mSrcLoc = null;
// data control name
private String mDCName = null;
// data control definition
private AbstractDefinition mDefinition = null;
public SampleDCAdapter()
{
}
/**
* Initializes the adapter from a source object.
* <p>
* The source object can be different thing depending on the context of the
* design time that the adapter is used in. For JDeveloper, the object will
* be a JDeveloper node.
* </p>
* <p>
* Adapter implementations will check the <code>"ctx"</code> parameter to
* get the current design time context. The source object will be used to
* extract the information for the data source.
* </p>
* @param sourceObj Object that contains information about the data source
* that will be used to define the data control.
* @param ctx Current design time context.
*/
public void initialize(Object sourceObj, DTContext ctx)
{
mSrc = sourceObj;
mJdevCtx = (Context) ctx.get(DTContext.JDEV_CONTEXT);
}
/**
* Invlokes the UI at the design time.
* <p>
* This method is a call back from the JDeveloper design time environment to
* the adapters to bring up any UI if required to gather information about
* the data source they represent.
* </p>
*
* @return false if the user cancels the operation. The default retrun value
* is true.
*/
public boolean invokeUI()
{
// First check if this is a JDev environment.
if (mJdevCtx != null && mSrc != null)
{
if (extractDataSourceInfo(mSrc))
{
SampleDCDef def = new SampleDCDef(mSrcLoc,mDCName);
mDefinition = def;
return true;
}
return false;
}
return false;
}
/**
* <p>
* The Definition instance obtained can be used by the ADF design time to
* capture the data control metadata.
*</p>
*
* @return The definition instance describing the data control design time.
*/
public AbstractDefinition getDefinition()
{
return mDefinition;
}
/**
* @param source the data source object.
* @return false if data type is unknown.
*/
public boolean canCreateDataControl(Object source)
{
return extractDataSourceInfo(source);
}
/**
* Extracts information from a data source. This method extracts name
* from the object.
* @param obj the data source object.
*/
private boolean extractDataSourceInfo(Object obj)
{
mDCName = "SampleDC";
// See if the node dropped is a text node of CSV type.
// We will assume that the CSV data file must end with .csv
if (obj instanceof oracle.ide.model.TextNode)
{
oracle.ide.model.TextNode tn = (oracle.ide.model.TextNode) obj;
URL url = tn.getURL();
String loc = url.getFile();
// Check if the file has a matching extension
if (loc.endsWith(".csv"))
{
mSrcLoc = loc;
String path = url.getPath();
int index = path.lastIndexOf('/');
if (index != -1)
{
String fileName = path.substring(index+1);
int dotIndex = fileName.lastIndexOf('.');
mDCName = fileName.substring(0,dotIndex);
}
return true;
}
}
return false;
}
}
フレームワークは、ユーザーがデータ・コントロール・パレットにノードをドラッグ・アンド・ドロップするとinitializeメソッドをコールします。メソッドには次のシグネチャがあります。
sourceObjパラメータでは、ドロップされたノードを指定します。このパラメータをチェックして、アダプタが処理できるノード・タイプであることを確認できます。
ctxパラメータでは、設計時のコンテキストを指定します。DTContextのパッケージ・パスはoracle.adf.model.adapter.DTContextです。
initializeメソッドでは、次のタスクを実行する必要があります。
ソース・ノードがサポートされているかどうかをチェックします。
ノードがサポートされている場合は、ソース・ノードからデータ・コントロール・インスタンスの作成に必要なすべての情報を抽出します。情報がデータ・コントロール・インスタンスの作成に十分ではない場合、invokeUIメソッドでユーザーが必要な情報を入力するUIを表示できます。
シンプルなCSVアダプタでは、initializeメソッドはいくつかのクラス変数のみを設定します。これらのクラス変数は、後でinvokeUIメソッドでチェックされます。
このメソッドでは、ドロップされたデータソースに関する情報をユーザーから収集するためのUIを表示できます。メソッドにはAbstractAdapterに次のシグネチャがあります。
メソッドは、ユーザーがUIでの操作を取り消した場合にfalseを返す必要があります。この場合、データ・コントロールは作成されません。
情報を収集するためのUIが実行された場合は、trueを返す必要があります(デフォルトの実装)。
シンプルなCSVアダプタでは、initializeメソッドを使用して、次のことを実行するextractDataSourceInfoをコールする必要があります。
ユーザーが右クリックしたノードがテキスト・ファイルを表し、ファイル名に.csv拡張子があることをチェックします。
CSVファイルのファイル名を取得します。
mSrcLocおよびmDCNameクラス変数を設定します。mSrcLocはCSVファイルの場所をポイントします。mDCNameはデータ・コントロールに使用する名前です。この場合、.csv拡張子のないCSVファイルの名前です。
invokeUIは、これらの変数を使用してSampleDCDefオブジェクトをインスタンス化します。SampleDCDefオブジェクトというクラスも実装する必要があります。このクラスについては、31.4項「データ・コントロール定義クラスの実装」で説明します。
例31-6に、invokeUIメソッドを示します。
このメソッドは、ドロップされたソース・ノードから収集した情報に基づいて作成されたデータ・コントロールの定義を返します。メソッドには次のシグネチャがあります。
AbstractDefinitionクラスは、作成されたデータ・コントロール定義クラスです。31.4項「データ・コントロール定義クラスの実装」を参照してください。
シンプルなCSVアダプタでgetDefinitionメソッドが返すクラス変数mDefinitionの値は、invokeUIメソッドで設定された値です。mDefinitionは、ユーザーによって作成されたデータ・コントロール定義クラス(シンプルなCSVアダプタの場合はSampleDCDef)を参照します。
このクラスは、設計時および実行時にフレームワークがデータ・コントロールをインスタンス化するために必要なすべての情報を提供する必要があります。このクラスは、次の操作を行います。
デフォルト・コンストラクタの作成。31.4.4項「デフォルト・コンストラクタの作成」を参照してください。
ユーザーからのデータソースに関するメタデータの収集。31.4.5項「ユーザーからのメタデータの収集」を参照してください。
出力の構造の定義。構造では、ユーザーがデータ・コントロール・パレットでデータ・コントロールを展開したときに何を表示するかを定義します。表示後、ユーザーは要素をデータ・コントロール・パレットのデータ・コントロール・エントリからページにドラッグ・アンド・ドロップして、ビュー・コンポーネントを作成できます。31.4.6項「データ・コントロールの構造の定義」を参照してください。
そのメタデータを使用した、データ・コントロール・クラスのインスタンスの作成。データ・コントロール・クラスは、実装するクラスです。31.4.7項「データ・コントロールのインスタンスの作成」を参照してください。
フレームワークが、DCXファイルからメタデータをロードできるようにする。31.4.8項「実行時のメタデータの設定」を参照してください。
データ・コントロールの名前の設定。31.4.9項「データ・コントロールの名前の設定」を参照してください。
データ・コントロール定義クラスは、抽象クラスoracle.adf.model.adapter.AbstractDefinitionを展開する必要があります。このクラスは、JDEV_HOME/bc4j/lib/adfm.jarファイルにあります。
例31-9に、データ・コントロール定義クラスの作成時に実装する必要があるメソッドの概要を示します。SampleDCDefからのサンプルです。これは、シンプルなCSVデータ・コントロール・アダプタのデータ・コントロール定義クラスです。
例31-9 データ・コントロール定義クラスの概要
import oracle.adf.model.adapter.AbstractDefinition;
import org.w3c.dom.Node;
import oracle.binding.meta.StructureDefinition;
import oracle.binding.DataControl;
import java.util.Map;
public class SampleDCDef extends AbstractDefinition
{
// default constructor
public SampleDCDef ()
{
// you need a default constructor.
// see Section 31.4.4, "Creating a Default Constructor".
}
public Node getMetadata()
{
// you need to implement this method.
// see Section 31.4.5, "Collecting Metadata from the User".
}
public StructureDefinition getStructure()
{
// you need to implement this method.
// see Section 31.4.6, "Defining the Structure of the Data Control".
}
public DataControl createDataControl()
{
// you need to implement this method.
// see Section 31.4.7, "Creating an Instance of the Data Control".
}
public void loadFromMetadata(Node node, Map params)
{
// you need to implement this method.
// see Section 31.4.8, "Setting the Metadata for Runtime".
}
public String getDCName()
{
// you need to implement this method.
// see Section 31.4.9, "Setting the Name for the Data Control".
}
}
例31-10に、SampleDCDefクラスの完全なソースを示します。
例31-10 SampleDCDefクラスの完全なソース
package oracle.adfinternal.model.adapter.sample;
import java.io.InputStream;
import java.util.Map;
import oracle.binding.DataControl;
import oracle.binding.meta.StructureDefinition;
import oracle.adf.model.adapter.AbstractDefinition;
import oracle.adf.model.adapter.AdapterDCService;
import oracle.adf.model.adapter.AdapterException;
import oracle.adf.model.adapter.dataformat.AccessorDef;
import oracle.adf.model.adapter.dataformat.StructureDef;
import oracle.adf.model.adapter.utils.NodeAttributeHelper;
import oracle.adf.model.utils.SimpleStringBuffer;
import oracle.adfinternal.model.adapter.sample.CSVHandler;
import oracle.adfinternal.model.adapter.sample.SampleDataControl;
import oracle.adfinternal.model.adapter.url.SmartURL;
import oracle.xml.parser.v2.XMLDocument;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class SampleDCDef extends AbstractDefinition
{
// Name of the root accessor for a definition
public static final String RESULT_ACC_NAME = "Result";
// Namespace for the metadata definition.
public static final String SAMPLEDC_NS =
"http://xmlns.oracle.com/adfm/adapter/sampledc";
// Definition tag as the root
public static final String DEFINITION = "Definition";
// Attribute to contain the source URL
public static final String SOURCE_LOC = "SourceLocation";
// Name of the data control
private String mName = "SampleDC";
// the structure definition
private StructureDef mStructDef = null;
// URL for this definition.
private String mCSVUrl = null;
public SampleDCDef()
{
}
public SampleDCDef(String csvURL,String dcName)
{
mCSVUrl = csvURL;
mName = dcName;
}
public Node getMetadata()
{
XMLDocument xDoc = new XMLDocument();
Element metadata = xDoc.createElementNS(SAMPLEDC_NS, DEFINITION);
metadata.setAttribute(SOURCE_LOC, mCSVUrl.toString());
return metadata;
}
public StructureDefinition getStructure()
{
if (mStructDef == null)
{
// create an empty StructureDefinition
mStructDef = new StructureDef(getName());
SmartURL su = new SmartURL(mCSVUrl.toString());
InputStream isData = su.openStream();
CSVHandler csvHandler = new CSVHandler(isData, true, "UTF-8", ",", "\"");
// Name of the accessor or the method structure to hold the attributes
String opName = new SimpleStringBuffer(50).append(getDCName())
.append("_")
.append(RESULT_ACC_NAME)
.toString();
StructureDef def = (StructureDef)csvHandler.getStructure(opName, null);
// Create the accessor definition
AccessorDef accDef =
new AccessorDef(RESULT_ACC_NAME, mStructDef, def, true);
def.setParentType(StructureDef.TYPE_ACCESSOR);
accDef.setBindPath(new SimpleStringBuffer(50)
.append(mStructDef.getFullName())
.append(".")
.append(AdapterDCService.DC_ROOT_ACC_NAME)
.toString());
mStructDef.addAccessor(accDef);
}
return mStructDef;
}
public void loadFromMetadata(Node node, Map params)
{
try
{
// Get the information from the definition
NodeList listChld = node.getChildNodes();
int cnt = listChld.getLength();
Node chld;
for (int i = 0; i < cnt; i++)
{
chld = listChld.item(i);
// System.out.println("Tag: " + chld.getNodeName());
if (DEFINITION.equalsIgnoreCase(chld.getNodeName()))
{
// Load the required attributes
NodeAttributeHelper attribs =
new NodeAttributeHelper(chld.getAttributes());
mCSVUrl = attribs.getValue(SOURCE_LOC);
}
}
}
catch (AdapterException ae)
{
throw ae;
}
catch (Exception e)
{
throw new AdapterException(e);
}
}
public DataControl createDataControl()
{
SampleDataControl dcDataControl = new SampleDataControl(mCSVUrl);
return dcDataControl;
}
public String getDCName()
{
return mName;
}
public String getAdapterType()
{
return "oracle.adfm.adapter.SampleDataControl";
}
}
データ・コントロール定義クラスのデフォルト・コンストラクタを作成する必要があります。シンプルなCSVアダプタには空のデフォルト・コンストラクタがあります。
デフォルト・コンストラクタは、実行時にのみ使用されます。設計時には使用されません。
データ・コントロール・アダプタのメタデータは、データソースに関する情報を提供します。データ・コントロール定義クラスは、メタデータを使用してデータ・コントロールを作成します。完全な機能を備えたCSVデータ・コントロール・アダプタのメタデータには、CSVファイルへのURL、フィールド・セパレータ文字、引用文字などが含まれています。シンプルなCSVアダプタの場合、メタデータにはCSVファイルの場所のみが含まれています。
データ・コントロール・アダプタは、様々な方法でメタデータを収集できます。次に例を示します。
JDeveloperに付属のCSVデータ・コントロール・アダプタは、ウィザードを使用してユーザーからメタデータを収集します。
Webサービスのデータ・コントロール・アダプタも、ウィザードを使用してメタデータを収集します。ユーザーがWebサービス接続ノードをドラッグして、データ・コントロール・パレットにドロップすることもできます。Webサービス・アダプタは、ウィザードを起動せずにノードからメタデータを抽出します。
ユーザーがデータ・コントロール・パレットにノードをドラッグ・アンド・ドロップすると、アダプタ・フレームワークは、登録されたデータ・コントロール・アダプタを検索して、ドロップされたタイプのノードを処理できるアダプタを探します。データ・コントロール・アダプタは、サポートするノード・タイプを宣言します。ノードは、特定のソース・タイプを表すJDeveloperノードです。ドロップされたタイプのノードをサポートするアダプタが検出された場合、フレームワークはデータ・コントロール・アダプタを起動し、ノードから必要な情報を抽出します。
シンプルなCSVアダプタは、ユーザーがノードを右クリックし、ポップアップ・メニューから「データ・コントロールの作成」を選択すると、ノードからメタデータを抽出します。
データ・コントロール・アダプタがメタデータを取得する方法に関係なく、データ・コントロール定義クラスにgetMetadataメソッドを実装する必要があります。フレームワークはこのメソッドをコールして、メタデータを取得します。
このメソッドは、Nodeオブジェクトの形でメタデータを返します。getMetadataメソッドには次のシグネチャがあります。
シンプルなCSVアダプタでは、getMetadataメソッドは、mCSVUrlクラス変数からメタデータを取得し、値をElementオブジェクトに挿入します。
例31-13 getMetadataメソッド
public Node getMetadata()
{
XMLDocument xDoc = new XMLDocument();
Element metadata = xDoc.createElementNS(SAMPLEDC_NS, DEFINITION);
metadata.setAttribute(SOURCE_LOC, mCSVUrl.toString());
return metadata;
}
フレームワークは、getMetadataの戻り値(Nodeオブジェクト)から情報を抽出し、情報をDataControls.dcxファイルに書き込みます。たとえば、ユーザーがCSVデータ・コントロールを作成すると、ファイルは次のようになります。
例31-14 DataControls.dcxファイル
<?xml version="1.0" encoding="UTF-8" ?>
<DataControlConfigs xmlns="http://xmlns.oracle.com/adfm/configuration"
version="10.1.3.36.45" Package="view" id="DataControls">
<AdapterDataControl id="testdata"
FactoryClass="oracle.adf.model.adapter.DataControlFactoryImpl"
ImplDef="oracle.adfinternal.model.adapter.sample.SampleDCDef"
SupportsTransactions="false"
SupportsSortCollection="false" SupportsResetState="false"
SupportsRangesize="false" SupportsFindMode="false"
SupportsUpdates="false" Definition="testdata"
BeanClass="testdata"
xmlns="http://xmlns.oracle.com/adfm/datacontrol">
<Source>
<Definition
SourceLocation="/C:/Application1/ViewController/public_html/testdata.csv"/>
</Source>
</AdapterDataControl>
</DataControlConfigs>
AdapterDataControlタグのid属性の値(testdata)は、CSVファイルの名前から抽出されます。AdapterDataControlタグの他の属性には、シンプルなCSVアダプタ本体に関する情報が含まれます。Definition要素には、ノードから提供されたメタデータが書き込まれます。SourceLocation属性では、CSVファイルの場所を指定します。
データ・コントロール定義の構造では、ユーザーがデータ・コントロール・パレットでデータ・コントロールを展開したときに表示する項目を説明します。表示可能な項目には、ユーザーが起動または表示できる基礎となるサービスのメソッド、アクセッサおよび属性が含まれます。ユーザーは、これらの項目をビュー・ページにドラッグ・アンド・ドロップできます。
データ・コントロール定義クラスでは、getStructureメソッドを実装する必要があります。ユーザーがデータ・コントロール・パレットでデータ・コントロールを展開すると、フレームワークは、このメソッドをコールします。
getStructureメソッドには次のシグネチャがあります。
StructureDefinitionはインタフェースです。このインタフェースの詳細は、JDeveloperのオンライン・ヘルプの「リファレンス」→「Oracle ADF Model API Reference」を参照してください。
例31-16 getStructureメソッド
public StructureDefinition getStructure()
{
if (mStructDef == null)
{
// create an empty StructureDefinition
mStructDef = new StructureDef(getName());
SmartURL su = new SmartURL(mCSVUrl.toString());
InputStream isData = su.openStream();
CSVHandler csvHandler = new CSVHandler(isData, true, "UTF-8", ",", "\"");
// Name of the accessor or the method structure to hold the attributes
String opName = new SimpleStringBuffer(50).append(getDCName())
.append("_")
.append(RESULT_ACC_NAME)
.toString();
StructureDef def = (StructureDef)csvHandler.getStructure(opName, null);
// Create the accessor definition
AccessorDef accDef =
new AccessorDef(RESULT_ACC_NAME, mStructDef, def, true);
def.setParentType(StructureDef.TYPE_ACCESSOR);
accDef.setBindPath(new SimpleStringBuffer(50)
.append(mStructDef.getFullName())
.append(".")
.append(AdapterDCService.DC_ROOT_ACC_NAME)
.toString());
mStructDef.addAccessor(accDef);
}
return mStructDef;
}
フレームワークはデータ・コントロール定義クラスのcreateDataControlメソッドをコールして、データ・コントロール・インスタンスを作成します。createDataControlメソッドには次のシグネチャがあります。
メソッドによって返されるDataControlオブジェクトは、作成するデータ・コントロール・クラスのインスタンスです。このクラスについては、31.5項「データ・コントロール・クラスの実装」を参照してください。
シンプルなCSVアダプタのデータ・コントロール定義では、createDataControlメソッドは次のとおりです。
例31-18 createDataControlメソッド
public DataControl createDataControl()
{
SampleDataControl dcDataControl = new SampleDataControl(mCSVUrl);
return dcDataControl;
}
SampleDataControlクラスについては、31.5項「データ・コントロール・クラスの実装」で詳しく説明します。
ユーザーがデータ・コントロールを参照するビュー・ページを実行すると、フレームワークはDCXファイルからメタデータを読み取り、データ・コントロール定義クラスのloadFromMetadataメソッドを起動して、設計時に保存されたメタデータとともにデータ・コントロールをロードします。
フレームワークは、getMetadataメソッドでDCXファイルにメタデータを書き込んでいます。31.4.5項「ユーザーからのメタデータの収集」を参照してください。
loadFromMetadataメソッドには次のシグネチャがあります。
例31-19 loadFromMetadataのシグネチャ
public void loadFromMetadata(org.w3c.dom.Node node, java.util.Map params);
nodeパラメータにメタデータが含まれています。シンプルなCSVアダプタのメソッドは次のとおりです。
例31-20 loadFromMetadataメソッド
public void loadFromMetadata(Node node, Map params)
{
try
{
// Get the information from the definition
NodeList listChld = node.getChildNodes();
int cnt = listChld.getLength();
Node chld;
for (int i = 0; i < cnt; i++)
{
chld = listChld.item(i);
// System.out.println("Tag: " + chld.getNodeName());
if (DEFINITION.equalsIgnoreCase(chld.getNodeName()))
{
// Load the required attributes
NodeAttributeHelper attribs =
new NodeAttributeHelper(chld.getAttributes());
mCSVUrl = attribs.getValue(SOURCE_LOC);
}
}
}
catch (AdapterException ae)
{
throw ae;
}
catch (Exception e)
{
throw new AdapterException(e);
}
}
データ・コントロール・パレットのデータ・コントロール・インスタンスの識別に使用される文字列を返すgetDCNameメソッドを実装する必要があります。getDCNameには次のシグネチャがあります。
シンプルなCSVアダプタでは、メソッドは、SampleDCDef(String csvURL, String dcName)コンストラクタによって設定されたmNameクラス変数の値のみを返します。このコンストラクタは、SampleDCAdapterクラスでコールされました。mNameは、.csv拡張子のないCSVファイルの名前です。
各データ・コントロール・インスタンスには、アプリケーション内で一意の名前が必要です。たとえば、アプリケーションに2つのCSVデータ・コントロールがある場合は、これらに「CSV1」、「CSV2」という名前を付けることができます。JDeveloperに付属のCSVデータ・コントロール・アダプタでは、ユーザーはウィザードで名前を入力できます。シンプルなCSVアダプタの場合、名前は.csv拡張子のないCSVファイルの名前になります。
データ・コントロール・クラスは、設計時に保存されたメタデータに基づいて、データソースにアクセスできる必要があります。このクラスは、データ・コントロール定義クラスのcreateDataControlメソッドによってインスタンス化されます(31.4.7項「データ・コントロールのインスタンスの作成」を参照)。
このクラスは、次のことを行う必要があります。
抽象クラスoracle.adf.model.AbstractImplの展開
次に示すデータ・コントロール・インタフェースの1つの実装
表31-1 データ・コントロール・インタフェース
| インタフェース | 使用するとき |
|---|---|
|
|
リクエストの開始と終了を区別する必要がなく、トランザクション・サポートが必要ない場合に、このインタフェースを実装します。 |
|
|
リクエストの開始と終了を区別する必要がある場合に、このインタフェースを実装します。このインタフェースは |
|
|
トランザクション・サポートが必要な場合に、このインタフェースを実装します。このインタフェースは、 |
抽象クラスoracle.adf.model.AbstractImplは、JDEV_HOME/bc4j/lib/adfm.jarファイルにあります。
データ・コントロール・インタフェースは、JDEV_HOME/bc4j/lib/adfbinding.jarファイルにあります。
次のクラス概要に、データ・コントロール・クラスで実装する必要があるメソッドを示します。
例31-23 データ・コントロール・クラスの概要
import oracle.adf.model.adapter.AbstractImpl;
import oracle.binding.DataControl;
import java.util.HashMap;
public class SampleDataControl extends AbstractImpl implements ManagedDataControl
{
public boolean invokeOperation(java.util.Map map,
oracle.binding.OperationBinding action)
{
// you need to implement this method.
// see Section 31.5.4, "Implementing the invokeOperation Method".
}
public String getName()
{
// you need to implement this method.
// see Section 31.5.5, "Implementing the getName Method".
}
public void release(int flags)
{
// you need to implement this method.
// see Section 31.5.6, "Implementing the release Method".
}
public Object getDataProvider()
{
// you need to implement this method.
// see Section 31.5.7, "Implementing the getDataProvider Method".
}
}
例31-24に、SampleDataControlクラスの完全なソースを示します。
例31-24 SampleDataControlクラスの完全なソース
package oracle.adfinternal.model.adapter.sample;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.naming.Context;
import oracle.binding.ManagedDataControl;
import oracle.binding.OperationInfo;
import oracle.adf.model.adapter.AdapterException;
import oracle.adf.model.adapter.AbstractImpl;
import oracle.adf.model.adapter.dataformat.CSVHandler;
import oracle.adfinternal.model.adapter.url.SmartURL;
// Data control that represents a URL data source with CSV data format.
public class SampleDataControl extends AbstractImpl
implements ManagedDataControl
{
//URL to access the data source
private String mCSVUrl = null;
public SampleDataControl()
{
}
public SampleDataControl(String csvUrl)
{
mCSVUrl = csvUrl;
}
public boolean invokeOperation(java.util.Map map,
oracle.binding.OperationBinding action)
{
Context ctx = null;
try
{
// We are interested of method action binding only.
if (action == null)
{
return false;
}
OperationInfo method = action.getOperationInfo();
// No method defined, we are not interested.
if (method == null)
{
return false;
}
// Execute only when the adapter execute is invoked
if (METHOD_EXECUTE.equals(method.getOperationName()))
{
Object retVal = null;
if (mCSVUrl != null)
{
SmartURL su = new SmartURL(mCSVUrl);
InputStream isData = su.openStream();
CSVHandler csvHandler =
new CSVHandler(isData,true,"UTF-8",",","\"");
Map properties = new HashMap();
retVal = csvHandler.getResult(properties);
}
Map rootDataRow = new java.util.HashMap(2);
rootDataRow.put(SampleDCDef.RESULT_ACC_NAME, retVal);
ArrayList aRes = new ArrayList(2);
aRes.add(rootDataRow);
processResult(aRes.iterator(), map, action);
return true;
}
}
catch (AdapterException ae)
{
throw ae;
}
catch (Exception e)
{
throw new AdapterException(e);
}
return false;
}
/**
* Perform request level initialization of the DataControl.
* @param requestCtx a HashMap representing request context.
*/
public void beginRequest(HashMap requestCtx)
{
}
/**
* perform request level cleanup of the DataControl.
* @param requestCtx a HashMap representing request context.
*/
public void endRequest(HashMap requestCtx)
{
}
/**
* return false as resetState was deferred to endRequest processing
*/
public boolean resetState()
{
return false;
}
/**
* returns the name of the data control.
*/
public String getName()
{
return mName;
}
/**
* releases all references to the objects in the data provider layer
*/
public void release(int flags)
{
}
/**
* Return the Business Service Object that this datacontrol is associated with.
*/
public Object getDataProvider()
{
return null;
}
}
データ・コントロール・クラスでinvokeOperationメソッドを実装する必要があります。フレームワークは、ユーザーがビュー・ページを実行すると、このメソッドを起動します。
このメソッドは、DataControlインタフェースで宣言されます。メソッドには次のシグネチャがあります。
例31-25 invokeOperationのシグネチャ
public boolean invokeOperation(java.util.Map bindingContext,
oracle.binding.OperationBinding action);
bindingContextパラメータには、データソースからフェッチされた戻り値が入ります。値を取得するキーは、フレームワークによって生成されます。通常、値をフィルタリングまたは変換する必要がないかぎり、値を処理する必要はありません。
actionパラメータでは、値を生成したメソッドを指定します。メソッドには、Webサービスの場合のように、基礎となるサービスでサポートされているメソッドを指定できます。データ・コントロールでデフォルトの動作を無効にする必要がある場合、フレームワークは、一部の組込みアクションにもデータ・コントロールをコールします。このパラメータをチェックして、アクションを処理する必要があるかどうかを確認できます。メソッドを公開しないデータソースを表すデータ・コントロールの場合、フレームワークはアクションAbstractImpl.METHOD_EXECUTEを作成して、データ・コントロールの問合せを実行します。
アクションを処理しない場合、メソッドはfalseを返します。
シンプルなCSVアダプタでは、invokeOperationメソッドは、データをフェッチする前にメソッドがMETHOD_EXECUTEであることをチェックします。CSVHandlerクラスが起動され、次にCSVParserクラスが起動されて、CSVファイルからデータが取得されます。
例31-26 invokeOperationメソッド
public boolean invokeOperation(java.util.Map map,
oracle.binding.OperationBinding action)
{
Context ctx = null;
try
{
// We are interested in method action binding only.
if (action == null)
{
return false;
}
OperationInfo method = action.getOperationInfo();
// No method defined, we are not interested.
if (method == null)
{
return false;
}
// Execute only when the adapter execute is invoked
if (METHOD_EXECUTE.equals(method.getOperationName()))
{
Object retVal = null;
if (mCSVUrl != null)
{
SmartURL su = new SmartURL(mCSVUrl);
InputStream isData = su.openStream();
CSVHandler csvHandler =
new CSVHandler(isData, true, "UTF-8", ",", "\"");
Map properties = new HashMap();
retVal = csvHandler.getResult(properties);
}
Map rootDataRow = new java.util.HashMap(2);
rootDataRow.put(SampleDCDef.RESULT_ACC_NAME, retVal);
ArrayList aRes = new ArrayList(2);
aRes.add(rootDataRow);
processResult(aRes.iterator(), map, action);
return true;
}
}
catch (AdapterException ae)
{
throw ae;
}
catch (Exception e)
{
throw new AdapterException(e);
}
return false;
}
invokeOperationは、データのフェッチ後にprocessResultメソッドをコールする点に注意してください。詳細は、次の項を参照してください。
invokeOperationは、processResultをコールしてフレームワークに更新された値を提供します。メソッドは結果をバインディング・コンテキストに入れて、フレームワークから取り出せるようにします。メソッドの構文は次のとおりです。
例31-27 processResultの構文
public void processResult(Object result,
Map bindingContext,
oracle.binding.OperationBinding action)
resultパラメータでは、更新された値を指定します。
bindingContextパラメータでは、バインディング・コンテキストを指定します。通常は、invokeOperationメソッドに渡されたバインディング・コンテキストと同じです。
actionパラメータでは、操作を指定します。通常は、invokeOperationメソッドに渡されたアクション値と同じです。
バインディング・コンテキストで使用されるデータ・コントロール名を返す、getNameメソッドを実装します。
このメソッドは、DataControlインタフェースで宣言されます。次のシグネチャがあります。
シンプルなCSVアダプタでは、メソッドは単純にmNameを返します。これはAbstractImplクラスで宣言された変数です。
フレームワークはreleaseメソッドをコールして、データ提供レイヤーでのオブジェクトへのすべての参照を解放します。
このメソッドは、DataControlインタフェースで宣言されます。次のシグネチャがあります。
flagsパラメータは、解放する参照を示します。
REL_ALL_REFS: データ・コントロールは、ビュー・オブジェクトとモデル・オブジェクトへのすべての参照を解放します。
REL_DATA_REFS: データ・コントロールは、データ・プロバイダ・オブジェクトへの参照を解放します。
REL_VIEW_REFS: データ・コントロールは、ビュー・オブジェクトまたはUIレイヤー・オブジェクトへのすべての参照を解放します。
シンプルなCSVデータ・コントロール・アダプタでは、releaseメソッドは空白です。ただし、データ・コントロールで接続を使用する場合、このメソッドで接続を閉じ、解放する必要があります。
ADFインタフェースを実装する必須クラスに加えて、必要に応じてアダプタのサポート・クラスを作成できます。シンプルなCSVアダプタには、2つの追加クラス、CSVHandlerとCSVParserが含まれています。これらのクラスは、CSVファイルを行とフィールドに読み込み、解析します。これらのクラスの完全なソース・リストは、31.11項「サポート・ファイルの内容」を参照してください。
JDeveloperのアダプタを定義するには、adapter-definition.xmlと呼ばれるファイルを作成し、meta-infと呼ばれるディレクトリに入れます。ファイル名とディレクトリ名では、大文字と小文字が区別されます。
一般的なadapter-definition.xmlファイルには、次のエントリがあります。
例31-32 adapter-definition.xmlファイルの説明
<AdapterDefinition> <Adapter Name="unique name for the adapter" ClassName="full name of class that implements AbstractAdapter"> <Schema Namespace="name of schema that defines the data control metadata for this adapter" Location="location of schema definition file"/> <Source> <Type Name="name of source type that the adapter can handle to create a data control" JDevNode="full class name of supported node"/> </Source> <JDevContextHook Class="full name of class that provides the JDeveloper context hook, if any"/> <Dependencies> <Library Path="full path name of the JAR file that the adapter needs in order to run"/> </Dependencies> </Adapter> </AdapterDefinition>
AdapterDefinitionタグは、すべてのアダプタのコンテナ・タグです。
Adapterタグは、アダプタを説明します。次の属性があります。
Nameでは、アダプタの一意の名前を指定します。フレームワークは、この名前を使用してアダプタを識別します。
ClassNameでは、AbstractAdapterを実装する完全なJavaクラスを指定します。
Schemaタグでは、アダプタのメタデータのネームスペースとスキーマ定義を定義します。JDeveloperは、設計時にメタデータを検証できるように、スキーマを登録します。アダプタがサポートするすべてのネームスペースとスキーマを定義できます。これはオプションです。
Sourceタグでは、アダプタがサポートするノード(またはデータソース)のタイプを指定します。次の属性があります。
JDevNodeでは、サポートされるノード・タイプのJavaクラスを指定します。このノード・タイプは、JDeveloperの接続ナビゲータに表示されます。
Name: 任意の文字列
JDevContextHookタグでは、ポップアップ・メニュー(ユーザーが構造ペインでデータ・コントロール・インスタンスのメタデータ・ノードを右クリックすると表示されるメニュー)に追加するメニューを指定します。
Dependenciesタグは、アダプタが実行時に必要とするライブラリ・ファイルをリストします。フレームワークは、ユーザーがアダプタに基づくデータ・コントロールを使用すると、プロジェクトにライブラリ・ファイルを追加します。
シンプルなCSVデータ・コントロール・アダプタのadapter-definition.xmlファイルは、次のとおりです。
例31-33 シンプルなCSVアダプタのadapter-definition.xmlファイル
<AdapterDefinition>
<Adapter Name="oracle.adfm.adapter.SampleDataControl"
ClassName="oracle.adfinternal.model.adapter.sample.SampleDCAdapter">
<Schema Namespace="http://xmlns.oracle.com/adfm/adapter/sample"
Location="/oracle/adfinternal/model/adapter/sample/sampleDC.xsd"/>
<Source>
<Type Name="csvNode" JDevNode="oracle.ide.model.TextNode"/>
</Source>
<Dependencies>
<Library Path="${oracle.home}/jlib/sampledc.jar"/>
</Dependencies>
</Adapter>
</AdapterDefinition>
sampleDC.xsdファイルについては、31.11.1項「sampleDC.xsd」を参照してください。
アダプタをビルドするには、プロジェクトに次のライブラリを追加する必要があります。
JDeveloperの「プロジェクト・プロパティ」ダイアログの左側で「ライブラリ」を選択します。
右側の「ライブラリの追加」をクリックして、次のライブラリを追加します。
JSR-227 API
ADF Model Generic Runtime
Oracle XML Parser v2
右側の「Jar/ディレクトリの追加」をクリックして、次のライブラリを追加します。
JDEV_HOME/ide/lib/ide.jar
JDEV_HOME/ide/lib/javatools.jar
JDEV_HOME/bc4j/jlib/dc-adapter.jar
アダプタをJDeveloperにデプロイするには、次の手順を実行します。
meta-infディレクトリ(adapter-definition.xmlファイルが入っているディレクトリ)にextension.xmlファイルを作成します。
これは、アダプタをJDeveloperの拡張機能としてデプロイしているためです。extension.xmlを使用して、JARファイルをJDeveloperのクラスパスに追加します。
extension.xmlファイルには、次の行があります。
例31-34 extension.xml
<?xml version = '1.0' encoding = 'UTF-8'?>
<extension xmlns="http://jcp.org/jsr/198/extension-manifest"
id="oracle.adfm.sample-adapters"
version="10.1.3.36.45"
esdk-version="1.0">
<name>ADFm Sample Adapter</name>
<owner>Oracle Corporation</owner>
<dependencies>
<import>oracle.BC4J</import>
<import>oracle.j2ee</import>
</dependencies>
<classpaths>
<classpath>../../BC4J/jlib/dc-adapters.jar</classpath>
<classpath>../../jlib/sampledc.jar</classpath>
</classpaths>
<hooks>
<!-- Adapter-specific data control library definitions -->
<libraries xmlns="http://xmlns.oracle.com/jdeveloper/1013/jdev-libraries">
<library name="Sample Data Control" deployed="true">
<classpath>../../jlib/sampledc.jar</classpath>
</library>
</libraries>
</hooks>
</extension>
extension.xmlファイルのタグの詳細は、JDEV_HOME/jdev/doc/extension/ide-extension-packaging.htmlというファイルを参照してください。
アダプタのクラス・ファイル(adapter-definition.xmlファイルとextension.xmlファイル)を含むJARファイルを作成します。XMLファイルは、meta-infディレクトリに入れる必要があります。
シンプルなCSVアダプタでは、このJARファイルはsampledc.jarと呼ばれ、次のファイルが含まれています。
例31-35 sampledc.jar
connections.xml extension/meta-inf/extension.xml meta-inf/adapter-definition.xml meta-inf/Manifest.mf oracle/adfinternal/model/adapter/sample/CSVHandler$1.class oracle/adfinternal/model/adapter/sample/CSVHandler.class oracle/adfinternal/model/adapter/sample/CSVParser.class oracle/adfinternal/model/adapter/sample/SampleDataControl.class oracle/adfinternal/model/adapter/sample/SampleDCAdapter.class oracle/adfinternal/model/adapter/sample/SampleDCDef.class
JARファイルをJDEV_HOME/jlibディレクトリにコピーします。
meta-infディレクトリに、extension.xmlファイルとmanifestファイルのみを含む別のJARファイルを作成します。シンプルなCSVアダプタでは、このJARファイルはoracle.adfm.sampledc.10.1.3.jarと呼ばれ、次のファイルが含まれています。
2番目のJARファイル(oracle.adfm.sampledc.10.1.3.jarなど)をJDEV_HOME/jdev/extensionsディレクトリにコピーします。
JDeveloperを実行している場合は停止します。
JDeveloperを起動します。アダプタがサポートするノード・タイプを右クリックすると、「データ・コントロールの作成」メニュー項目が表示されます。
JDeveloper拡張機能の詳細は、Extension SDKをダウンロードしてください。
JDeveloperで、「ヘルプ」→「更新の確認」を選択します。更新の確認ウィザードが起動します。
ウィザードの「ようこそ」ページで、「次へ」をクリックします。
「ソース」ページで「更新センターの検索」を選択し、そのセクションにリストされているすべての場所を選択します。「次へ」をクリックします。
「更新」ページで「Extension SDK」を選択します。「次へ」をクリックして、Extension SDKをダウンロードし、インストールします。
サマリー・ページで「終了」をクリックします。Extension SDKファイルにアクセスするには、JDeveloperを再起動する必要があります。
Extension SDKのヘルプは、JDeveloperのオンライン・ヘルプを開き、「JDeveloperの拡張」→「Extention SDKでJDeveloperを拡張」に移動して参照できます。
JDeveloperオンライン・ヘルプには、この章で説明したクラスに関する説明がJavadocフォーマットで記載されています。
表31-2 Javadocの場所
| クラス/インタフェース | オンライン・ヘルプでのJavadocの場所 |
|---|---|
|
|
「リファレンス」→「Oracle ADF Model API Reference」→「Packages」→「oracle.adf.model.adapter」→「Class Summary」 |
|
|
「リファレンス」→「Oracle ADF Model API Reference」→「Packages」→「oracle.binding.meta」→「Interface Summary」 |
|
|
「リファレンス」→「Oracle ADF Model API Reference」→「Packages」→「oracle.binding」→「Interface Summary」 |
この項では、次のファイルの内容を示します。
例31-37に、sampleDC.xsdファイルの内容を示します。
例31-37 sampleDC.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://xmlns.oracle.com/adfm/adapter/test"
xmlns="http://xmlns.oracle.com/adfm/adapter/test"
elementFormDefault="qualified">
<xsd:element name="Definition">
<xsd:complexType>
<xsd:attribute name="SourceLocation" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
</xsd:schema>
例31-38に、CSVHandlerクラスの内容を示します。
例31-38 CSVHandler
package oracle.adfinternal.model.adapter.sample;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import oracle.binding.meta.DefinitionContext;
import oracle.binding.meta.StructureDefinition;
import oracle.adf.model.utils.SimpleStringBuffer;
import oracle.adf.model.adapter.AdapterException;
import oracle.adf.model.adapter.dataformat.AttributeDef;
import oracle.adf.model.adapter.dataformat.StructureDef;
import oracle.adfinternal.model.adapter.sample.CSVParser;
import oracle.adf.model.adapter.utils.Utility;
/**
* Format handler for character separated values.
* <p>
* This class generates structures according to the JSR 227 specification from
* a CSV data stream by parsing the data. The data types are guessed from the
* value of the first data line. It can extract values from a CSV data stream
* as well.
* <p>
* Data controls that deals with CSV data can use this class to generate data
* and structure.
*
* @version 1.0
* @since 10.1.3
*/
public class CSVHandler
{
// stream containing the data.
private InputStream mDataStream;
// if the first row contains the names
private boolean mIsFirstRowNames = false;
// Encoding styles
private String mEncStyle;
// Character value separator
private String mDelimiter;
// Character used to quote a multi-word string
private String mQuoteChar;
// Column names
private List mColNames = null;
////////////////////////////// Constructors //////////////////////////////////
/**
* Creats a CSV format handler object.
*
* @param is input stream that contains the CSV data.
* @param isFirstRowNames flag to indicate if the first row of the CSV data
* can be treated as column names.
* @param encodingStyle encoding style of the data.
* @param delim character value separators.
* @param quoteChar value that can be treated as quote.
*/
public CSVHandler(
InputStream is,
boolean isFirstRowNames,
String encodingStyle,
String delim,
String quoteChar)
{
mDataStream = is;
mIsFirstRowNames = isFirstRowNames;
mEncStyle = encodingStyle;
mDelimiter = delim;
mQuoteChar = quoteChar;
}
///////////////////// Impl of FormatHandler //////////////////////////////////
/**
* Returns the structure definition extracted for the data format.
* <p>
*
* @param name name of the root structure.
* @param ctx definition context information.
* @return the structure information extracted.
*/
public StructureDefinition getStructure(String name, DefinitionContext ctx)
{
StructureDef attrParent = null;
try
{
CSVParser parser;
if (mEncStyle == null)
{
parser = new CSVParser(mDataStream);
}
else
{
parser = new CSVParser(mDataStream, mEncStyle);
}
parser.setSeparators(mDelimiter.toCharArray());
if (mQuoteChar != null && mQuoteChar.length() != 0)
{
parser.setQuoteChar(mQuoteChar.charAt(0));
}
// Get the column names
Iterator colNames = getColNames(parser).iterator();
// Create the structure definition
attrParent = new StructureDef(name);
// Parse the data to get the attributes
if (mIsFirstRowNames)
{
parser.nextLine();
}
String[] vals = parser.getLineValues();
if (vals != null)
{
int i = 0;
while (colNames.hasNext())
{
String type = "java.lang.String";
if (i < vals.length)
{
type = checkType(vals[i]);
++i;
}
AttributeDef attr =
new AttributeDef((String) colNames.next(), attrParent, type);
attrParent.addAttribute(attr);
}
}
else
{
while (colNames.hasNext())
{
AttributeDef attr =
new AttributeDef((String) colNames.next(),
attrParent, "java.lang.String");
attrParent.addAttribute(attr);
}
}
}
catch (Exception e)
{
throw new AdapterException(e);
}
return attrParent;
}
/**
* Returns the resulting data extracted from the input.
* @param params parameters passed containig the context information.
* @return <code>Iterator</code> of <code>Map</code> objects for the result.
* If no data found it can return null. The <code>Map</code>
* contains the value of attributes as defined in the data structure.
* For complex data, <code>Map</code>s can contain other iterator of
* <code>Map</code>s as well.
*/
public Iterator getResult(Map params)
{
try
{
final CSVParser parser;
if (mEncStyle == null)
{
parser = new CSVParser(mDataStream);
}
else
{
parser = new CSVParser(mDataStream, mEncStyle);
}
parser.setSeparators(mDelimiter.toCharArray());
if (mQuoteChar != null && mQuoteChar.length() != 0)
{
parser.setQuoteChar(mQuoteChar.charAt(0));
}
final List cols = getColNames(parser);
final boolean bEndOfData = (mIsFirstRowNames) ? !parser.nextLine() : false;
//return the data iterator
return new Iterator()
{
CSVParser _parser = parser;
Iterator _colNames = cols.iterator();
boolean _eof = bEndOfData;
public void remove()
{
}
public boolean hasNext()
{
return !_eof;
}
public Object next()
{
try
{
if (_eof)
{
return null;
}
java.util.HashMap map = new java.util.HashMap(5);
// Create the current row as Map
String[] data = _parser.getLineValues();
int i = 0;
while (_colNames.hasNext())
{
String val = null;
if (i < data.length)
{
val = data[i];
}
map.put(_colNames.next(), val);
i++;
}
// get the next data line.
_eof = !_parser.nextLine();
return map;
}
catch (Exception e)
{
throw new AdapterException(e);
}
}
};
}
catch (AdapterException ae)
{
throw ae;
}
catch (Exception e)
{
throw new AdapterException(e);
}
}
//============================================================================
// Class Helper Methods
//============================================================================
/**
* Attempts to obtain the Java type from the string value.
* @param data String value whose datatype has to be guessed.
* @return Java type name.
*/
private String checkType(String data)
{
try
{
// We first try to convert the value into a long number.
// If successful, we will use long; if it throws NumberFormatException,
// we will attempt to convert it to float. If this too fails, we return
// string.
if (data != null)
{
try
{
// Try to conver the value into an integer number.
long numTest = Long.parseLong(data);
return "java.lang.Long"; //NOTRANS
}
catch (NumberFormatException nfe)
{
// Try to convert the value into float number.
float numTest = Float.parseFloat(data);
return "java.lang.Float"; //NOTRANS
}
}
else
{
return "java.lang.String"; //NOTRANS
}
}
catch (NumberFormatException nfe)
{
// If conversion failed, we assume this is a string.
return "java.lang.String";
}
}
/**
* Gets the column names.
*/
/**
* Gets the column names.
*/
private List getColNames(CSVParser parser)
{
try
{
if (mColNames == null)
{
// Get the first row. If the first row is NOT the column names, we need
// to generate column names for them.
if (!parser.nextLine())
{
// No data found.
// ToDo: resource
new Exception("No data");
}
mColNames = new java.util.ArrayList(10);
String[] cols = parser.getLineValues();
if (mIsFirstRowNames)
{
makeValidColumnNames(cols);
for (int i = 0; i < cols.length; i++)
{
mColNames.add(cols[i]);
}
}
else
{
for (int i = 0; i < cols.length; i++)
{
String colName =
new SimpleStringBuffer(20).append("Column").append(i).toString();
mColNames.add(colName);
}
}
}
return mColNames;
}
catch (Exception e)
{
throw new AdapterException(e);
}
}
/**
* Make valid column names for all columns in CSV data source.
*
* This method applies the following rules to translate the given string
* to a valid column name which can be accepted by EL:
*
* 1. If the first character of the string is digit,
* prefix the string with '_'.
* 2. Translate any characters other than letter, digit, or '_' to '_'.
*
*
*/
private String[] makeValidColumnNames(String[] cols)
{
for (int i = 0; i <cols.length; i++)
{
// Trim out leading or ending white spaces
if (cols[i] != null && cols[i].length() > 0)
{
cols[i] = cols[i].trim();
}
if (cols[i] == null || cols[i].length() == 0)
{
// Default as "column1", "column2", ... if column name null
cols[i] = new SimpleStringBuffer("column").append(i+1).toString();
}
else
{
// Check special characters
try
{
cols[i] = Utility.normalizeString(cols[i]);
}
catch (Exception e)
{
// On error, simply default to "columnX".
cols[i] = new SimpleStringBuffer("column").append(i+1).toString();
}
}
}
return cols;
}
}
例31-39に、CSVParserクラスの内容を示します。
例31-39 CSVParser
package oracle.adfinternal.model.adapter.sample;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.ArrayList;
import oracle.adf.model.utils.SimpleStringBuffer;
public final class CSVParser
{
/////////////////////////////// Constants ////////////////////////////////////
/** UTF8 encoding, used for hadling data in different languages. */
public static final String UTF8_ENCODING = "UTF8";
/** Quote character */
private static char CHAR_QUOTE = '"';
/** Comma (seperator) character */
private static char CHAR_COMMA = ',';
/////////////////////////////// Class Variables //////////////////////////////
/**
* CSV stream reader
*/
private LineNumberReader mReader;
/** Buffer to store one line of values. */
private ArrayList mValueArrayList = new ArrayList();
/** Buffer to store one string value. */
private SimpleStringBuffer mValueBuffer = new SimpleStringBuffer(256);
/** Current processed line. */
private String mLine = null;
/** Current character position in the current line. */
private int mLinePosition = -1;
/** Length of current line. */
private int mLineLength = 0;
/** If last character is comma. */
private boolean mLastCharIsComma = false;
/** Value separator character set. The separator can be one of these values.*/
private char[] mSepCharSet = {CHAR_COMMA};
/** Quote character. */
private char mQuoteChar = CHAR_QUOTE;
////////////////////////////// Constructors //////////////////////////////////
/**
* Constructor
*
* @param pInputStream CSV input stream
* @throws Exception any error occurred
*/
public CSVParser(InputStream pInputStream) throws Exception
{
// If no encoding is passed in, use "UTF-8" encoding
this(pInputStream, UTF8_ENCODING);
}
/**
* Constructor
*
* @param pInputStream CSV input stream
* @param pEnc character encoding
* @throws Exception any error occurred
*/
public CSVParser(InputStream pInputStream, String pEnc) throws Exception
{
if (pInputStream == null)
{
throw new Exception("Null Input Stream."); //TODO: Resource
}
mReader = new LineNumberReader(new InputStreamReader(pInputStream, pEnc));
}
///////////////////////////// Public Methods /////////////////////////////////
/**
* Sets the separator characters as a list of possible separators for the
* data. CSV data may have more than one separators. By default this parser
* considers comma (,) as the data separator.
* @param seps Array of separator charactors.
*/
public void setSeparators(char[] seps)
{
if ((seps != null) && (seps.length > 0))
{
mSepCharSet = seps;
}
}
/**
* Sets the quote character.
* @param ch Quote character.
*/
public void setQuoteChar(char ch)
{
mQuoteChar = ch;
}
/**
* Moves to the next line of the data.
* @return returns false if the end of data reached.
* @throws Exception any error occurred
*/
public boolean nextLine() throws Exception
{
setLine(mReader.readLine());
if (mLine == null)
{
// End of file
mValueArrayList.clear();
return false;
}
parseLine();
return true;
}
/**
* Gets values of next line.
* @return next line elements from input stream. If end of data reached,
* it returns null.
* @throws Exception any error occurred
*/
public String[] getLineValues() throws Exception
{
if (mValueArrayList.size() > 0)
{
String[] ret = new String[mValueArrayList.size()];
return (String[]) mValueArrayList.toArray(ret);
}
return null;
}
//////////////////////////// Class Helpers ///////////////////////////////////
/**
* Checks if the character is a valid separator.
*/
private boolean isSeparator(char ch)
{
for (int i = 0; i < mSepCharSet.length; i++)
{
if (ch == mSepCharSet[i])
{
return true;
}
}
return false;
}
/**
* Tests if end of line has reached.
* @return true if end of line.
*/
public boolean isEndOfLine()
{
// If last char is comma, must return at least one more value
return (mLinePosition >= mLineLength) && (!mLastCharIsComma);
}
/**
* Sets current line to be processed
*
* @param line the line to be processed
*/
private void setLine(String line)
{
mLine = line;
if (line != null)
{
mLineLength = line.length();
mLinePosition = 0;
}
}
/**
* If next character is quote character
*
* @return true if next character is quote
*/
private boolean isNextCharQuote()
{
if ((mLinePosition + 1) >= mLineLength)
{
// no more char in the line
return false;
}
else
{
char ch = mLine.charAt(mLinePosition + 1);
if (ch == mQuoteChar)
{
return true;
}
else
{
return false;
}
}
}
/**
* Parse one line.
*
* @return values of the line
* @throws Exception any error occurred
*/
private void parseLine() throws Exception
{
mValueArrayList.clear();
String[] values = null;
String value = null;
while (!isEndOfLine())
{
value = getNextValue();
mValueArrayList.add(value);
}
}
/**
* Gets next value from current line.
* @return next data value.
*/
private String getNextValue() throws Exception
{
mLastCharIsComma = false;
// Clean up value buffer first
if (mValueBuffer.length() > 0)
{
mValueBuffer.setLength(0);
}
boolean insideQuote = false;
boolean firstChar = true;
boolean endValue = false;
// Scan char by char
while ((mLinePosition < mLineLength) && !endValue)
{
boolean copyChar = true;
char ch = mLine.charAt(mLinePosition);
// If first char
if (firstChar)
{
// Only check quote at first char
if (ch == mQuoteChar)
{
insideQuote = true;
copyChar = false;
}
// Also need to check comma at first char
else if (isSeparator(ch))
{
copyChar = false;
endValue = true;
mLastCharIsComma = true;
}
firstChar = false;
}
// Not first char but inside quote
else if (insideQuote)
{
// Check end quote
if (ch == mQuoteChar)
{
copyChar = false;
// Two sucesstive quote chars inside quote means quote char itself
if (isNextCharQuote())
{
mLinePosition++;
}
// Otherwise it is ending quote
else
{
insideQuote= false;
}
}
}
// Not first char and outside quote
else
{
// Check comma
if (isSeparator(ch))
{
copyChar = false;
endValue = true;
mLastCharIsComma = true;
}
}
if (copyChar)
{
mValueBuffer.append(ch);
}
mLinePosition++;
}
if (mValueBuffer.length() > 0)
{
return mValueBuffer.toString();
}
else
{
return null;
}
}
}