ExecuteQuery.run が呼び出され、ユーザーがクエリーの選択と実行を行えるようになります。この例の、ExecuteQuerty.run メソッドには ViewRecord.Viewer と呼ばれる ExeuteQuerty.Viewer の実インスタンスがあります。 これにより、結果セットの表示に [開く] ボタンが現れ、クリックすると Viewer.view メソッドが呼び出されます。 Viewer.view メソッド (以下で定義します) には、結果セットの選択された行に関連付けられたリソースのプロキシが渡されます。そのプロキシは CqRowData.getRecord() メソッドを使用して取得します。
したがって、この例の実質は、Viewer.view メソッドの中で具体化されます。
static class Viewer implements ExecuteQuery.Viewer {
Viewer(CqProvider provider) { m_provider = provider; }
/**
* @see com.ibm.rational.stp.client.samples.ExecuteQuery.Viewer#view(com.ibm.rational.wvcm.stp.cq.CqRecord)
*/
public JFrame view(CqRecord record)
{
if (record != null) try {
record = (CqRecord)record.doReadProperties(RECORD_PROPERTIES);
return showRecord("View: ", record, null);
} catch (WvcmException ex){
ex.printStackTrace();
}
return null;
}
/**
* テキスト ウィンドウに添付ファイル リソースの内容を表示します。
*
* @param attachment 表示する添付ファイルの添付ファイル プロキシです。
*
*/
public void view(CqAttachment attachment)
{
if (attachment != null) try{
File file = File.createTempFile("attach", "tmp");
attachment.doReadContent(file.getAbsolutePath(), null);
BrowserDataModel.showFile(attachment.getDisplayName(), file);
} catch(Throwable ex) {
Utilities.exception(null, "View Attachment", ex);
}
}
/**
* テーブルにレコード リソースの ALL_FIELD_VALUES プロパティを表示します。
* テーブルの列は、{@link #fieldMetaProperties} 配列の内容によって
* 決まります。ほとんどのオブジェクトの表示は、オブジェクト独自の
* toString() メソッドによって実装されます。
*
* @param title テーブルを含むウィンドウのタイトル文字列
* @param record 表示されるレコードのレコード プロキシ。これが、ALL_FIELD_VALUES
* プロパティを定義する必要があります。また、そのプロパティ内の FieldValues
* では、{@link #fieldMetaProperties} 配列にリストされている
* メタ プロパティを定義する必要があります。
* @param future プロパティ テーブルとともに表示される追加の
* ウィンドウ コンポーネント。(この例の拡張機能で使用されます)。
* @return このメソッドで作成される GUI コンポーネントを含む RecordFrame 構造。
*
* @throws WvcmException
*/
RecordFrame showRecord(String title,
CqRecord record,
JComponent[] future) throws WvcmException
{
final StpProperty.List<CqFieldValue<?>> fields =
record.getAllFieldValues();
// テーブル モデルを定義します。各行はレコード リソースの
// プロパティ、各列は名前、タイプ、および値などの、そのプロパティの
// メタ プロパティです。
TableModel dataModel = new AbstractTableModel() {
private static final long serialVersionUID = 1L;
public int getColumnCount() { return fieldMetaProperties.length; }
public int getRowCount() { return fields.size();}
public Object getValueAt(int row, int col)
{
try {
Object val = fields.get(row)
.getMetaProperty((MetaPropertyName<?>)
fieldMetaProperties[col].getRoot());
if (val instanceof CqRecord)
return ((CqRecord)val).getUserFriendlyLocation()
.getName();
else if (val instanceof CqAttachmentFolder)
return ((CqAttachmentFolder)val)
.getAttachmentList().size()
+ " attachments";
else
return val;
} catch(Throwable ex) {
if (ex instanceof StpException) {
return ((StpException)ex).getStpReasonCode();
} else {
String name = ex.getClass().getName();
return name.substring(name.lastIndexOf(".")+1);
}
}
}
public String getColumnName(int col)
{ return fieldMetaProperties[col].getRoot().getName(); }
};
// 表示レイアウトを定義します
final JTable table = new JTable(dataModel);
final JPanel panel = new JPanel(new BorderLayout());
final JPanel buttons = new JPanel(new FlowLayout());
final JButton button = new JButton("View");
final RecordFrame frame =
new RecordFrame(title + record.getUserFriendlyLocation().toString(),
table, fields);
// 選択されたレコードまたは添付ファイル フィールドを表示するボタンを追加します。
buttons.add(button, BorderLayout.SOUTH);
button.setEnabled(false);
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0)
{
int[] selected = table.getSelectedRows();
for (int i =0; i < selected.length; ++i) {
int row = selected[i];
if (isAttachmentList(fields, row)) {
view(selectAttachment(frame, fields, row, "View"));
} else {
view(getRecordReferencedAt(fields, row));
}
}
}
});
// さらにボタンを追加します (後の例で使用されます)
if (future != null)
for(int i = 0; i < future.length; ++i) buttons.add(future[i]);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// 選択の変更を通知するよう求め、レコード値の入ったフィールドまたは添付
// フィールドが選択された場合のみビュー ボタンを使用可能化します
ListSelectionModel rowSM = table.getSelectionModel();
rowSM.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()){
int[] selected = table.getSelectedRows();
button.setEnabled(false);
for (int i=0; i <selected.length; ++i)
if (getRecordReferencedAt(fields, selected[i]) != null
|| isAttachmentList(fields, selected[i])) {
button.setEnabled(true);
break;
}
}
}
});
panel.add(new JScrollPane(table), BorderLayout.CENTER);
panel.add(buttons, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setContentPane(panel);
frame.setBounds(g_windowX += 10, g_windowY += 10, 600, 300);
frame.setVisible(true);
return frame;
}
protected CqProvider m_provider;
}
/**
* それぞれのレコードのフィールド値から要求されるプロパティ。これには、
* 添付ファイルについての特定の追加情報が含まれます
*/
static final PropertyRequest VALUE_PROPERTIES =
new PropertyRequest(StpResource.USER_FRIENDLY_LOCATION,
CqAttachmentFolder.ATTACHMENT_LIST
.nest(CqAttachment.DISPLAY_NAME,
CqAttachment.FILE_NAME,
CqAttachment.FILE_SIZE,
CqAttachment.DESCRIPTION));
/** 要求されて表示されるフィールドのメタ プロパティ */
static final NestedPropertyName[] fieldMetaProperties =
new PropertyRequest(CqFieldValue.NAME,
CqFieldValue.REQUIREDNESS,
CqFieldValue.TYPE,
CqFieldValue.VALUE.nest(VALUE_PROPERTIES)).toArray();
/**
* このビューアによって表示されるレコードのデータの読み取りに使用する
* PropertyRequest。ALL_FIELD_VALUES プロパティ自体のメタ プロパティではなく、
* ALL_FIELD_VALUES リスト内のフィールドのメタ プロパティの要求に使用する間接参照
* のレベルですので注意してください。
*/
final static PropertyRequest RECORD_PROPERTIES =
new PropertyRequest(CqRecord.USER_FRIENDLY_LOCATION,
CqRecord.ALL_FIELD_VALUES
.nest(StpProperty.VALUE.nest(fieldMetaProperties)));
/**
* フィールドのプロパティ値を検査し、それがレコードを参照している場合は、参照される
* レコードのプロキシを戻します。それ以外の場合は NULL を戻します。
* @param fields 検査する Property.List。
* @param row 検査するリスト内のエレメントのインデックス。
* @return フィールドがレコードを参照する場合はレコード プロキシ。それ以外は NULL です。
*/
static CqRecord getRecordReferencedAt(StpProperty.List<CqFieldValue<?>> fields,
int row)
{
try {
CqFieldValue field = fields.get(row);
if (field.getFieldType() == ValueType.RESOURCE
&& field.getValue() instanceof CqRecord)
return (CqRecord)field.getValue();
} catch (WvcmException ex) { ex.printStackTrace(); }
return null;
}
/**
* 示されたフィールドが添付ファイル フィールドかどうか。
* @param fields 検査する Property.List。
* @param row 検査するリスト内のエレメントのインデックス。
* @return true 指定されたインデックスが添付ファイル フィールドの場合。
*/
static boolean isAttachmentList(StpProperty.List<CqFieldValue<?>> fields,
int row)
{
if (row >= 0) try {
CqFieldValue field = fields.get(row);
return field.getFieldType() == ValueType.ATTACHMENT_LIST;
} catch (WvcmException ex) { ex.printStackTrace(); }
return false;
}
/**
* ユーザーに、レコードの指定されたフィールドに関連付けられた添付ファイル
* のリストを示し、ユーザーが 1 つを選択できるようにします。
*
* @param frame このメソッドによって生成されたダイアログの親フレーム。
* @param fields 検査する Property.List。
* @param row 検査するリスト内のエレメントのインデックス。
* @param op 選択された添付ファイルに対して実行される操作を特定する文字列。
*
* @return 選択された添付ファイルの添付ファイル プロキシ。
* ユーザーが選択を行わないことを選択した場合は NULL です。
*/
static CqAttachment
selectAttachment(Component frame,
StpProperty.List<CqFieldValue<?>> fields,
int row,
String op)
{
CqFieldValue field = fields.get(row);
try {
CqAttachmentFolder folder = (CqAttachmentFolder)field.getValue();
ResourceList<CqAttachment> attachments = setUserFriendlyLocation
(folder.doReadProperties(ATTACHMENT_PROPERTIES)
.getProperty(CqAttachmentFolder.ATTACHMENT_LIST));
if (attachments.size() > 0) {
CqAttachment attachment =
(CqAttachment) JOptionPane
.showInputDialog(frame,
"Choose an Attachment to " + op,
op + " Attachment",
JOptionPane.INFORMATION_MESSAGE,
null,
attachments.toArray(),
attachments.get(0));
return attachment;
}
} catch(Throwable t) { Utilities.exception(frame, op + " Attachment", t);}
return null;
}
/**
* {@link #selectAttachment} によって生成された添付ファイル選択リスト内に表示される
* 添付ファイル プロパティ。
*/
final static PropertyRequest ATTACHMENT_PROPERTIES =
new PropertyRequest(CqAttachmentFolder.ATTACHMENT_LIST
.nest(CqAttachment.DISPLAY_NAME,
CqAttachment.FILE_NAME,
CqAttachment.FILE_SIZE,
CqAttachment.DESCRIPTION,
CqAttachment.USER_FRIENDLY_LOCATION));
/**
* ViewRecord の例のメインプログラム。プロバイダをインスタンス化して、
* ExecuteQuery サンプルを呼び出します。呼び出しの際に、ClearQuest レコードのフィールドを
* 表示するビューアのバージョンを ExecuteQuery に渡します。
* @param args 使用されません。
*/
public static void main(String[] args)
{
try {
CqProvider provider = Utilities.getProvider().cqProvider();
ExecuteQuery.run("View Record", provider, new Viewer(provider));
} catch(Throwable ex) {
Utilities.exception(null, "View Record", ex);
System.exit(0);
}
}
/**
* レコード フィールド表示のための JFrame の拡張機能。
* クライアントに、フレームの JTable コンポーネントと、テーブルに表示
* されているフィールド リストを公開します。
*/
static class RecordFrame extends JFrame
{
RecordFrame(String title,
JTable table,
StpProperty.List fields)
{
super(title);
m_table = table;
m_fields = fields;
}
JTable m_table;
StpProperty.List m_fields;
private static final long serialVersionUID = 1L;
}
/** 表示される次のウィンドウの X のオフセット */
private static int g_windowX = 200;
/** 表示される次のウィンドウの Y のオフセット */
private static int g_windowY = 200;
この例の中で、ViewRecord.Viewer は、クエリーによって戻されたレコードの表示のみでなく、レコード内の選択されたフィールドが参照するレコードの表示、およびレコードのフィールドに添付されたファイルの表示にも使用されています。ユーザーは、この機能を使用して 1 つのレコードから別のレコードへと参照をブラウズすることができます。
ViewRecord.view(CqRecord) は、データベース内のレコードのすべてのフィールドと、ビューアによって使用される各フィールドのメタ プロパティを読み取って、取り込まれたプロキシを showRecord メソッドに渡します。ビューアは、ClearQuestR レコードの ALL_FIELD_VALUES プロパティを使用して、レコード内のすべてのフィールドのリストを取得します。各フィールドについて、NAME、REQUIREDNESS、TYPE、VALUE メタ プロパティが要求されます。これらのメタ プロパティ要求は、そのメタプロパティが、ALL_FIELD_VALUES プロパティ自体に対してではなく、ALL_FIELD_VALUES プロパティの値に対して取得されるようにするために、別の VALUE メタ プロパティ要求の下にネストされることに気をつけてください。(RECORD_PROPERTIES、VALUE_PROPERTIES、fieldMetaProperties の宣言を参照してください。)
フィールドが添付ファイル フィールドである場合は、その値の ATTACHMENT_LIST プロパティも要求されます。 (フィールドが添付ファイル フィールドではない場合、このプロパティ要求は失敗しますが、このプロパティはフィールドが添付である場合のみアクセスされるので、この失敗が発生しても例外は出されません。)
テーブルの行と列の内容の みが異なる、ExecuteQuery.showResults と同じ Swing GUI コンポーネントおよび 構造を使用します。この場合、それぞれの行はレコードの 1 つのフィールドであり、CqFieldValue オブジェクトとして表わされます。 それぞれの列は、フィールドのメタ プロパティです。汎用 StpProperty.getMetaProperty インターフェイスは、各フィールドの CqFieldValue/StpProperty 構造からそれぞれのメタ プロパティ値を取り出すために使用されます。 2 つの例外がありますが、レコード ビュー内のメタ プロパティのイメージの生成は、それぞれのメタ プロパティ値の toString() メソッドによって行われます。レコード リソースでは、出力を見易くするために USER_FRIENDLY_LOCATION プロパティの名前フィールドのみが表示されます。 添付ファイル フィールドでは、添付ファイルの数のみが表示され、各添付ファイル名は表示されません。
フィールド タイプ RESOURCE_LIST、JOURNAL、STRING_LIST、STRING の表示についても特に注意が必要です。これについては、読者の練習課題として残します。
添付ファイル フィールドが選択され、[ビュー] ボタンがクリックされると、selectAttachment が呼び出され、その結果が ViewRecord.view(CqAttachment) に渡されます。 selectAttachment メソッドは、再度 JOptionPane.showInputDialog を使用して、選択されたフィールドに関連付けられた添付ファイルのリストをユーザーに示します。 添付ファイル フィールドの値は添付フォルダ リソースです。フィールドに関連付けられた添付ファイルは、その添付フォルダの結合メンバです。
ViewRecord.view(CqAttachment) は、CqAttachment.doReadContent を使用して、添付ファイルをデータベース ファイルから一時ファイルに読み込み、次に、ユーティリティ メソッド (すべて Swing コード) を呼び出してファイルをユーザーに表示します。
また、ViewRecord.Viewer は、別のウィンドウでの RESOURCE_LIST 値の表示もサポートします。しかし、これについても読者の練習課題として残します。