< Anterior | Siguiente >

Visualización de un registro

En esta lección aprenderá a ver todos los campos de un registro que una consulta devuelve utilizando la interfaz PropertyRequest.
En el ejemplo de código se amplia el ejemplo del módulo anterior (para ejecutar una consulta) añadiendo la posibilidad de ver todos los campos de un registro devueltos por una consulta y no tan sólo los que figuran en los campos de visualización de la consulta.

Se invoca ExecuteQuery.run para que el usuario pueda seleccionar y ejecutar una consulta. En este ejemplo, el método ExecuteQuerty.run se proporciona con una instancia real de ExeuteQuerty.Viewer denominada ViewRecord.Viewer. Esto hace que aparezca un botón Abrir en la pantalla de la consulta de resultados, que cuando se pulsa, invoca el método Viewer.view. Al método Viewer.view (definido a continuación) se le pasa un proxy para el recurso asociado con la fila seleccionada del conjunto de resultados. El proxy se obtiene utilizando el método CqRowData.getRecord().

La esencia de este ejemplo, se incorpora por consiguiente en el método 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;

En este ejemplo, ViewRecord.Viewer no sólo se utiliza para visualizar un registro devuelto por una consulta, sino también para ver un registro referenciado por un campo seleccionado de una consulta y ver un archivo adjunto a un campo de un registro. El usuario puede utilizar esta función para recorrer las referencias e ir de registro a otro.

ViewRecord.view(CqRecord) lee todos los campos del registro en la base de datos y las metapropiedades de cada campo empleadas por el visor y transfiere el proxy llenado al método showRecord. El visor utiliza la propiedad ALL_FIELD_VALUES de un registro ClearQuest para listar todos los campos del registro. Para cada campo, se solicitan las metapropiedades NAME, REQUIREDNESS, TYPE y VALUE. Recuerde que estas solicitudes de metapropiedades se anidan debajo de otra solicitud de metapropiedad VALUE de modo que estas metapropiedades se obtengan para el valor de la propiedad ALL_FIELD_VALUES y no para la propiedad ALL_FIELD_VALUES en sí. (Véase la declaración de RECORD_PROPERTIES, VALUE_PROPERTIES y fieldMetaProperties.)

En caso de que el campo sea un campo de archivos adjuntos, también se solicita la propiedad ATTACHMENT_LIST del valor. (Si el campo no es un campo de archivos adjuntos, esta solicitud de propiedad no se realizará correctamente, ya que como sólo se accede a esta propiedad si el campo es un archivo adjunto, este error no generará una excepción.)

ViewRecord.showRecord utiliza los mismos componentes y estructura de la GUI de Swing que ExecuteQuery.showResults; sólo cambia el contenido de las filas y las columnas de la tabla. En este caso, cada fila es un campo del registro, que se expresa como un objeto CqFieldValue. Cada columna es una metapropiedad del campo. La interfaz StpProperty.getMetaProperty genérica se utiliza para captar cada valor de metapropiedad de la estructura CqFieldValue/StpProperty para cada campo. Salvo dos excepciones, el método toString() de cada valor de metapropiedad está condicionado por la generación de una imagen de la metapropiedad en la vista del registro. En los recursos del registro, tan sólo se visualiza el campo de nombre de la propiedad USER_FRIENDLY_LOCATION para evitar confusiones en la salida. En los campos de archivos adjuntos, sólo se visualiza el número de archivos adjuntos, no cada nombre de archivo adjunto.

La visualización de los tipos de campos RESOURCE_LIST, JOURNAL, STRING_LIST y STRING podría ser objeto de atención especial. Este punto se deja como ejercicio para el lector.

Cuando se selecciona un campo de archivos adjuntos y se pulsa el botón ver, se invoca SelectAttachment y el resultado se transfiere a ViewRecord.view(CqAttachment). El método selectAttachment utiliza de nuevo JOptionPane.showInputDialog para mostrar al usuario una lista de los archivos adjuntos asociados al campo seleccionado. El valor de un campo de archivos adjuntos es un recurso de la carpeta de archivos adjuntos. Los archivos adjuntos asociados al campo son miembros enlazados de dicha carpeta de archivos adjuntos.

ViewRecord.view(CqAttachment) utiliza CqAttachment.doReadContent para leer el archivo adjunto de la base de datos en un archivo temporal y a continuación, invoca el método del programa de utilidad (todo el código Swing) para mostrar el archivo al usuario.

ViewRecord.Viewer también debe dar soporte a la visualización de valores RESOURCE_LIST en una ventana distinta. Pero esto también se deja como ejercicio para el lector.

Puede utilizar ClearQuest CM API para escribir aplicaciones cliente o programas de utilidad para visualizar registros así como los campos y valores de campo.

Punto de comprobación de la lección

ClearQuest CM API permite realizar muchas operaciones que son específicas de Rational ClearQuest, como por ejemplo, ejecutar una consulta, repetir un conjunto de resultados y visualizar tanto los registros como los campos. Ahora que ha aprendido a utilizar ClearQuest CM API, el siguiente paso es aprender a modificar un registro.
En esta lección, ha aprendido lo siguiente:
  • Varias interfaces de ClearQuest CM API que son específicas de los recursos de Rational ClearQuest.
  • Cómo escribir código de aplicación cliente de ClearQuest CM API para recuperar registros, solicitar propiedades y visualizar valores de campo.
< Anterior | Siguiente >

Comentarios