< Anterior | Próximo >

Visualizando um registro

Nesta lição, você aprende como visualizar todos os campos de um registro retornados por uma consulta usando a interface PropertyRequest.
O exemplo de código estende o exemplo do módulo anterior (para executar uma consulta) incluindo a capacidade de visualizar todos os campos de um registro retornados por uma consulta, não apenas aqueles dos campos de exibição da consulta.

ExecuteQuery.run é invocado para que o usuário possa selecionar e executar uma consulta. Neste exemplo, o método ExecuteQuerty.run é fornecido com uma instância real do ExeuteQuerty.Viewer chamado ViewRecord.Viewer. Isso faz com que um botão Abrir apareça na exibição do conjunto de resultados, o qual, quando clicado, invoca o método Viewer.view. É passado um proxy para o método Viewer.view (definido abaixo) para o recurso associado com a linha selecionada do conjunto de resultados. O proxy é obtido usando o método CqRowData.getRecord().

A substância deste exemplo é, portanto, incorporada no 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;
    }
    
    /**
     * Exibe o conteúdo de um recurso de Anexo em uma janela de texto.
     * 
     * @param attachment Um proxy de anexo para o anexo a ser
     *            exibido.
     */
    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);
        }
    }
    
    /**
     * Exibe a propriedade ALL_FIELD_VALUES de um recurso de registro em uma
     * tabela. As colunas da tabela são determinadas pelo conteúdo da matriz
     * {@link #fieldMetaProperties}. A exibição da maioria dos objetos é
     * implementado pelo próprio método do objeto toString().
     * 
     * @param title A cadeia de título para a janela que contém a tabela
     * @param record O proxy Recurso para o registro a ser exibido. Deve
     *            definir a propriedade ALL_FIELD_VALUES e os FieldValues
     *            nessa propriedade deve definir as meta-propriedades listadas na
     *            matriz {@link #fieldMetaProperties}.
     * @param future Componentes adicionais da janela a ser exibida junto com a
     *            tabela de propriedade. (Usada pelas extensões deste exemplo).
     * @return Uma estrutura RecordFrame que contém os componentes da GUI criados
     *         por este método.
     * @throws WvcmException
     */
    RecordFrame showRecord(String title, 
                           CqRecord record, 
                           JComponent[] future) throws WvcmException 
    {
        final StpProperty.List<CqFieldValue<?>> fields = 
            record.getAllFieldValues();
        
        // Definir um modelo de tabela no qual cada linha seja uma propriedade do
        // recurso de registro e cada coluna seja uma meta-propriedade da
        // propriedade, como seu nome, tipo e valor;
        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(); }
        };
        
        // Definir o layout de exibição
        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);

        // Adicionar um botão para visualizar um registro selecionado ou campo de anexo
        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));
                        }
                    }
                }
            });
        
        // Adicionar mais botões (usado pelos exemplos anteriores) 
        if (future != null)
            for(int i = 0; i < future.length; ++i) buttons.add(future[i]);

        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        // Pedir para ser notificado das mudanças de seleção e ativar o botão de visualização
        // somente se um campo de valor de registro ou campo de anexo for selecionado
        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;
}

/** 
 * As propriedades a serem solicitadas de cada valor de campo do registro, incluindo as
 * informações adicionais específicas para os anexos 
 */
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));

/** As meta-propriedades do campo a serem solicitadas e exibidas */
static final NestedPropertyName[] fieldMetaProperties = 
    new PropertyRequest(CqFieldValue.NAME, 
                        CqFieldValue.REQUIREDNESS, 
                        CqFieldValue.TYPE,
                        CqFieldValue.VALUE.nest(VALUE_PROPERTIES)).toArray();
              
/** 
 * O PropertyRequest a usar ao os dados de leitura de um registro a serem
 * exibidos por este visualizador. Observe que o nível de caminhos indiretos usados para solicitar
 * as meta-propriedades dos campos da lista ALL_FIELD_VALUES ao invés das
 * meta-propriedades da propriedade ALL_FIELD_VALUES em si.
 */
final static PropertyRequest RECORD_PROPERTIES =
    new PropertyRequest(CqRecord.USER_FRIENDLY_LOCATION,
                        CqRecord.ALL_FIELD_VALUES
                        .nest(StpProperty.VALUE.nest(fieldMetaProperties)));

/**
 * Examina o valor da propriedade de um campo e, se ele fizer referência a um registro,
 * retorna um proxy para o registro para o registro referido. Caso contrário, ele retorna nulo.
 * @param fields O Property.List a examinar.
 * @param row O índice do elemento na lista a examinar.
 * @return Um proxy de Registro se o campo fizer referência a um registro; caso contrário, nulo
 */
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;
}

/**
 * Se o campo indicado está ou não em um campo de anexo.
 * @param fields O Property.List a examinar.
 * @param row O índice do elemento na lista a examinar.
 * @return true iff o campo no índice dado é um campo de anexo
 */
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;
}

