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 * © 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 }