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.