< Précédent | Suivant >

Affichage d'un enregistrement

Dans la présente leçon, vous allez apprendre à afficher toutes les zones d'un enregistrement renvoyé par une requête à l'aide de l'interface PropertyRequest.
L'exemple de code vient développer l'exemple du modèle précédent (pour l'exécution d'une requête) en ajoutant la possibilité d'afficher toutes les zones d'un enregistrement renvoyé par une requête, et pas seulement celles des zones d'affichage de la requête.

ExecuteQuery.run est appelé de sorte que l'utilisateur puisse sélectionner et exécuter une requête. Dans cet exemple, la méthode ExecuteQuerty.run est fournie avec une instance réelle de ExeuteQuerty.Viewer, appelée ViewRecord.Viewer. Le bouton Open apparaît ainsi dans l'écran de l'ensemble de résultats. Si vous cliquez dessus, la méthode Viewer.view est appelée. La méthode Viewer.view (définie ci-dessous) est transmise via un proxy pour la ressource associée à la ligne sélectionnée de l'ensemble de résultats. Le proxy est obtenu grâce à la méthode CqRowData.getRecord().

Le contenu de cet exemple est par conséquent représenté dans la méthode 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;
    }
    
    /**
     * Affiche le contenu d'une ressource Attachment dans une fenêtre texte.
     * 
     * @param attachment : proxy Attachment pour la pièce jointe
     *            à afficher.
     */
    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);
        }
    }
    
    /**
     * Affiche la propriété ALL_FIELD_VALUES d'une ressource d'enregistrement dans une
     * table. Les colonnes de la table sont déterminées par le contenu du tableau
     * {@link #fieldMetaProperties}. L'affichage de la plupart des objets est
     * implémenté par la propre méthode toString() de l'objet.
     * 
     * @param title : chaîne de titre de la fenêtre contenant la table
     * @param record : proxy Record pour l'enregistrement à afficher. Doit
     *            définir la propriété ALL_FIELD_VALUES et FieldValues
     *            dans cette propriété doit définir les méta-propriétés répertoriées
     *            dans le tableau {@link #fieldMetaProperties}.
     * @param future : composants de fenêtre supplémentaires à afficher avec la table
     *            des propriétés. (Utilisés par les extensions dans cet exemple).
     * @return : structure RecordFrame contenant les composants d'interface graphique créés
     *         par cette méthode.
     * @throws WvcmException
     */
    RecordFrame showRecord(String title, 
                           CqRecord record, 
                           JComponent[] future) throws WvcmException 
    {
        final StpProperty.List<CqFieldValue<?>> fields = 
            record.getAllFieldValues();
        
        // Définissez un modèle de table dans lequel chaque ligne est une propriété de la
        // ressource d'enregistrement et chaque colonne est une méta-propriété de la
        // propriété, telle que son nom, son type et sa valeur ;
        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(); }
        };
        
        // Définissez la méthode d'affichage
        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);

        // Ajoutez un bouton pour afficher un enregistrement sélectionné ou une zone pièces jointes
        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));
                        }
                    }
                }
            });
        
        // Ajoutez plus de boutons (utilisés par d'autres exemples)
        if (future != null)
            for(int i = 0; i < future.length; ++i) buttons.add(future[i]);

        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        // Demandez à être notifié des changements de sélection et activez le bouton d'affichage
        // uniquement si une zone contenant des valeurs d'enregistrement ou une zone pièces jointes est sélectionnée
        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;
}

/** 
 * Propriétés requises pour chaque valeur de zone d'enregistrement, y compris
 * les informations supplémentaires spécifiques pour les pièces jointes
 */
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));

/** Les méta-propriétés de zone requises et à afficher */
static final NestedPropertyName[] fieldMetaProperties = 
    new PropertyRequest(CqFieldValue.NAME, 
                        CqFieldValue.REQUIREDNESS, 
                        CqFieldValue.TYPE,
                        CqFieldValue.VALUE.nest(VALUE_PROPERTIES)).toArray();
              
/** 
 * PropertyRequest à utiliser lors de la lecture de données à partir d'un enregistrement à
 * afficher par ce visualiseur. Notez le niveau d'indirection utilisé pour demander
 * les méta-propriétés des zones dans la liste ALL_FIELD_VALUES plutôt
 * que les méta-propriétés de la propriété ALL_FIELD_VALUES elle-même.
 */
final static PropertyRequest RECORD_PROPERTIES =
    new PropertyRequest(CqRecord.USER_FRIENDLY_LOCATION,
                        CqRecord.ALL_FIELD_VALUES
                        .nest(StpProperty.VALUE.nest(fieldMetaProperties)));

/**
 * Examine la valeur de propriété d'une zone et, si elle référence un enregistrement,
 * renvoie un proxy pour l'enregistrement référencé. Sinon, renvoie null.
 * @param fields : Property.List à examiner.
 * @param row : index de l'élément dans la liste à examiner.
 * @return : proxy Record si la zone référence un enregistrement ; null sinon
 */
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;
}

/**
 * Indique si la zone spécifiée est une zone pièces jointes.
 * @param fields : Property.List à examiner.
 * @param row : index de l'élément dans la liste à examiner.
 * @return : true si la zone dans l'index donné est une zone pièces jointes
 */
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;
}

/**
 * Présente à l'utilisateur une liste des pièces jointes associées à une
 * zone spécifiée d'un enregistrement et autorise l'utilisateur à en sélectionner une.
 * 
 * @param frame : cadre parent de la boîte de dialogue générée par cette méthode.
 * @param fields : Property.List à examiner.
 * @param row : index de l'élément dans la liste à examiner.
 * @param op : chaîne identifiant l'opération qui sera exécutée sur la
 *            pièce jointe sélectionnée.
 * @return : proxy Attachment pour la pièce jointe sélectionnée ; null si l'utilisateur
 *         n'effectue aucune sélection.
 */
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;
}

