MokaByte 60 - Febbraio 2002 
Java Beans
La programmazione per componenti in Java
IV parte:
di
Andrea Gini
Nei mesi passati abbiamo studiato le regole di programmazione che definiscono la specifica Java Beans. L'adozione di speciali regole di naming permette a tool grafici abilitati ai Bean di riconoscere questi ultimi, di analizzarne proprietà ed eventi e di fornire all'utente gli opportuni strumenti di supporto. E' possibile scavalcare questo processo automatico di analisi delle firme dei metodi, qualora si desideri fornire un elenco esplicito dei servizi disponibili: in questo articolo impareremo come fare

Introspezione: l'interfaccia BeanInfo
Le convenzioni di naming descritte nei paragrafi precedenti permettono ai Tool grafici abilitati ai Beans di scoprire i servizi di un Componente grazie alla reflection. Questo processo automatico è certamente comodo, ma ha il difetto di non offrire nessun tipo di controllo sul numero e sul tipo di servizi da mostrare. In alcune occasioni può essere necessario mascherare un certo numero di servizi, specie quelli ereditati da una superclasse.
I Beans creati a partire dalla classe JComponent, ad esempio, ereditano automaticamente più di dieci attributi (dimensioni, colore, allineamento......) e ben dodici tipi diversi di Evento (ComponentEvent, MouseEvent, HierarcyEvent....). Un simile eccesso provoca di solito disorientamento nell'utente; in questi casi è preferibile fornire un elenco esplicito dei servizi da associare al nostro Bean, in modo da ripulire gli eccessi.
Per raggiungere questo obiettivo, bisogna associare al Bean una classe di supporto, che implementi l'interfaccia BeanInfo. Una classe BeanInfo permette di fare un certo numero di cose: esporre solamente i servizi che si desidera rendere visibili, aggirare le convenzioni di naming imposte dalle specifiche Java Beans, associare al Bean un'icona e attribuire ai servizi nomi più descrittivi di quelli rilevabili con il processo di analisi delle firme dei metodi.

 

Creare una classe BeanInfo
Per creare una classe BeanInfo bisogna anzitutto definire una classe con lo stesso nome del Bean, a cui si deve aggiungere il suffisso BeanInfo. Per semplificare il lavoro si può estendere SimpleBeanInfo, una classe che fornisce un'implementazione nulla di tutti i metodi dell'interfaccia. In questo modo ci limiteremo a sovrascrivere solamente i metodi che ci interessano, lasciando tutti gli altri con l'impostazione di default.
Per ridefinire il numero ed il tipo dei servizi Bean, dobbiamo agire in modo appropriato a restituire le proprietà, i metodi o gli Eventi che si desidera esporre. Opzionalmente, possiamo associare un'icona al Bean, definendo il metodo public java.awt.Image getIcon(int iconKind). Per finire, possiamo specificare la classe del Bean e il suo Customizer, qualora ne esista uno, con il metodo public BeanDescriptor getBeanDescriptor().
La classe BeanInfo così prodotta deve essere messa nello stesso package che contiene il Bean. In assenza di una classe BeanInfo, i servizi di un Bean vengono trovati con la reflection.

Feature Descriptors
Una classe di tipo BeanInfo restituisce, tramite i seguenti metodi, vettori di descriptors che contengono informazioni relative ad ogni proprietà, metodo o Evento che il progettista di un Bean desidera esporre:

PropertyDescriptor[] getPropertyDescriptors();
MethodDescriptor[] getMethodDescriptors();
EventSetDescriptor[] getEventSetDescriptors();

Ogni descriptor fornisce una precisa rappresentanzione di una classe di servizi Bean. Il package java.bean implementa le seguenti classi:

  • FeatureDescriptor: è la classe base per tutte le altre classi descriptor, e definisce gli aspetti comuni a tutta la famiglia.
  • BeanDescriptor: descrive il tipo e il nome della classe Bean associati, oltre a fornire il Custimizer, se ne esiste uno.
  • PropertyDescriptor: descrive le proprietà del Bean.
  • IndexedPropertyDescriptor: è una sottoclasse di PropertyDescriptor, e descrive le proprietà indicizzate.
  • EventSetDescriptor: descrive gli eventi che il Bean è in grado di inviare.
  • MethodDescriptor: descrive i metodi del Bean.
  • ParameterDescriptor: descrive i parametri dei metodi.

 

Esempio
In questo esempio vedremo un BeanInfo per il Bean PhotoAlbum, che permette di nascondere una grossa quantità di servizi Bean che per default vengono eredita dalla superclasse JPanel:

package com.mokabyte.mokabook.javaBeans.photoAlbum;

