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    }