001 /*
002 * file ViewRecord.java
003 *
004 * Licensed Materials - Property of IBM
005 * Restricted Materials of IBM - you are allowed to copy, modify and
006 * redistribute this file as part of any program that interfaces with
007 * IBM Rational CM API.
008 *
009 * com.ibm.rational.stp.client.samples.ViewRecord
010 *
011 * (C) Copyright IBM Corporation 2005, 2008. All Rights Reserved.
012 * Note to U.S. Government Users Restricted Rights: Use, duplication or
013 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
014 */
015
016 package com.ibm.rational.stp.client.samples;
017
018 import static com.ibm.rational.stp.client.samples.ExecuteQuery.setUserFriendlyLocation;
019 import java.awt.BorderLayout;
020 import java.awt.Component;
021 import java.awt.FlowLayout;
022 import java.awt.event.ActionEvent;
023 import java.awt.event.ActionListener;
024 import java.io.File;
025
026 import javax.swing.JButton;
027 import javax.swing.JComponent;
028 import javax.swing.JFrame;
029 import javax.swing.JOptionPane;
030 import javax.swing.JPanel;
031 import javax.swing.JScrollPane;
032 import javax.swing.JTable;
033 import javax.swing.ListSelectionModel;
034 import javax.swing.event.ListSelectionEvent;
035 import javax.swing.event.ListSelectionListener;
036 import javax.swing.table.AbstractTableModel;
037 import javax.swing.table.TableModel;
038 import javax.wvcm.ResourceList;
039 import javax.wvcm.WvcmException;
040 import javax.wvcm.PropertyRequestItem.NestedPropertyName;
041 import javax.wvcm.PropertyRequestItem.PropertyRequest;
042
043 import com.ibm.rational.wvcm.stp.StpException;
044 import com.ibm.rational.wvcm.stp.StpProperty;
045 import com.ibm.rational.wvcm.stp.StpResource;
046 import com.ibm.rational.wvcm.stp.StpProperty.MetaPropertyName;
047 import com.ibm.rational.wvcm.stp.cq.CqAttachment;
048 import com.ibm.rational.wvcm.stp.cq.CqAttachmentFolder;
049 import com.ibm.rational.wvcm.stp.cq.CqFieldValue;
050 import com.ibm.rational.wvcm.stp.cq.CqProvider;
051 import com.ibm.rational.wvcm.stp.cq.CqRecord;
052 import com.ibm.rational.wvcm.stp.cq.CqFieldValue.ValueType;
053
054 /**
055 * View the current state of a record
056 */
057 public class ViewRecord {
058 /**
059 * An instance of ExecuteQuery.Viewer that allows (read-only) viewing of a
060 * Record resource.
061 */
062 static class Viewer implements ExecuteQuery.Viewer {
063 Viewer(CqProvider provider) { m_provider = provider; }
064
065 /**
066 * @see com.ibm.rational.stp.client.samples.ExecuteQuery.Viewer#view(com.ibm.rational.wvcm.stp.cq.CqRecord)
067 */
068 public JFrame view(CqRecord record)
069 {
070 if (record != null) try {
071 record = (CqRecord)record.doReadProperties(RECORD_PROPERTIES);
072 return showRecord("View: ", record, null);
073 } catch (WvcmException ex){
074 ex.printStackTrace();
075 }
076
077 return null;
078 }
079
080 /**
081 * Displays the content of an Attachment resource in a text window.
082 *
083 * @param attachment An Attachment proxy for the attachment to be
084 * displayed.
085 */
086 public void view(CqAttachment attachment)
087 {
088 if (attachment != null) try{
089 File file = File.createTempFile("attach", "tmp");
090
091 attachment.doReadContent(file.getAbsolutePath(), null);
092 BrowserDataModel.showFile(attachment.getDisplayName(), file);
093 } catch(Throwable ex) {
094 Utilities.exception(null, "View Attachment", ex);
095 }
096 }
097
098 /**
099 * Displays the ALL_FIELD_VALUES property of a record resource in a
100 * table. The columns of the table are determined by the content of the
101 * {@link #fieldMetaProperties} array. The display of most objects is
102 * implemented by the object's own toString() method.
103 *
104 * @param title The title string for the window that contains the table
105 * @param record The Record proxy for the record to be displayed. Must
106 * define the ALL_FIELD_VALUES property and the FieldValues
107 * in that property must define the meta-properties listed in
108 * the {@link #fieldMetaProperties} array.
109 * @param future Additional window components to be displayed along with
110 * the property table. (Used by extensions to this example).
111 * @return A RecordFrame structure containing the GUI components created
112 * by this method.
113 * @throws WvcmException
114 */
115 RecordFrame showRecord(String title,
116 CqRecord record,
117 JComponent[] future) throws WvcmException
118 {
119 final StpProperty.List<CqFieldValue<?>> fields =
120 record.getAllFieldValues();
121
122 // Define a table model in which each row is a property of the
123 // record resource and each column is a meta-property of the
124 // property, such as its name, type, and value;
125 TableModel dataModel = new AbstractTableModel() {
126 private static final long serialVersionUID = 1L;
127 public int getColumnCount() { return fieldMetaProperties.length; }
128 public int getRowCount() { return fields.size();}
129 public Object getValueAt(int row, int col)
130 {
131 try {
132 Object val = fields.get(row)
133 .getMetaProperty((MetaPropertyName<?>)
134 fieldMetaProperties[col].getRoot());
135
136 if (val instanceof CqRecord)
137 return ((CqRecord)val).getUserFriendlyLocation()
138 .getName();
139 else if (val instanceof CqAttachmentFolder)
140 return ((CqAttachmentFolder)val)
141 .getAttachmentList().size()
142 + " attachments";
143 else
144 return val;
145
146 } catch(Throwable ex) {
147 if (ex instanceof StpException) {
148 return ((StpException)ex).getStpReasonCode();
149 } else {
150 String name = ex.getClass().getName();
151 return name.substring(name.lastIndexOf(".")+1);
152 }
153 }
154 }
155 public String getColumnName(int col)
156 { return fieldMetaProperties[col].getRoot().getName(); }
157 };
158
159 // Define the display layout
160 final JTable table = new JTable(dataModel);
161 final JPanel panel = new JPanel(new BorderLayout());
162 final JPanel buttons = new JPanel(new FlowLayout());
163 final JButton button = new JButton("View");
164 final RecordFrame frame =
165 new RecordFrame(title + record.getUserFriendlyLocation().toString(),
166 table, fields);
167
168 // Add a button for viewing a selected record or attachment field
169 buttons.add(button, BorderLayout.SOUTH);
170 button.setEnabled(false);
171 button.addActionListener(new ActionListener(){
172 public void actionPerformed(ActionEvent arg0)
173 {
174 int[] selected = table.getSelectedRows();
175
176 for (int i =0; i < selected.length; ++i) {
177 int row = selected[i];
178 if (isAttachmentList(fields, row)) {
179 view(selectAttachment(frame, fields, row, "View"));
180 } else {
181 view(getRecordReferencedAt(fields, row));
182 }
183 }
184 }
185 });
186
187 // Add more buttons (used by later examples)
188 if (future != null)
189 for(int i = 0; i < future.length; ++i) buttons.add(future[i]);
190
191 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
192
193 // Ask to be notified of selection changes and enable the view button
194 // only if a record-valued field or attachment field is selected
195 ListSelectionModel rowSM = table.getSelectionModel();
196 rowSM.addListSelectionListener(new ListSelectionListener() {
197 public void valueChanged(ListSelectionEvent e) {
198 if (!e.getValueIsAdjusting()){
199 int[] selected = table.getSelectedRows();
200 button.setEnabled(false);
201
202 for (int i=0; i <selected.length; ++i)
203 if (getRecordReferencedAt(fields, selected[i]) != null
204 || isAttachmentList(fields, selected[i])) {
205 button.setEnabled(true);
206 break;
207 }
208 }
209 }
210 });
211
212 panel.add(new JScrollPane(table), BorderLayout.CENTER);
213 panel.add(buttons, BorderLayout.SOUTH);
214 frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
215 frame.setContentPane(panel);
216 frame.setBounds(g_windowX += 10, g_windowY += 10, 600, 300);
217 frame.setVisible(true);
218
219 return frame;
220 }
221
222 protected CqProvider m_provider;
223 }
224
225 /**
226 * Properties to be requested from each record field valie, including
227 * specific additional information for attachments
228 */
229 static final PropertyRequest VALUE_PROPERTIES =
230 new PropertyRequest(StpResource.USER_FRIENDLY_LOCATION,
231 CqAttachmentFolder.ATTACHMENT_LIST
232 .nest(CqAttachment.DISPLAY_NAME,
233 CqAttachment.FILE_NAME,
234 CqAttachment.FILE_SIZE,
235 CqAttachment.DESCRIPTION));
236
237 /** The field meta-properties to be requested and displayed */
238 static final NestedPropertyName[] fieldMetaProperties =
239 new PropertyRequest(CqFieldValue.NAME,
240 CqFieldValue.REQUIREDNESS,
241 CqFieldValue.TYPE,
242 CqFieldValue.VALUE.nest(VALUE_PROPERTIES)).toArray();
243
244 /**
245 * The PropertyRequest to use when reading data from a record to be
246 * displayed by this viewer. Note the level of indirection used to request
247 * the meta-properties of the fields in the ALL_FIELD_VALUES list rather
248 * than those meta-properties of the ALL_FIELD_VALUES property itself.
249 */
250 final static PropertyRequest RECORD_PROPERTIES =
251 new PropertyRequest(CqRecord.USER_FRIENDLY_LOCATION,
252 CqRecord.ALL_FIELD_VALUES
253 .nest(StpProperty.VALUE.nest(fieldMetaProperties)));
254
255 /**
256 * Examines the property value of a field and, if it references a record,
257 * returns a proxy for the referenced record. Otherwise it returns null.
258 * @param fields The Property.List to examine.
259 * @param row The index of the element in the list to examine.
260 * @return A Record proxy if the field references a record; null otherwise
261 */
262 static CqRecord getRecordReferencedAt(StpProperty.List<CqFieldValue<?>> fields,
263 int row)
264 {
265 try {
266 CqFieldValue field = fields.get(row);
267
268 if (field.getFieldType() == ValueType.RESOURCE
269 && field.getValue() instanceof CqRecord)
270 return (CqRecord)field.getValue();
271 } catch (WvcmException ex) { ex.printStackTrace(); }
272
273 return null;
274 }
275
276 /**
277 * Whether or not the indicated field is an attachment field.
278 * @param fields The Property.List to examine.
279 * @param row The index of the element in the list to examine.
280 * @return true iff the field at the given index is an attachment field
281 */
282 static boolean isAttachmentList(StpProperty.List<CqFieldValue<?>> fields,
283 int row)
284
285 {
286 if (row >= 0) try {
287 CqFieldValue field = fields.get(row);
288
289 return field.getFieldType() == ValueType.ATTACHMENT_LIST;
290 } catch (WvcmException ex) { ex.printStackTrace(); }
291
292 return false;
293 }
294
295 /**
296 * Presents to the user a list of the attachments associated with a
297 * specified field of a record and allows the user to select one.
298 *
299 * @param frame The parent frame for the dialog generated by this method.
300 * @param fields The Property.List to examine.
301 * @param row The index of the element in the list to examine.
302 * @param op A string identifying the operation that will be performed on
303 * the selected attachment.
304 * @return An Attachment proxy for the selected attachment; null if the user
305 * chooses to make no selection.
306 */
307 static CqAttachment
308 selectAttachment(Component frame,
309 StpProperty.List<CqFieldValue<?>> fields,
310 int row,
311 String op)
312 {
313 CqFieldValue field = fields.get(row);
314
315 try {
316 CqAttachmentFolder folder = (CqAttachmentFolder)field.getValue();
317 ResourceList<CqAttachment> attachments = setUserFriendlyLocation
318 (folder.doReadProperties(ATTACHMENT_PROPERTIES)
319 .getProperty(CqAttachmentFolder.ATTACHMENT_LIST));
320
321 if (attachments.size() > 0) {
322 CqAttachment attachment =
323 (CqAttachment) JOptionPane
324 .showInputDialog(frame,
325 "Choose an Attachment to " + op,
326 op + " Attachment",
327 JOptionPane.INFORMATION_MESSAGE,
328 null,
329 attachments.toArray(),
330 attachments.get(0));
331
332 return attachment;
333 }
334 } catch(Throwable t) { Utilities.exception(frame, op + " Attachment", t);}
335
336 return null;
337 }
338
339 /**
340 * The attachment properties to be displayed in the attachment selection
341 * list generated by {@link #selectAttachment}.
342 */
343 final static PropertyRequest ATTACHMENT_PROPERTIES =
344 new PropertyRequest(CqAttachmentFolder.ATTACHMENT_LIST
345 .nest(CqAttachment.DISPLAY_NAME,
346 CqAttachment.FILE_NAME,
347 CqAttachment.FILE_SIZE,
348 CqAttachment.DESCRIPTION,
349 CqAttachment.USER_FRIENDLY_LOCATION));
350
351 /**
352 * The main program for the ViewRecord example. Instantiates a Provider and
353 * then invokes the ExecuteQuery example, passing in a version of Viewer
354 * that displays fields of a ClearQuest record.
355 * @param args not used.
356 */
357 public static void main(String[] args)
358 {
359 try {
360 CqProvider provider = Utilities.getProvider().cqProvider();
361 ExecuteQuery.run("View Record", provider, new Viewer(provider));
362 } catch(Throwable ex) {
363 Utilities.exception(null, "View Record", ex);
364 System.exit(0);
365 }
366 }
367
368 /**
369 * An extension of JFrame for the record field display,
370 * exposing to clients the JTable component of the frame and
371 * the field list that is being displayed in the table.
372 */
373 static class RecordFrame extends JFrame
374 {
375 RecordFrame(String title,
376 JTable table,
377 StpProperty.List fields)
378 {
379 super(title);
380
381 m_table = table;
382 m_fields = fields;
383 }
384
385 JTable m_table;
386 StpProperty.List<CqFieldValue<?>> m_fields;
387 private static final long serialVersionUID = 1L;
388 }
389
390 /** X offset for the next window to be displayed */
391 private static int g_windowX = 200;
392
393 /** Y offset for the next window to be displayed */
394 private static int g_windowY = 200;
395 }