import java.beans.*;
import com.mokabyte.mokabook.javaBeans.photoAlbum.*;

public class PhotoAlbumBeanInfo extends SimpleBeanInfo {

    private static final Class beanClass = PhotoAlbum.class;

    public PropertyDescriptor[] getPropertyDescriptors() {
        try {
            PropertyDescriptor imageNumber =
                new PropertyDescriptor("imageNumber",
                                        beanClass,
                                        
"getImageNumber",
                                        
null);
            PropertyDescriptor imageIndex =
                new PropertyDescriptor("imageIndex",
                                       beanClass,
                                       "getImageIndex",
                                       
null);
            PropertyDescriptor imageComment =
                new PropertyDescriptor("imageComment",
                                       beanClass,
                                       "getImageComment",
                                       null);

            imageIndex.setBound(true);
            imageComment.setBound(true);

            PropertyDescriptor properties[] = {imageNumber, imageIndex,
                                               imageComment};
            return properties;
        }
        catch (IntrospectionException e) {
            throw new Error(e.toString());
        }
    }

    public EventSetDescriptor[] getEventSetDescriptors() {
        try {
            EventSetDescriptor changed =
                new EventSetDescriptor(beanClass,
                                       "propertyChange",
                                      
PropertyChangeListener.class,
                                       
"propertyChange");

            changed.setDisplayName("Property Change");
            EventSetDescriptor events[] = {changed};
            return events;
        }
        
catch (IntrospectionException e) {
            throw new Error(e.toString());
        }
    }

    public MethodDescriptor[] getMethodDescriptors() {
        try {
            MethodDescriptor showNext = new MethodDescriptor(
                    beanClass.getMethod("showNext",null));
            MethodDescriptor methods[] = {showNext};
            return methods;
        }
        catch (Exception e) {
            throw new Error(e.toString());
        }
    }

    public java.awt.Image getIcon(int iconKind){
        if(iconKind==SimpleBeanInfo.ICON_COLOR_16x16)
            return loadImage("photoAlbumIcon16.gif");
        else
            return loadImage("photoAlbumIcon32.gif");
    }
}

La classe viene definita come sottoclasse di SimpleBeanInfo, in modo da rendere il processo di sviluppo più rapido.
Il primo metodo, getPropertyDescriptors, restituisce un array con un tre PropertyDescriptor, uno per ciascuna delle proprietà che si vuole rendere visibile. Il costruttore di PropertyDescriptor richiede quattro argomenti: il nome della proprietà, la classe del Bean, il nome del metodo getter e quello del metodo setter: quest'ultimo è posto a null, a significare che le proprietà sono di tipo Read Only. Si noti, in questo metodo e nei successivi, che la creazione dei Descriptors deve essere definita all'interno di un blocco try - catch, dal momento che può generare IntrospectionException.
Il secondo metodo, getEventSetDescriptors, restituisce un vettore con un unico EventSetDescriptor. Quest'ultimo viene inizializzato con quattro parametri: la classe del Bean, il nome della proprietà, la classe dell'ascoltatore e la firma del metodo che riceve l'evento. Si noti la chiamata al metodo setDisplayName, che permette di impostare un nome più leggibile di quello che viene normalmente ottenuto dalle firme dei metodi.
Il terzo metodo, getMethodDescriptors, restituisce un vettore contenente un unico MethodDescriptor, che descrive il metodo showNext. Il costruttore di MethodDescriptor richiede come unico parametro un oggetto di classe Method, che in questo esempio viene richiesto alla classe PhotoAlbum ricorrendo alla reflection.
Infine il metodo getIcon restituisce un'icona, che normalmente viene associata al Bean all'interno di strumenti visuali.
Per impacchettare il Bean PhotoAlbum con le icone e il BeanInfo, si può seguire la procedura già descritta, modificando la riga di comando dell'utility jar in modo da includere le icone nell'archivio:

jar cfm photoAlbum.jar photoAlbumManifest.tmp com\mokabyte\mokabook\javaBeans\photoAlbum\*.class com\mokabyte\mokabook\javaBeans\photoAlbum\*.gif com\mokabyte\mokabook\javaBeans\photoAlbum\images\*.*

 

Conclusioni
Questo mese abbiamo studiato come sia possibile ridurre l'interfaccia di programmazione di un Bean, in modo da semplificarne l'uso all'interno di strumenti di sviluppo visuali. Chi dispone di strumenti di sviluppo come JBuilder, un ambiente di sviluppo abilitato, potrà provare ad integrare i bean di esempio (o altri di propria invenzione) nella palette del tool. Il mese prossimo concluderemo il panorama sui Java Beans, attraverso una panoramica sui meccanismi più avanzati per la personalizzazione.

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it