Viewing a record
About this task
ExecuteQuery.run is invoked so that the user can select and execute a query. In this example, the ExecuteQuerty.run method is provided with a real instance of ExeuteQuerty.Viewer called ViewRecord.Viewer. This causes an Open button to appear in the result set display, which when clicked invokes the Viewer.view method. The Viewer.view method (defined below) is passed a proxy for the resource associated with the selected row of the result set. The proxy is obtained using the CqRowData.getRecord() method.
The substance of this example is, therefore, embodied in the Viewer.view method:
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;
In this example, the ViewRecord.Viewer is used not only to display a record returned by a query, but also to view a record referenced by a selected field of a record and to view a file attached to a field of a record. The user may use this feature to browse through references from one record to another.
ViewRecord.view(CqRecord) reads all fields from the record in the database and the meta-properties of each field used by the viewer and passes the populated proxy to the showRecord method. The viewer uses the ALL_FIELD_VALUES property of a Rational® ClearQuest® record to get a list of all the fields in the record. For each field, the NAME, REQUIREDNESS, TYPE and VALUE meta-properties are requested. Note that these meta-property requests are nested under another VALUE meta-property request so that these meta-properties are obtained for the value of the ALL_FIELD_VALUES property and not for the ALL_FIELD_VALUES property itself. (See the declaration of RECORD_PROPERTIES, VALUE_PROPERTIES, and fieldMetaProperties.)
In case the field is an attachment field, the ATTACHMENT_LIST property of the value is also requested. (If the field is not an attachment field, this property request will fail, but since this property is accessed only if the field is an attachment, this failure will not result in an exception.)
ViewRecord.showRecord uses the same Swing GUI components and structure as ExecuteQuery.showResults only the content of the rows and columns of the table differ. In this case, each row is a field of the record, which is expressed as a CqFieldValue object. Each column is a meta-property of the field. The generic StpProperty.getMetaProperty interface is used to fetch each meta-property value from the CqFieldValue/StpProperty structure for each field. With two exceptions, the toString() method for each meta-property value is relied upon for generating an image of the meta-property in the record view. For record resources, just the name field of the USER_FRIENDLY_LOCATION property is displayed to reduce the clutter in the output. For attachment fields, only the number of attachments is displayed, not each attachment name.
The display of field types RESOURCE_LIST, JOURNAL, STRING_LIST, and STRING might also be given special attention. This is left as an exercise for the reader.
When an attachment field is selected and the view button is clicked, selectAttachment is called and its result is passed to ViewRecord.view(CqAttachment). The selectAttachment method uses JOptionPane.showInputDialog again to present to the user a list of the attachments associated with the selected field. The value of an attachment field is an attachment folder resource. The attachments associated with the field are bound members of that attachment folder.
ViewRecord.view(CqAttachment) uses CqAttachment.doReadContent to read the attached file from the database into a temporary file and then calls a utility method (all Swing code) to display the file to the user.
The ViewRecord.Viewer should also support the display of RESOURCE_LIST values in a separate window. But this, too, has been left as an exercise for the reader.
Results
Lesson checkpoint
- About several Rational ClearQuest CM API interfaces that are specific to Rational ClearQuest resources.
- How to write Rational ClearQuest CM API client application code for retrieving records, requesting properties, and viewing field values.