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;
Dentro deste exemplo, o ViewRecord.Viewer é usado não apenas para exibir um registro retornado por uma consulta, mas também para visualizar um registro mencionado 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.