/**
 * Propriétés de la pièce jointe à afficher dans la liste de sélection des pièces jointes
 * générée par {@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));

/**
 * Programme principal de l'exemple ViewRecord. Instancie un Provider et
 * appelle l'exemple ExecuteQuery, en transmettant dans une version de Viewer
 * qui affiche les zones d'un enregistrement ClearQuest.
 * @param args : non utilisé.
 */
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);
    }
}

/**
 * Extension de JFrame pour l'affichage de la zone d'enregistrement,
 * exposant aux clients le composant JTable du cadre et de la liste
 * des zones affiché dans la 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;
}

/** Décalage X pour la prochaine fenêtre à afficher */
private static int g_windowX = 200;

/** Décalage Y pour la prochaine fenêtre à afficher */
private static int g_windowY = 200;

Dans cet exemple, ViewRecord.Viewer n'est pas uniquement utilisé pour afficher un enregistrement renvoyé par une requête, mais également pour afficher un enregistrement référencé par une zone sélectionnée d'un enregistrement et pour afficher un fichier joint à une zone d'un enregistrement. L'utilisateur peut utiliser cette fonction pour parcourir les références d'un enregistrement à un autre.

ViewRecord.view(CqRecord) lit toutes les zones de l'enregistrement dans la base de données ainsi que les méta-propriétés de chaque zone utilisée par l'afficheur et transmet le proxy renseigné à la méthode showRecord. L'afficheur utilise la propriété ALL_FIELD_VALUES d'un enregistrement ClearQuest pour obtenir une liste de toutes les zones de l'enregistrement. Pour chaque zone, les méta-propriétés NAME, REQUIREDNESS, TYPE et VALUE sont demandées. Notez que ces demandes de méta-propriété sont imbriquées sous une autre demande de méta-propriété VALUE de sorte que ces méta-propriétés soient obtenues pour la valeur de la propriété ALL_FIELD_VALUES et non pour la propriété ALL_FIELD_VALUES elle-même. (Voir la déclaration de RECORD_PROPERTIES, VALUE_PROPERTIES et fieldMetaProperties.)

Si la zone est une zone pièces jointes, la propriété ATTACHMENT_LIST de la valeur est également requise. (Si la zone n'est pas une zone pièces jointes, cette demande de propriété échouera, mais étant donné que cette propriété est utilisée uniquement si la zone est une pièce jointe, cet incident ne provoquera pas d'exception.)

ViewRecord.showRecord utilise les mêmes composants de l'interface graphique Swing et la même structure que ExecuteQuery.showResults uniquement si le contenu des lignes et des colonnes de la table diffère. Dans ce cas, chaque ligne est une zone de l'enregistrement, exprimée en tant qu'objet CqFieldValue. Chaque colonne est une méta-propriété de la zone. L'interface StpProperty.getMetaProperty générique est utilisée pour extraire chaque valeur de méta-propriété à partir de la structure CqFieldValue/StpProperty pour chaque zone. Avec deux exceptions, la méthode toString() pour chaque valeur de méta-propriété est utilisée pour la génération d'une image de la méta-propriété dans la vue des enregistrements. Pour les ressources de l'enregistrement, seule la zone de nom de la propriété USER_FRIENDLY_LOCATION s'affiche pour réduire la taille de la sortie. Pour les zones pièces jointes, seul le nombre de pièces jointes s'affiche, les noms de chaque pièce jointe n'étant pas détaillés.

L'affichage des types de zone RESOURCE_LIST, JOURNAL, STRING_LIST et STRING doit également attirer l'attention du lecteur.

Lorsqu'une zone pièces jointes est sélectionnée et que vous cliquez sur le bouton view, selectAttachment est appelé et son résultat est transmis à ViewRecord.view(CqAttachment). La méthode selectAttachment utilise de nouveau JOptionPane.showInputDialog pour présenter à l'utilisateur une liste des pièces jointes associées à la zone sélectionnée. La valeur d'une zone pièces jointes est une ressource de dossier pièces jointes. Les pièces jointes associées à la zone sont des membres liés de ce dossier pièces jointes.

ViewRecord.view(CqAttachment) utilise CqAttachment.doReadContent pour lire le fichier joint à partir de la base de données dans un fichier temporaire, puis appelle une méthode utilitaire (code Swing) pour afficher le fichier à l'utilisateur.

ViewRecord.Viewer devrait également prendre en charge l'affichage des valeurs RESOURCE_LIST dans une fenêtre distincte. Cette affirmation est également laissée aux bons soins du lecteur.

Vous pouvez utiliser l'API CM Rational pour écrire des applications client ou des utilitaires destinés à l'affichage d'enregistrements et de leurs zones et valeurs de zone.

Résumé de la leçon

L'API CM Rational vous permet d'effectuer plusieurs opérations spécifiques à Rational ClearQuest, telles que l'exécution d'une requête, l'itération d'un ensemble de résultats et l'affichage d'enregistrements et de leurs zones. Maintenant que vous avez appris à utiliser l'API CM Rational pour afficher un enregistrement, la prochaine étape consiste à apprendre comment modifier un enregistrement.
Dans cette leçon, vous avez appris les notions suivantes :
  • Les interfaces d'API CM Rational qui sont spécifiques aux ressources Rational ClearQuest.
  • Comment écrire du code d'application client d'API CM Rational pour extraire des enregistrements, demander des propriétés et afficher des valeurs de zone.
< Précédent | Suivant >

Commentaires