< 上一课 | 下一课 >

查看记录

在本课程中,您将了解如何用 PropertyRequest 接口查看由查询返回的记录的所有字段。
下面这段示例代码进一步完善了上一个模块的示例代码(运行查询代码),它添加了一个功能 - 查看由查询返回的记录的所有字段,而不仅仅只能查看查询的显示字段。

调用了 ExecuteQuery.run,这样用户可以选择和执行查询。在本示例中,ExeuteQuerty.Viewer 的实际实例(称为 ViewRecord.Viewer)提供了 ExecuteQuerty.run 方法。这会在结果集的显示中出现 Open 按钮,但单击该按钮时,会调用 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;
    }
    
    /**
     * Displays the content of an Attachment resource in a text window.
     * 
     * @param attachment An Attachment proxy for the attachment to be
     *            displayed.
     */
    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);
        }
    }
    
    /**
     * Displays the ALL_FIELD_VALUES property of a record resource in a
     * table. The columns of the table are determined by the content of the
     * {@link #fieldMetaProperties} array. The display of most objects is
     * implemented by the object's own toString() method.
     * 
     * @param title The title string for the window that contains the table
     * @param record The Record proxy for the record to be displayed. Must
     *            define the ALL_FIELD_VALUES property and the FieldValues
     *            in that property must define the meta-properties listed in
     *            the {@link #fieldMetaProperties} array.
     * @param future Additional window components to be displayed along with
     *            the property table. (Used by extensions to this example).
     * @return A RecordFrame structure containing the GUI components created
     *         by this method.
     * @throws WvcmException
     */
    RecordFrame showRecord(String title, 
                           CqRecord record, 
                           JComponent[] future) throws WvcmException 
    {
        final StpProperty.List<CqFieldValue<?>> fields = 
            record.getAllFieldValues();
        
        // Define a table model in which each row is a property of the
        // record resource and each column is a meta-property of the
        // property, such as its name, type, and value;
        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(); }
        };
        
        // Define the display layout
        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);

        // Add a button for viewing a selected record or attachment field
        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));
                        }
                    }
                }
            });
        
        // Add more buttons (used by later examples) 
        if (future != null)
            for(int i = 0; i < future.length; ++i) buttons.add(future[i]);

        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        // Ask to be notified of selection changes and enable the view button
        // only if a record-valued field or attachment field is selected
        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;
}

/** 
 * Properties to be requested from each record field value, including
 * specific additional information for attachments 
 */
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));

/** The field meta-properties to be requested and displayed */
static final NestedPropertyName[] fieldMetaProperties = 
    new PropertyRequest(CqFieldValue.NAME, 
                        CqFieldValue.REQUIREDNESS, 
                        CqFieldValue.TYPE,
                        CqFieldValue.VALUE.nest(VALUE_PROPERTIES)).toArray();
              
/** 
 * The PropertyRequest to use when reading data from a record to be
 * displayed by this viewer. Note the level of indirection used to request
 * the meta-properties of the fields in the ALL_FIELD_VALUES list rather
 * than those meta-properties of the ALL_FIELD_VALUES property itself.
 */
final static PropertyRequest RECORD_PROPERTIES =
    new PropertyRequest(CqRecord.USER_FRIENDLY_LOCATION,
                        CqRecord.ALL_FIELD_VALUES
                        .nest(StpProperty.VALUE.nest(fieldMetaProperties)));

/**
 * Examines the property value of a field and, if it references a record,
 * returns a proxy for the referenced record. Otherwise it returns null.
 * @param fields The Property.List to examine.
 * @param row The index of the element in the list to examine.
 * @return A Record proxy if the field references a record; null otherwise
 */
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;
}

/**
 * Whether or not the indicated field is an attachment field.
 * @param fields The Property.List to examine.
 * @param row The index of the element in the list to examine.
 * @return true iff the field at the given index is an attachment field
 */
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;
}

/**
 * Presents to the user a list of the attachments associated with a
 * specified field of a record and allows the user to select one.
 * 
 * @param frame The parent frame for the dialog generated by this method.
 * @param fields The Property.List to examine.
 * @param row The index of the element in the list to examine.
 * @param op A string identifying the operation that will be performed on
 *            the selected attachment.
 * @return An Attachment proxy for the selected attachment; null if the user
 *         chooses to make no selection.
 */
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;
}