/**
 * Apresenta ao usuário uma lista dos anexos associados com um
 * campo especificado de um registro e permite que o usuário selecione um.
 * 
 * @param frame O quadro pai para o diálogo gerado por este método.
 * @param fields O Property.List a examinar.
 * @param row O índice do elemento na lista a examinar.
 * @param op Uma cadeia identificando a operação que será executada no
 *            anexo selecionado.
 * @return Um proxy de Anexo para o anexo selecionado; nulo se o usuário
 *         escolher não fazer nenhuma seleção.
 */
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;
}

/**
 * As propriedades do anexo a serem exibidas na lista de seleção do anexo
 * gerado por {@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));

/**
 * O principal programa para o exemplo ViewRecord. Instancia um Provedor e,
 * em seguida, invoca o exemplo ExecuteQuery, inserindo uma versão do Visualizador 
 * que exibe campos de um registro ClearQuest.
 * @param args não usado.
 */
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);
    }
}

/**
 * Uma extensão do JFrame para a exibição do campo de registro, 
 * expondo aos clientes o componente JTable do quadro e
 * a lista do campo que está sendo exibido na tabela.
 */
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 deslocamento para a próxima janela a ser exibida */
private static int g_windowX = 200;

/** Y deslocamento para a próxima janela a ser exibida */
private static int g_windowY = 200;

Neste exemplo, o ViewRecord.Viewer é usado não apenas para exibir um registro retornado por uma consulta, mas também para visualizar um registro referido por um campo selecionado de um registro e para visualizar um arquivo anexado a um campo de um registro. O usuário pode usar este recurso para navegar através das referências de um registro para outro.

ViewRecord.view(CqRecord) lê todos os campos do registro no banco de dados e as meta-propriedades de cada campo usado pelo visualizador e passa o proxy preenchido para o método showRecord. O visualizador usa a propriedade ALL_FIELD_VALUES de um registro ClearQuest para obter uma lista de todos os campos do registro. Para cada campo, as meta-propriedades NAME, REQUIREDNESS, TYPE e VALUE são necessárias. Observe que estes pedidos de meta-propriedade são aninhados em outro pedido de meta-propriedade VALUE para que estas meta-propriedades sejam obtidas para o valor da propriedade ALL_FIELD_VALUES e não para a propriedade ALL_FIELD_VALUES em si. (Consulte a declaração de RECORD_PROPERTIES, VALUE_PROPERTIES e fieldMetaProperties.)

No caso de o campo ser um campo de anexo, a propriedade ATTACHMENT_LIST do valor também é solicitada. (Se o campo não for um campo de anexo, este pedido da propriedade irá falhar, mas como esta propriedade é acessada apenas se o campo for um anexo, esta falha irá resultar em uma exceção).

ViewRecord.showRecord usa os mesmos componentes e estrutura da GUI Swing como ExecuteQuery.showResults, apenas o conteúdo das linhas e colunas da tabela diferem. Neste caso, cada linha é um campo do registro, que é expresso como um objeto CqFieldValue. Cada coluna é uma meta-propriedade do campo. A interface genérica StpProperty.getMetaProperty é usada para buscar cada valor de meta-propriedade da estrutura CqFieldValue/StpProperty para cada campo. Com duas exceções, conta-se com o método toString() para cada valor de meta-propriedade para gerar uma imagem da meta-propriedade na visualização do registro. Para recursos do registro, apenas o campo de nome da propriedade USER_FRIENDLY_LOCATION é exibida para reduzir a confusão na saída. Para campos de anexo, apenas o número de anexos é exibido, não cada nome do anexo.

Também pode-se dar atenção especial à exibição de tipos de campo RESOURCE_LIST, JOURNAL, STRING_LIST e STRING. Isso é deixado como um exercício para o leitor.

Quando um campo de anexo é selecionado e o botão visualizar é clicado, selectAttachment é chamado e seu resultado é passado para o ViewRecord.view(CqAttachment). O método selectAttachment usa JOptionPane.showInputDialog novamente para apresentar ao usuário uma lista dos anexos associados com o campo selecionado. O valor de um campo de anexo é um recurso da pasta de anexo. Os anexos associados com o campo são membros do limite dessa pasta de anexo.

ViewRecord.view(CqAttachment) usa o CqAttachment.doReadContent para ler o arquivo anexado do banco de dados para um arquivo temporário e, em seguida, chama um método de utilitário (todo código Swing) para exibir o arquivo para o usuário.

O ViewRecord.Viewer também deve suportar a exibição de valores RESOURCE_LIST em uma janela separada. Mas isto, também, foi deixado como um exercício para o leitor.

É possível usar o ClearQuest CM API para gravar aplicativos clientes ou utilitários para visualizar registros, bem como seus campos e valores do campo.

Ponto de verificação da lição

O ClearQuest CM API permite executar muitas operações específicas para o Rational ClearQuest, como a execução de uma consulta, a iteração de um conjunto de resultados e a visualização de registros e seus campos. Agora que você aprendeu como usar o ClearQuest CM API para visualizar um registro, a próxima etapa é aprender como modificar um registro.
Nesta lição, você aprendeu o seguinte:
  • Sobre várias interfaces do ClearQuest CM API que são específicas para os recursos do Rational ClearQuest.
  • Como gravar o código do aplicativo cliente do ClearQuest CM API para recuperar registros, solicitar propriedades e visualizar valores do campo.
< Anterior | Próximo >

Feedback