/**
 * The attachment properties to be displayed in the attachment selection
 * list generated by {@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));

/**
 * The main program for the ViewRecord example. Instantiates a Provider and
 * then invokes the ExecuteQuery example, passing in a version of Viewer 
 * that displays fields of a ClearQuest record.
 * @param args not used.
 */
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);
    }
}

/**
 * An extension of JFrame for the record field display, 
 * exposing to clients the JTable component of the frame and
 * the field list that is being displayed in the table.
 */
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 offset for the next window to be displayed */
private static int g_windowX = 200;

/** Y offset for the next window to be displayed */
private static int g_windowY = 200;

在本示例中,ViewRecord.Viewer 不仅用于显示返回的查询记录,也用于查看由某条记录的选定字段所引用的记录以及查看附加到记录字段上的文件。用户可以用这个功能浏览各记录之间的引用。

ViewRecord.view(CqRecord) 读取数据库中记录的所有字段以及 Viewer 所用的每个字段的元属性,并将填入的代理传递给 showRecord 方法。Viewer 利用 ClearQuest® 记录的 ALL_FIELD_VALUES 属性来获取某条记录所有字段的列表。对于每个字段,会请求 NAME、REQUIREDNESS、TYPE 和 VALUE 元属性。请注意:这些元属性请求嵌套在另一个 VALUE 元属性请求中,所以获得的这些元属性是针对 ALL_FIELD_VALUES 属性值,而不是针对 ALL_FIELD_VALUES 属性本身。(请参阅 RECORD_PROPERTIES、VALUE_PROPERTIES 和 fieldMetaProperties 的声明。)

如果字段为附件字段,那么还需要请求 ATTACHMENT_LIST 属性值。 (如果字段不是附件字段,那么这个属性请求会失败,但是由于仅仅在字段为附件时才需要访问该属性,因此这个失败不会导致异常。)

ViewRecord.showRecord 采用了与 ExecuteQuery.showResults 相同的 Swing GUI 组件和结构,只是表中行和列的内容有所不同。在这种情况下,每一行都是该记录的一个字段,并用 CqFieldValue 对象来表示。 每一列是该字段的一个元属性。这里采用了通用的 StpProperty.getMetaProperty 接口来从每个字段的 CqFieldValue/StpProperty 结构取回每个元属性值。有两个例外,每个元属性值的 toString() 方法依赖于记录视图中生成的元属性映像。对于记录资源,只显示 USER_FRIENDLY_LOCATION 属性的名称字段,以减少输出上的杂乱。对于附件字段,只显示附件编号,而不显示每个附件名称。

另外,可能还要特别注意字段类型 RESOURCE_LIST、JOURNAL、STRING_LIST 和 STRING 的显示。这可以留给读者做练习。

在选中附件字段并单击 View 按钮后,会调用 selectAttachment,其结果将传递给 ViewRecord.view(CqAttachment)。 selectAttachment 方法再次使用 JOptionPane.showInputDialog,向用户呈示与选定字段相关联的附件列表。 附件字段值属于附件文件夹资源。与字段相关联的附件是该附件文件夹的绑定成员。

ViewRecord.view(CqAttachment) 使用 CqAttachment.doReadContent 将附加文件从数据库读取到临时文件,然后,调用实用程序方法(全部为 Swing 代码)来向用户显示该文件。

ViewRecord.Viewer 应该还支持在单独的窗口中显示 RESOURCE_LIST 值。不过,这也留给读者做练习。

可以使用 Rational® CM API 来编写客户机应用程序或实用程序来查看记录及其字段和字段值。

课程检查点

您可以用 Rational CM API 执行许多 Rational ClearQuest 特定的操作,如运行查询、迭代结果集和查看记录及其字段。现在,您已经了解了如何利用 Rational CM API 来查看记录,下一步将学习如何修改记录。
在本课程中,您学习到了以下内容:
  • 几个针对 Rational ClearQuest 资源的 Rational CM API 接口。
  • 如何编写 Rational CM API 客户机应用程序代码来检索记录、请求属性以及查看字段值。
< 上一课 | 下一课 >

反馈